Delphi TDBGrid edit inplace with drop down list - delphi

I have to do maintenance to an old Delphi v7 application which uses a TDBGrid allowing inplace editting with drop down lists.
The issue is: the grid will show only the values present at data source. Without editting, this would be no problem at all. But if I change some value, the grid will only show the newly set value after updating the underlying register (until that the "old" value is displayed).
This only applies to drop down lists. Other input types (checkboxes, text edits) work without problems.
The edit itself also works fine: the changed values are actually reflected in the datasource when the register is UPDATE'd. My only problem is displaying this not-yet-UPDATE'd value.
I have no idea how to even debug this, or further inspect this behavior. So even if you don't have a complete answer, please indicate what else can I do to find a solution.
UPDATE
The project originally used TDB3DGrid (whose source I'm not aware of), but to simplify things I've changed it to TDBGrid. To no avail. The only change was that true/false columns are now displayed as text, and edited as text boxes (displaying and accepting "True" and "False" strings); with TDB3DGrid it showed checkboxes.
Other columns (not edited trough drop down list) are fine: after editing the new value is displayed correctly even before updating the underlying register.
I'm still tracking down where does the list of alternatives that fill the drop down list comes from.
Also still trying to come with an MCVE.
UPDATE 2
Apparently the (now) TDBGrid's affected TDBGridColumn has an empty PickList property. Sure for designtime. But I think this is not changed in runtime.
TDBGrid's source is a child of a TQuery, whose affected field has FieldKind equal to fkLookup. Other properties set for the field are FieldName, KeyFields, LookupDataSet, LookupKeyFields and LookupResultField.

Related

How do I change TColumn properties programmatically prior to showing a DBGrid at runtime from a Delphi TDBGrid descendant?

I have descended a class from TDBGrid, and I want it to be able to remember any modifications that a particular user makes to the column order and column width. And, I want to be able to do this entirely within the grid itself. I do not want to have to attach code to the TDataSet, since this grid is used extensively in my application and I want to implement this feature in all of my grids without duplicating code.
I’ve got the persistence part down. I am able to detect that a user has made changes to the grid columns during their session, and persist that information when either the associated TDataSet is closing, or the grid itself is being destroyed. If you are interested, capture the column parameters from the overwritten ColumnMoved method, as well as from the overwritten ColsWidthChanged method. (Note that this must only be done from the ColsWidthChanged when the FGridState field is equal to gsColSizing.)
I can also retrieve the persisted information when the grid is being initialized but before it is displayed. My problem is that while I am successful at restoring the persisted column order and their widths, those setting are lost before the grid is displayed. Specifically, immediately after applying the persisted settings I can confirm that I have restored the saved column order and widths. But by the time the grid is displayed, it shows the default column order and widths. This suggests that I am applying the persisted settings too early in the loading process.
(By the way, you change the order of the TColumns in a TDBGrid by changing the individual TColumn.Index property. There is also a MoveColumn method. These techniques work once the default TColumns have already been created and configured.)
I am trying to restore previously persisted column settings from within the overwritten LinkActive method, when the value of the parameter to this method is equal to True.
I have inspected the various grid classes in the DBGrid hierarchy, and I simple don’t see a method to override that is later in the load cycle. Does anyone out there have a suggestion for an approach that will succeed in restoring previously save column order definitions immediately prior to the grid displaying the contents of the underlying TDataSet. Again, I want to do this entirely from the grid without having to assign an event handler to the TDataSet. On the other hand, if I need to override a method in something internal to the TDBGrid, such as its TGridDataLink, that would be fine.
Edit - - - - - - - - - - - - - - - - -
It appears that overriding LinkActive and testing for the parameter True (and with a little more finesse, such as making sure that you haven't already loaded the persisted data), is the correct way to do this. My problem was that my loading of the persisted data had some issues. Specifically, as I changed the positions of the default fields, other fields also changed positions, and these changes needed to be accounted for.
Edit 2 - - - - - - - - - - - - - - - - - -
Ok, this one is solved. While I had the persistence part working, I made an error in the restoration. So, here what was needed. In order to rearrange the columns, based on the new order that was persisted, you also have to know the default, or current, order of those fields. The trick is that as you start to change the order of columns in the Columns property, you also have to change your list of the current positions of the fields. I had failed to do this.
My code now not only changes the order of the fields in the TColumns property of the grid, but it also updates the list that I created to keep track of the current order.
It turns out that overriding LinkActive in the TDBBGrid class is a fine place to restore previously persisted column position data (see the edits to my question for more info). So, the problem was not in the choice of methods to override.
You may recall from my question that you change the order of the TColumns in a TDBGrid by changing the TColumn.Index property of the individual columns. There is also a MoveColumn method. However, in order for these to work, you have to know the current position of the fields in the Columns property. While I captured this information before restoring the saved positions, what I overlooked was that the current position of many of the fields change each time I change the order of a given field. Once I realized that, I not only changed the order of the fields in Columns, but I also updated my list containing the current positions of the fields.

How to add a non-bound column to a DevExpress DB QuantumGrid

I am using these components:
UniDac for connection to mysql database
DevExpress for QuantumGrid
IDE:
Embarcadero Rad Studio XE2
I have a cxGrid component with one level and a cxGrid1DBTableView specified as the level's View. I can get data from my database and edit it in the grid. I want to add a column that is not in the bound DataSet. When I specify the Column properties value as CheckBox I can see the column but I can't change the value from unchecked to checked by clicking it. The field doesn't have a DataBinding assigned to it. I tried other types of Properties but all are the same I cant change the row value in the grid.
I've been searching for a way to fix this for couple of days, so im hoping you guys can help me.
Are you trying to add a checkbox item that does not have a database field behind it? I have this on one of my forms.
In addition to setting the Properties to "Checkbox" you need to set the DataBinding -> ValueType to "Boolean". The DataBinding->FieldName can be left blank.
To access the values or change their defaults you can use the DataController like this:
View.DataController.Values[i, CheckBoxFieldIndex] := true;
In addition you need to set
DataController.DataModeController.SmartRefresh := true;
To set that option you will also need a KeyField defined for the Controller (DataController.KeyFieldNames)
Setting the SmartRefresh to true will prevent the grid from trying to get an updated value from the underlying dataset. You need to prevent the refresh or the value for the non-bound column will be set back to Null. This comes with some restriction on how you update your dataset. Any changes made to the data in code will not be reflected by the grid unless you explicitly refresh the grid.
You also have to fill the field View.DataController.KeyFieldNames with one of the datasets fields. At least I need it in Delphi 7.

TClientDataSet.Cancel Loses TDBMemo Values

I have a form with the following controls: TDBEdit, TDBMemo, TDataSource, TClientDataSet
If the user edits the fields and then clicks a button on the form that simply calls the MyCDS.Cancel method (to cancel the edits), the TDBEdit fields revert to their original values, but the TDBMemo fields are not reverted (they are set to blank values).
The TClientDataSet is populated from a MSSQLServer 2008 database. The TDBEdit fields are nvarchar(255) in the database, and the TDBMemo are nvarchar(max) or xml fields.
Looking at the values in the Debug Inspector (ctrl+F7, MyCDS.FieldByName('afield'), Inspect) shows the following for one of the nvarchar(max) fields:
DataSize = 0
DataType = ftWideMemo
Size = 1
This is the same for all of the nvarchar(max) and xml fields whether or not the underlying field has data or not.
It appears that there is an incompatibility between the nvarchar(max) (which is treated as a ftWideMemo) and the TDBMemo control.
Has anyone seen issues like this before? Do you have any suggestions how to resolve it?
In case anyone in interested, the problem is a bug in the TClientDataSet component. The problem only occurs when you clone a dataset, don't have a Provider and turn LogChanges off. If you edit a record on the cloned dataset and then cancel the edit, then any memo fields lose their values.
Since you don't necessarily use a Provider in file-based applications, it doesn't always make sense to enable LogChanges. However, to work around the limitation with memo fields, you can leave LogChanges on, and then call MergeChangeLog after operations that change the cloned dataset.
I created a program that demonstrates the issue. See the Embarcadero incident QC#110511.

How do you pass all the contents of a ListBoxFor?

I am currently binding an IEnumerable collection to a ListBoxFor, which works as expected, sending the currently selected values on POST. However, I need to send all the values instead (essentially any value in a given ListBoxFor I consider to be required, whether selected or not). How would I go about doing this?
(I can probably rig something up in jQuery where, on-submit, it manually selects all the elements in a box, but was wondering if there was a better way.)
If you want to continue using normal browser form serialization on submit, write a javascript function to fire right before the submission (hook into an onclick event or something) which iterates through the list box control and concatenates the desired values (perhaps comma-delimited) and places it in a hidden field. The value of that hidden field will be submitted normally and you can parse the individual values from it on the server side. It's still some manual work, but you avoid messing with GUI state (i.e. selecting all desired list box items) which I agree is something you don't want to do.

Delphi: TAdoTable.Insert not really an Insert?

I have two ADO Tables linked as master/details tables, tblCategory (master) and tblItems (details). Both tables have its own grid, and displayed in the same form. I also have data aware controls (dbedits).
Say, currently I'm at: Category=Books, No of Items=10 records, and pointing at record number 5 in the grid. I want to add a new record to the item, so I use:
tblItems.Insert;
The problem is, instead of adding a new row, the grid and the db aware controls are showing the current record (rec No 5). Not inly that, it seems the record is in edit mode too. After I cancel it and repeat the Insert command, only then the new row appeared.
How to fix this, so each time I use tblItems.Insert it always add a new and empty row :)
Nevermind, I think I know what caused it. It's the db aware controls. After the insert command, user will input data. This makes the db aware control receives focus and it automatically sets its position to the current record and displays it.
The solution is to use non-db-aware controls instead, and set the behavior programatically

Resources