@
- preamble
-
I. VirtualizingStackPanel
-
1.1 Introduction to Virtualization Features
- 1, add a ListBox control in Window.
- 2, in the design view with the mouse to select the ListBox control and right-click "Edit other templates" - "Edit the layout of the item template" - "Edit Copy".
- 3. View the generated template code.
- 1.2 Introduction to virtualization parameters
-
1.1 Introduction to Virtualization Features
-
II. CustomVirtualizingPanel
-
2.1 Basics
- 2.1.1 VirtualizingPanel
- 2.1.2 IScrollInfo
-
2.2 Practical cases
- 2.2.1 Demand analysis
- 2.2.2 Code Implementation
- 2.2.3 Operational effects
-
2.1 Basics
preamble
I believe that many WPF developers have encountered this situation, when an ItemsControl (or inherited from the ItemsControl) control bound to a collection of time, if the collection of too many entries, then the interface will become lagging or even stop responding, especially in the container or the window size changes, the interface rendering will give a sense of half a beat slower, the experience is very poor, then we can use virtualization technology to solve the problem. experience is very poor, then we can use virtualization technology to solve this problem.
The core idea of UI virtualization is to render only the controls that are within the visual range, so it is usually used with the ScrollViewer control, which calculates the controls that should be displayed within the visual range by using the VerticalOffset, HorizontalOffset, ViewportWidth, ViewportHeight, etc. parameters. parameters of the ScrollViewer control, you can calculate the controls that should be displayed in the visual range, and when the control is not displayed, it will be removed from the panel, which ensures that only a limited number of controls are rendered at the same time, instead of rendering all of them, and thus achieves the purpose of performance improvement.
I. VirtualizingStackPanel
1.1 Introduction to Virtualization Features
VirtualizingStackPanel is a built-in control in WPF that provides UI virtualization. It is the default layout control in ListBox, ListView, DataGrid, etc. We can see how it is defined by viewing the control template.
1, add a ListBox control in Window.
2, in the design view with the mouse to select the ListBox control and right-click "Edit other templates" - "Edit the layout of the item template" - "Edit Copy".
3. View the generated template code.
As you can see from the above code, ListBox has a property called ItemsPanel, in which a Panel control is specified, which is used by ListBox to lay out sub-items when rendering, and we only need to specify the VirtualizingStackPanel control in ItemsPanel to achieve virtualization. .
1.2 Introduction to virtualization parameters
If you implement a control inherited from ItemsControl and follow the steps in 1.1, you will find that you still can't realize the virtualization function, the reason is that the virtualization function is not turned on (ListBox, ListView, DataGrid, etc. are turned on by default), in order to turn on the virtualization function of the ItemsControl control we also To enable virtualization of the ItemsControl we need to set additional properties, as shown in the following example:
<ItemsControl ="True">
<>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</>
</ItemsControl>
There are many other parameters in the VirtualizingPanel besides the IsVirtualizing parameter to control more details of the virtualization, the following is a description of the parameters:
- ="10"
What it does: The CacheLength property specifies the number of items the control needs to cache during virtualization. This means that the panel retains a certain number of items in areas outside the viewport to improve scrolling smoothness. Cached items can be reused more quickly when the user scrolls the view, thus reducing the overhead of re-creation and layout.
Value: 10 means that 10 items are cached outside the viewport. This is a relative value and the exact number may vary depending on the actual implementation. - ="Item"
Role: The CacheLengthUnit property defines the unit of the CacheLength. You can choose Pixel or Item, where Item means the length of the cache is measured in the number of items, and Pixel means the length of the cache is measured in pixels.
Value: Item Indicates that the length of the cache is in terms of the number of items. This applies when the item size is fixed or the amount of data is small. - ="True"
Purpose: The IsContainerVirtualizable property indicates whether the panel allows virtualization of the containers of its children. A setting of True indicates that the panel can virtualize its containers to optimize performance, especially when working with large amounts of data.
Value: True means container virtualization is enabled. - ="True"
What it does: The IsVirtualizing property indicates whether virtualization is enabled for the panel. This is a core setting for virtualization, and setting it to True means that the panel will render and process only the items within the viewport, rather than loading them all at once.
Value: True indicates that virtualization is enabled, reducing unnecessary control instantiation and layout calculations. - ="True"
Purpose: The IsVirtualizingWhenGrouping property controls whether the panel continues to virtualize when grouping. When set to True, the panel continues to apply virtualization policies when grouping data to maintain performance optimization.
Value: True Indicates that virtualization is maintained even when data is grouped. - ="Item"
Role: The ScrollUnit property defines the unit of scrolling. You can choose Item or Pixel, where Item means scroll one item at a time and Pixel means scroll a certain number of pixels at a time.
Value: Item indicates a unit of scrolling one item at a time, rather than a fixed number of pixels, which is especially effective when items are at a consistent height. - ="Recycling"
Purpose: The VirtualizationMode property specifies the virtualization mode.Recycling mode means that the control reuses containers for items that are no longer visible, rather than destroying them. This approach reduces the overhead of creating and destroying controls, thus improving performance.
Value: Recycling Indicates that reuse mode is enabled, allowing the panel to manage control instances more efficiently, suitable for scenarios with dynamic data changes.
II. CustomVirtualizingPanel
2.1 Basics
To develop our own virtualized panel we need to inherit from the VirtualizingPanel class and implement the IScrollInfo interface. The VirtualizingPanel provides methods for manipulating the panel's child controls, and the IScrollInfo interface defines custom behavior for the ScrollViewer control. The IScrollInfo interface defines the custom behavior of the ScrollViewer control, and by implementing IScrollInfo we can take over the operations of the ScrollViewer control.
The code is as follows (example):
public class CustomVirtualizingPanel : VirtualizingPanel, IScrollInfo
{
}
2.1.1 VirtualizingPanel
VirtualizingPanel has a property called "ItemContainerGenerator", which provides methods for creating and destroying child controls of the virtualized panel, and its workflow is as follows:
- When the scroll bar of the ScrollViewer control moves, get the offset of the scroll bar and calculate the position of the child control that should be displayed in the Panel by using the offset and the viewport size;
- Call (); generates the Panel child control from the specified position;
- Call (); removes Panel child controls outside the visible range;
2.1.2 IScrollInfo
public class CustomVirtualizingPanel : VirtualizingPanel, IScrollInfo
{
public ScrollViewer ScrollOwner { get; set; } //Current ScrollViewer control
public bool CanVerticallyScroll { get; set; } //Is it possible to scroll vertically?
public bool CanHorizontallyScroll { get; set; } //Is it possible to scroll horizontally?
public double ExtentWidth { get; } //The total width of the scrolling content (including visible and invisible parts)
public double ExtentHeight { get; } //Scroll the total height of the content (including visible and invisible parts)
public double ViewportWidth { get; } // ScrollViewer control can see the width of the part of the region
public double ViewportHeight { get; } // ScrollViewer control can see the height of the part of the region
public double HorizontalOffset { get; } // Offset of the horizontal scrollbar
public double VerticalOffset { get; } //offset of the vertical scrollbar
public void LineDown() { } //mouse click on the scrollbar down arrow action
public void LineLeft() { } //Mouse click on the left arrow of the scrollbar operation
public void LineRight() { } //mouse click on the right arrow of the scrollbar action
public void LineUp() { } //Mouse click on the upper arrow of the scrollbar action
public void MouseWheelDown() { } //MouseWheelDown operation
public void MouseWheelLeft() { } //MouseWheelLeft operation
public void MouseWheelRight() { } //MouseWheelRight operation
public void MouseWheelUp() { } //MouseWheelUp operation
public void PageDown() { } //Press the keyboard on the scroll bar up and down page operation
public void PageLeft() { } //Press the keyboard on the scroll bar on the left page of the operation
public void PageRight() { } //Press the keyboard on the scroll bar on the right page of the action
public void PageUp() { } // press the keyboard on the scroll bar on the page of the operation
public void SetHorizontalOffset(double offset) { } //Set the horizontal offset of the scrollbar
public void SetVerticalOffset(double offset) { } //Set the vertical offset of the scrollbar
public Rect MakeVisible(Visual visual, Rect rectangle) { return default; } //Forced scrolling Panel child control (such as only part of the area displayed in the visual range, after clicking the full scroll to the visual range)
}
2.2 Practical cases
2.2.1 Demand analysis
- CustomVirtualizingPanel should be highly flexible to meet different virtualization layout requirements with minimal effort, without having to rewrite a CustomVirtualizingPanel control every time.
- It is better to be able to switch layouts via attributes so that you can realize transition effects when switching layouts.
2.2.2 Code Implementation
Through the analysis to achieve the above effect, the best way is to CustomVirtualizingPanel need to calculate the key part of the abstraction made into an interface, when the need for layout calculation we can directly through the interface to get the key calculation results.
1) Define the interface
public interface IVirtualizingPanelBuilder
{
void Initialize(CustomVirtualizingPanel virtualizingPanel);
double GetItemWidth(Size availableSize);
double GetItemHeight(Size availableSize);
int CalculateItemsPerRowCount(Size availableSize);
int CalculateRowCount(Size availableSize);
Size CalculateExtent(Size availableSize);
ItemRange CalculateItemRange(Size availableSize);
}
2) Add the property VirtualizingPanelBuilder to the CustomVirtualizingPanel class.
public IVirtualizingPanelBuilder VirtualizingPanelBuilder
{
get { return (IVirtualizingPanelBuilder)GetValue(VirtualizingPanelBuilderProperty); }
set { SetValue(VirtualizingPanelBuilderProperty, value); }
}
3) Implement VirtualizingPanelBuilder.
public class VirtualizingStackPanelBuilder : DependencyObject, IVirtualizingPanelBuilder
{
/// <summary>
/// virtual panel
/// </summary>
private CustomVirtualizingPanel _virtualizingPanel;
/// <summary>
/// initialization
/// </summary>
/// <param name="virtualizingPanel"></param>
public void Initialize(CustomVirtualizingPanel virtualizingPanel)
{
_virtualizingPanel = virtualizingPanel;
}
/// <summary>
/// gainItemhigh degree
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
public double GetItemHeight(Size availableSize)
{
if (ItemHeight > 0)
return ItemHeight;
else if (_virtualizingPanel. != 0)
return _virtualizingPanel.Children[0].;
else
return _virtualizingPanel.CalculateChildSize(availableSize).Height;
}
/// <summary>
/// gainItemheight
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
public double GetItemWidth(Size availableSize)
{
return ;
}
/// <summary>
/// Calculate the number of lines displayed in eachItemcriticize (i.e. enumerate shortcomings)
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
public int CalculateItemsPerRowCount(Size availableSize)
{
return 1;
}
/// <summary>
/// 计算行criticize (i.e. enumerate shortcomings)
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
public int CalculateRowCount(Size availableSize)
{
return _virtualizingPanel.;
}
/// <summary>
/// Calculate rolling area
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
public Size CalculateExtent(Size availableSize)
{
var height = GetItemHeight(availableSize);
var rowCount = CalculateRowCount(availableSize);
return new Size(, height * rowCount);
}
/// <summary>
/// Calculate the visible region of theItemrealm
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
public ItemRange CalculateItemRange(Size availableSize)
{
if (!this._virtualizingPanel.IsVirtualizing)
{
return new ItemRange(0, this._virtualizingPanel. - 1);
}
var viewportHeight = _virtualizingPanel.ViewportHeight;
var offsetY = _virtualizingPanel.VerticalOffset;
var rowCount = (availableSize);
var itemHeight = (availableSize);
var firstVisibleItemIndex = (int)(offsetY / itemHeight);
var lastVisibleItemIndex = (int)((offsetY + viewportHeight) / itemHeight) - 1;
if (lastVisibleItemIndex >= rowCount)
lastVisibleItemIndex = rowCount - 1;
return new ItemRange(firstVisibleItemIndex, lastVisibleItemIndex);
}
/// <summary>
/// Itemhigh degree
/// </summary>
public double ItemHeight
{
get { return (double)GetValue(ItemHeightProperty); }
set { SetValue(ItemHeightProperty, value); }
}
public static readonly DependencyProperty ItemHeightProperty =
("ItemHeight", typeof(double), typeof(VirtualizingStackPanelBuilder), new PropertyMetadata(ItemHeightPropertyChangedCallback));
public static void ItemHeightPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((VirtualizingStackPanelBuilder)d)._virtualizingPanel?.InvalidateVisual();
}
}
4) Setting parameters
<local:CustomVirtualizingPanel>
<local:>
<local:VirtualizingStackPanelBuilder ItemHeight="100" />
</local:>
</local:CustomVirtualizingPanel>
2.2.3 Operational effects
In order to demonstrate the transition effect of layout switching, here in addition to the above StackPanel layout also implements the UniformGrid layout, the following demonstration of 100 million data layout switching and non-virtualized state of the layout switching transition effect.
1) Virtualization Switching Layout
2) Non-virtualized switching transition effects