Description:
The DXGrid utilizes the virtualization mechanism to improve its performance. Virtualization reuses row, cell, and column visual elements, and changes their properties and data context. Virtualization occurs when a visual element is moved outside screen bounds (e.g., when the grid is scrolled or a TreeList node is expanded). Since visual objects are not recreated completely, there may be certain issues; for example, when trying to change a particular cell appearance. How to solve these issues?
Answer:
Though all issues with virtualization are caused by the fact that grid objects are reused, these issues can occur in different use cases, so the best way to describe the virtualization concept is to list the most common use cases (incorrect ones) and demonstrate a proper way of dealing with them.
1. Incorrect bindings.
The data context of cell and row elements (RowData, EditGridCellData, etc.) is not completely recreated, but is only modified according to a row and cell that these elements currently represent. Therefore, bindings to this exact context will not be updated properly.
Let's take a look at an example. Assume you wish to color blank cells in the grid so that it becomes easier for the user to find them. To implement this, you create a custom CellStyle as follows:
XAMLINCORRECT BINDING EXAMPLE
<Style BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=CellStyle}}" TargetType="dxg:CellContentPresenter">
<Setter Property="Background" Value="{Binding Converter={StaticResource Converter}}"/>
</Style>
Here, a converter is used to check a cell value and return a certain color depending on the check result. However, converter methods will be called neither on the cell value changing nor when the scrolling is performed. The problem here is that the EditGridCellData object itself is not changed in these situations. So, the binding expression for the Background property will not be re-evaluated. To solve this issue, set the binding property path directly to the cell value:
XAMLCORRECT BINDING EXAMPLE
<Style BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=CellStyle}}" TargetType="dxg:CellContentPresenter">
<Setter Property="Background" Value="{Binding Value, Converter={StaticResource Converter}}"/>
</Style>
The above example is simple, as it only uses a cell value to calculate cell color. Sometimes it is necessary to use the whole EditGridCellData object to check a cell for multiple conditions (e.g., to return a different color for a focused or selected cell). In this case, you can use MultiBinding and even pass the EditGridCellData object to the converter, but it is also necessary to add bindings to all properties whose changing should trigger the converter call:
XAML<Style BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=CellStyle}}" TargetType="dxg:CellContentPresenter">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding />
<Binding Path="Value" />
<Binding Path="IsSelected" />
<Binding Path="RowData.IsFocused" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
The same is true for other bindings that use the row and cell data context (bindings inside the CellTemplate, DisplayTemplate, EditTemplate, RowStyle, etc.).
2. Manual modifications of grid visual elements.
In our GridControl, you can retrieve grid visual elements that are used to represent a cell via the GetRowElement… and GetCellElement… methods. However, it is not recommended to use these elements to change cell appearance since modifications made for an element that represent a certain cell will remain even after this element is reused. The code operation below will cause virtualization issues and should be done in an appropriate style or template:
C#GridCellContentPresenter element = tableView.GetCellElementByRowHandleAndColumn(rowHandle, gridControl.Columns["FieldName"]) as GridCellContentPresenter;
element.Background = new SolidColorBrush(Colors.Red);
3. The use of custom attached properties.
Sometimes, if data placed in the EditGridCellData object is not enough to calculate the value of the property that should be changed in style (e.g., the cell background), it makes sense to attach a custom property to the cell and change this property when necessary (e.g., in the CellValueChanged method).
However, the value of the property attached to a row or cell element will be used for multiple rows and cells during the grid virtualization.
In this case, there are two approaches to overcome the situation: attach your custom property to the RowState object (that can be obtained from the RowData field) or use Unbound Columns instead of attached properties. In the second approach, you can get an unbound column value in xaml using RowTypeDescriptor that is the data context for the RowData object and is stored in the Data field of the EditGridCellData object. For example:
XAML<Style BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=CellStyle}}" TargetType="dxg:CellContentPresenter">
<Setter Property="Background" Value="{Binding Data.UnboundField, Converter={StaticResource Converter}}"/>
</Style>
...
<Style BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=RowStyle}}" TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Background" Value="{Binding DataContext.UnboundField, Converter={StaticResource Converter}}" />
</Style>
4. Applying animation to rows and cells.
Due to virtualization specifics, attaching animation to row and cell elements will also produce issues on scrolling. The only way to avoid these issues is to create an unbound column to store custom objects with animation attached and bind element properties to these objects in style. This approach is demonstrated in the following example: E841: How to highlight modified rows.
5. Handling value changed events for cell editors.
Cell editors are not recreated during the virtualization process as well, so their values are changed not only when the user interacts with them or the data source is changed, but also when the grid is being scrolled. Therefore, instead of handling an editor's value changed events, it is recommended to use CellValueChanging/CellValueChanged.
Appendix.
Since the grid virtualization occurs mostly when the grid is being scrolled, there can be two types of its virtualization: horizontal and vertical. The difference between them is that during the horizontal virtualization, column objects are also recreated.
There can be certain issues specific to the horizontal virtualization. For example, when you scroll through columns with different types and type-dependent editors, and a cell value is bound to an editor manually, a new value may be passed before the editor is changed, which will result in a format exception. To solve such issues, disable the horizontal virtualization by setting the AllowHorizontalScrollingVirtualization property to false.
As for the vertical scrolling virtualization, it cannot be disabled either directly or by using ScrollViewer properties. However, understanding the specifics of the DXGrid virtualization concept, you will unlikely face any problems with it and will benefit from the performance increase provided.
Hi Support,
Currently We are using 11.2.6, and we plan to upgrade to 12.2 soon.
We find serveral issues may caused by virtualization mechanism in 12.2 while testing.
I will try to resolve the issues by setting the binding property instead of using the datacontext and row elements as you advised.
By the way, can you please let me konw which version the DXGrid virtualization mechanism got invloed?
Regars,
King
Hello King,
In version 11.2 our GridControl already uses the virtualization mechanism, so I don't see any problem for you here to upgrade. By the way, from my experience, almost any issue caused by the grid virtualization can be properly solved. If you have any problems with it, please create a support ticket and we will help you.
Thanks.
Interesting! I'd like to know more about RowData.RowState property, though. What is it exactly, some kind of Tag property on steroids? What kind of properties can I attach to it? How does it plays along with virtualization? Do you have a sample scenario that shows how it's used?
Hi David,
Yes, you are correct, this property is something like Tag and may contain information related to a row. This information is unique for each data row and not mixed during scrolling (unlike visual elements which are reused by different data rows). You can use an arbitrary attached property in RowState and then use in RowStyle or in other places.
I have created a small sample to illustrate how RowState can be used. Please take a moment to review it and let me know if you need further clarification.
Thanks,
Alexander
Hi Support,
Thank you for your sample. Is there a way to change background color of only one cell (not entire row), using this approach (attached properties)? Like in this question https://www.devexpress.com/Support/Center/Question/Details/Q451047, but without virtualization problems.
Hi,
I've created a separate ticket on your behalf: T218201 - How to implement cell animation in the grid and make sure that there are no virtualization issues. Let's discuss your requirements there.
Thanks,
Alex
Hi,
We have a weird issue with the Virtualization.
We have several columns, half with CellTemplate = cellTemplate1 and the rest with cellTemplate2. Only columns with cellTemplate2 has the converter.
When we move horizentally to the end (to columns with cellTemplate2) and go back the columns that are using the cellTemplate1 we get to the converter of cellTemplate2 with column that is using cellTemplate1 (which doesn't have a converter at all).
If we set the virtualization to false everything works. We use the virtualization as true since it works very good with performance.
What is going on here, could be that when you change the columns data, you forget to set the cellTemplate?
Best regards,
Chen
Hi Chen,
To process your recent post more efficiently, I created a separate ticket on your behalf: T348355: GridControl doesn't correctly apply cell templates when scrolling horizontally. This ticket is currently in our processing queue. Our team will address it as soon as we have any updates.