Hello,
I have several use-cases for a grid bound to a large number of rows (10k+) that also needs to keep track of the value history for a couple of fields. This is also a live dataset which receives regular updates.
I am currently using the CellDataPresenter ValueHistory collection to keep track of changes on a column but I am running into several issues with regards to memory use. I need to store the history values of rows that aren't displayed in the scrolling region so I currently have the following settings for the grid:
<igdp:XamDataGrid CellContainerGenerationMode="PreLoad" RecordContainerGenerationMode="PreLoad">
And my field settings are
<igdp:UnboundField Label="Change" Name="Change" BindingMode="OneWay" BindingPath="[PriceMove]" DataType="{x:Type System:Double}" BindingRetentionMode="Retain" > <igdp:UnboundField.Settings> <igdp:FieldSettings DataValueChangedHistoryLimit="2" DataValueChangedScope="AllAllocatedRecords" DataValueChangedNotificationsActive="True" CellValuePresenterStyle="{StaticResource staleCellStyle}" /> </igdp:UnboundField.Settings> </igdp:UnboundField>
The style staleCellStyle is using a converter to access the cell value history and do its magic.
My question is the following:
Can I still use some virtualization features of the grid if I want to use full-grid history support? Can I virtualize the cells but not the records? I am slightly confused by the record / cell virtualization strategies here.
Also, I noticed that without binding retention set to Retain, the grid would sometimes not process the PropertyChanged events while scrolling, and the bindings would just get recreated after the event had fired, thus missing any updates that occured during scrolling - although the updates to the dataset are done on the UI thread.
Thanks for your help,
Florian
Florian Doyon said: <igdp:XamDataGrid CellContainerGenerationMode="PreLoad" RecordContainerGenerationMode="PreLoad">
I would not use these settings with any except a grid that will only have a small number of records. These settings relate to what elements (containers) are created and how long they are retained. RecordContainerGenerationMode is used by the GridViewPanel to determine which recordpresenters should be allocated. The CellContainerGenerationMode is used to determine which CellValuePresenter, etc. elements should be allocated. For both of these it is recommended that you continue to use the recycling/virtualize options so that only the elements that are needed for what is in view are created/maintained.
Florian Doyon said:Can I still use some virtualization features of the grid if I want to use full-grid history support?
Yes you can use the virtualization features. I'm guessing the issue you are facing relates to the fact that the grid does not pre-allocate records by default - it lazily allocates them as they are needed. So if you are expecting the history to be maintained for every record - even those that you or the grid have never accessed - then you would probably want to set the RecordLoadMode to PreloadRecords. Since the cells are also lazily allocated, you would want to handle the InitializeRecord event and simply index into the Cells collection of the e.Record for the Field on which you have enabled the history.
Florian Doyon said:Can I virtualize the cells but not the records?
In theory one can set those properties such that the recordpresenters are pre/lazy loaded but that the cellvaluepresenters are still recycled but I would still recommend against preloading the record presenters if you have more than a handful of records since WPF does have limits on the number of elements used.
Florian Doyon said: Also, I noticed that without binding retention set to Retain, the grid would sometimes not process the PropertyChanged events while scrolling, and the bindings would just get recreated after the event had fired, thus missing any updates that occured during scrolling - although the updates to the dataset are done on the UI thread.
This is correct. As documented for that property, the bindings for unbound cells are only kept around while they are needed and then discarded. Since they are not needed in this case for the records out of view (e.g. because there are no cellvaluepresenters accessing them, no summaries on them, etc), the bindings will be released. Since the bindings are released the grid won't get the change notifications and the history won't be updated.
Hey Guys,
I apologize for bringing up an old issue but I'm experiencing this as well. I'm using the cell value history to indicate edited cells. When I bulk edit 19 + records, only the records in the scroll view log the value change in the history.
As per your above suggestion, I expect my grid to have 100s of records so I tried setting RecordLoadMode="PreloadRecords" and running the following in my InitializeRecord handler to try and initialize each cell:
if (e.Record == null || !e.Record.IsDataRecord || e.Record as DataRecord == null) return; (e.Record as DataRecord).Cells.ForEach(cell => {});
I'm not sure if this is what you meant by "Since the cells are also lazily allocated, you would want to handle the InitializeRecord event and simply index into the Cells collection of the e.Record for the Field on which you have enabled the history" but this is not working for me.
Using CellContainerGenerationMode="PreLoad" & RecordContainerGenerationMode="PreLoad" but affects my screen's performance (as expected). Do you have any other suggestions?
Hello Andrew, and thank you very much for your very detailed and helpful response.
I am still running into what I think is a bug in the grid when handling history when not pre-alocating records and cells. I think that virtualization process doesn't reassign the proper cell history to the presenter when scrolling to a virtualized record.
I attached a sample app which is as barebones as possible.
The goal : define styles that react to up/down signals from the dataset. I cannot modify the dataset in the real world, but in this test, I did add a 'PrevMarketPrice' to make the bug observable by the user.
When the app launches, press Toggle Update so the viewmodel spawns a thread that simulates market conditions. We're tracking the history for the MarketPrice field here.
The price going up triggers a green background, going down is red. This works fine for the initial set of records.
To observe the bug, while the update is running, please scroll down to the bottom of the grid.
You will observe that the cells have a color assigned to them when scrolling into view, but this color never changes. Setting a breakpoint in the history converter, you will see that the CellValuePresenter.ValueHistory collection is not accurate as it only contains the value contained in the cell when it first scrolled into view and is never updated. The converter then subsequently runs against this value and the current cell value, but the output isn't accurate as it then represents the tick variation since the record scrolled into view.
This is extremely strange, as the log output and breakpoints set in the grid's DataValueChanged event will show that the Cell's history (and not the CellValuePresenter's) contains the proper values.
I tested and reproduced this behaviour both under XP and Win7 64, and by using Fields and UnboundFields with any BindingRetention possible.
The bug dissappears when setting RecordContainerGenerationMode and CellContainerGenerationMode to "PreLoad"
Regards,