OK, call me really thick, but I need help on this issue… yet again. I think this is at least my third question on the topic…
RecordIndexes!
How many could DevExpress create, and how confusing could this be?
let's see here, there is:
Controller.FocusedRowIndex
Controller.FocusedRecordIndex
DataController.FocusedRowIndex
DataController.FocusedRecordIndex
and finally DataController.FilteredRecordIndex[]
did I miss any?
did I miss any?
Then there is the magic bullet:
TcxCustomDataController.GetRowInfo(ARowIndex: Integer)
So far, this is what I have learned from trial/error in terms of getting the recordIndex to apply to the Values[][] array:
If you call DataController.FilteredRecordIndex[] you get back a RecordIndex - lovely, life is as it should be
If you refer to the DataController.FocusedRecordIndex you get back a RecordIndex - life is still good!
If you refer to the Controller.FocusedRecordIndex you must run this through the magic bullet (GetRowInfo)
I am to scared and nervous to sort out what I need to do if I have a Controller.FocusedRowIndex :)
The bottom line is, when things don't seem to work, and I suspect that I have the wrong index, I throw my RecordIndex into the DataController.GetRowInfo and magically it pops back to me the *real* record Index I probably want so I can manipulate the DataController.Values[][] correctly.
To Make matters worse, event handlers like TcxGridGetDisplayTextEvent (OnGetdisplayText) are defined as:
TcxGridGetDisplayTextEvent = procedure(Sender: TcxCustomGridTableItem; ARecord: TcxCustomGridRecord; var AText: string) of object;
You would think from this, that if you wanted the RecordIndex from within this even handler, you would just refer to ARecord.Index. Bzzz. Try again. In fact you must use the magic bullet (GetRowInfo method) to get the real RecordIndex. And if you dig into the source you will see that the ARecord param is a misnomer. It should be ARow since GetRowInfo takes in a RowIndex and not a RecordIndex.
Yes, I have read the documentation… over and over. Clearly it is not sinking in. What I want to know is when exactly do I need to call GetRowInfo. When will the FocusedRowIndex differ form the FocusedRecordIndex for both the Controller and DataController. and most importantly - why?
cheers
-randall
Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.
Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.
Hi,
After spending a number of hours trying to battle through code that is not working, I need to ask a few questions to sort out the behaviour I am seeing.
Usually when I am poking around at the DataController.Values[][] level, I am working against a non data aware column. But in this instance I am trying to set values of columns that are data aware. I am using a non data aware checkbox column. when the user checks that column, I need to set values in a number of other fields that are data aware. If I go the direct TDataset path, the changes made to the dataset are not reflected in the grid. Which is what I would expect. So again, I am back to pushing values in via the DataController.Values[][] array.
But if I push values in, once I move off the record, the changes are lost. Doing a DataController.Edit prior to adjusting the Values array does not help. that just puts the underlying dataset into edit mode. But if I use the DataController.SetEditValue method, the data will stick. But that seems to work directly against the DataController.EditingRecordIndex. That is fine for when the user clicks the checkbox since the EditingRecordIndex is the record I want to edit. But we also have options like "Select al in group" where we iterate over a number of grid rows/records in which case EditingRecordIndex is still pointing to the same record. Do I need to call the protected TcxCustomGridTableViewData.AssignEditingRecord as I move through the records?
So that leaves me stuck. I cannot find a way to manipulate the data via the GridView and force it to be applied to the table.
In addition, I want to know how permanent a recordIndex is. I was tracking some code, following the value of a TcxCustomGridRecord.recordIndex. It was the number I expected until (I think) I assigned a value via the Datacontroller.Values property. That same TcxCustomGridRecord instane is then passed to another method. But when I inspect the recordIndex in that method, it seems to have a completely arbitrary value. Sometimes -1, sometimes very large numbers. So my question is, does a RecordIndex change when the dataController.values change? In addition, I notice that when I manipulate this data in the grid, when there is a field grouping, those groups seems to collapse all by themselves. Is this "normal" grid behaviour? Or a side effect of assigning values to the Datacontroller.values property or whatever is causing the recordIndexes to change?
cheers
-randall
Hi Randall,
Let me first explain how the ExpressQuantumGrid (more precisely the ExpressDataController) manages its data. When the DataController loads data from a dataset, it creates a rectangular matrix (N rows x M columns) where records are ordered in the same manner as they were fetched from the dataset. We name these data rows "records", and you're referring to them when using the Values property of the DataController. When the data layout is changed (records are filtered, sorted or grouped), the DataController does not actually affect the original data matrix (the order of records remains the same). Instead, it uses logical "rows" which represent data in the current layout (taking sorting and grouping into account). "Rows" don't store any data, they just refer to corresponding "records".
We discussed this topic in report Q32378 ("Row/record index confusion"), and I recommend that you try the demo project, attached to that report. It illustrates how logical and physical indexes correspond to each other.
When you're working with the Table Controller (<GridView>.Controller), you should remember that it works only with logical data. The same is true for the ViewData object. So, their Records[]. Rows[], FocusedRow. FocusedRecord. FocusedRowIndex and FocusedRecordIndex properties actually provide the same values. You can easily see this in the source code … for example:
cxGridCustomTableView.pas function TcxCustomGridTableController.GetFocusedRecord: TcxCustomGridRecord; begin if (0 <= FocusedRecordIndex) and (FocusedRecordIndex < ViewData.RecordCount) then Result := ViewData.Records[FocusedRecordIndex] else Result := nil; end; function TcxGridTableController.GetFocusedRow: TcxCustomGridRow; begin Result := TcxCustomGridRow(FocusedRecord); end;
I agree that this looks confusing, but this is actually the result of a rather complex inner structure of our Grid. When designing it, we thought that it may be necessary to deal with both logical rows and physical records at the GridView's level. We've finally decided to work only with logical data. However, it's necessary to keep both the Row~ and Record~ properties for backward compatibility.
Does this make sense?
Thanks,
Serge
OK, I think I have found why my code is not working. If you can confirm that the behaviour I am seeing is what you would expect:
We do a lot of work with unbound checkbox columns. Usually in these instances we maintain something like a TList of RecordIndex values so we know what is selected and what is not. But in this particular instance it is an underlying date field in the dataset bound to the grid. When the user clicks ont eh checkbox, we run this code:
procedure TfmDesignDB.cxEditRepositoryDataDefSelectCheckBoxPropertiesEditValueChanged(Sender: TObject);
var bChecked : boolean;
iRecordIndex : integer;
focusedRecord : TcxCustomGridRecord;
begin
inherited;
bChecked := TcxCheckBox(sender).checked;
iRecordIndex := viewDataDef.DataController.FocusedRecordIndex;
focusedRecord := viewDataDef.ViewData.GetRecordByRecordIndex(iRecordIndex);
SelectedRowChange(focusedRecord, viewDataSelectCheckBox.Index, bChecked);
end;
And I can double check that when SelectedRowchange is called, focusedRecord.recordIndex has the value I expect…
SelectedRowchange looks like this:
procedure TfmDesignBase.SelectedRowChange(aGridRecord : TcxCustomGridRecord; iCheckBoxColIndex : integer; bIsSelected : boolean);
begin
viewDataDef.DataController.Values[aGridRecord.RecordIndex, iCheckBoxColIndex] := bIsSelected;
SelectedRowChanged(aGridRecord, bIsSelected);
end;
If I put a watch on aGridReocrd.recordIndex, the value changes when the Values array is assigned?!?! This may be behaviour you are expecting, but IMHO that is nuts. So the code is SelectedRowChanged, which then looks a aGridRecord.RecordIndex is now completely invalid.
I can address this, by passing around a aRecordIndex rather then aGridRecord and then using GetRecordByRecordIndex if I need the CustomgridRecord object again. But I need to know - why in the world, would the record index change just because I assigned a value to another column within the datacontroller.values array? I could understand that, if the column that I am changing happens to be the column we are currently sorted on. but it isn't.
I have to agree with what Toby Groves in Q32378 had to say… that I spend a LOT of time doing trial/error programming when it comes to the grid, and I've been working with it for about 2 years. The DevExpress components are supposed to simplify my life and speed my development. DevExpress should serious look at how to simplify things. For example, to create a "calcualted field" such as this checkbox column, should not be so difficult! It is frustrating to program and not be able to predict the behaviour of your code. And I pitty the poor soles that didn't purchase the source. They would surely be pulling their hair out.
my 2 cents
-rs
Hi,
Instead of using a data controller's Values property, use the SetEditValue method as shown in KB article: A343, How to set a value of another Grid column during editing of a cell. Please let us know your results.
Thanks,
Alex
OK, I will try that method. I had actually attempted to use SetEditValue previosuly, but I found that it caused an AV. This is most likely because I had set a value directly via the datacontroller.Values array previously. which seems to reinforce my theory that it is trashing the cache (the datacontroller.values array items) and recreating them, making my passed TcxGridReocrd object invalid.
Can you please confirm if my theory is correct. And if so, when exactly is the cache refreshed? Is it refreshed after every call to dataController.values[][] := xxxx ? Can it be postposed if, for example, I used dataController.begin/endUpdate?
cheers
randall
Hello Randall,
Please accept our apologies for the delay in answering. We need some additional time to research the problem. We'll contact you ASAP.
Thank you for your patience.
Thanks,
Stan.
Hi Randall,
You're correct in your assumption. Modifying values via a data controller's Values property sends a notification about a data change. Then, the ViewData and records are recreated. So, the record object is invalid as it actually references another record. You can pass a record object to the SelectedRowChange method only if this method doesn't change the grid's layout or data. To suspend these changes, enclose your code within the View's BeginUpdate/EndUpdate blocks or call the SetEditValue method mentioned above.
Thanks,
Alex
See my latest comments in http://www.devexpress.com/Support/Center/p/Q34130.aspx - it all amounts to the same problem. The EndUpdate will still attempt to refresh the grid. and SetEditValue only works against the current record.
-rs
Yes, that's how the EndUpdate and SetEditValue methods work. Based on your code sample, you're working with a focused record. So, the SetEditValue method should do the trick.
Thanks,
Alex