I have a love/hate relationship with the Developer Express Quantum Grid: I love the features and our users think they’re great. I’m even willing to spend the time required to make them work right — I understand that with such a huge number of features they’re going to be complicated (and boy are they ever!). But every once in a while I come across something that is just hair-pullingly more difficult than it needs to be. The upgrade from v3 to v4 was bound to be difficult, due to the vastly different architecture, but some parts were senselessly so — properties which did the same things had new, but synonymous, names, for example.
Last week we ran into another one.
We have a field which is sometimes ReadOnly and sometimes not ReadOnly. When it is ReadOnly it is set in code based on the value of other fields. Because TCustomClientDataset does not allow code to change a ReadOnly column without temporarily setting ReadOnly FALSE, we have code like this, which is called several times as the user changes the values the ReadOnly column depends upon during editing:
MyField.ReadOnly := FALSE; try MyField.AsString := GetMyFieldValue; finally MyField.ReadOnly := TRUE; end;
The VCL’s TDataSet allows this.
However, the code fails if we do nothing more than connect a Developer Express TcxGrid with a TcxGridDBTableView to the same dataset. The reason is that changing the ReadOnly property of the TField causes the datasource to send a deLayoutChanged data event. That shouldn’t be a problem, but when it trickles through the cxGrid it eventually hits TcxDBDataProvider.Freeze, which calls TDataSet.Cancel, tossing out the rest of the user’s changes.
In fairness to Developer Express, the VCL’s TDataSet architecture
doesn’t make this easy makes this very painful to implement right. It could be that you got deLayoutChanged because a field became ReadOnly, it could be an entirely different dataset was connected. They only way you, as a component author, have to find out is to exhaustively run through the possibilities and see if it affects you. I know that this is laborious and error-prone as I do it in some of my own controls. The TcxGrid’s data-aware provider doesn’t do this, however. It just reloads unconditionally, whether or not there is any difference in the data.
I’ve emailed Developer Express support about this. They replied promptly, as always. But they don’t have a way to stop this misbehavior. So the only way I have to implement the feature is to either toss the grid — which I don’t want to do — or set the ReadOnly property of the editor differently than the TField, which is non-trivial in our case since we’re creating all of the screens dynamically at runtime. Yuck!
So here’s a plea to component authors: Please, please avoid calling TDataSet.Cancel at all costs. Even the (Borland-provided) TDBGrid’s dgCancelOnExit option (true by default!) seems very wrong to me. The user’s data is the lifeblood of our applications; we take great pains to protect it. And if you absolutely must call Cancel in some situation, then you must also provide a way to prevent that from happening when your users know more about what is going on in their applications than you do.