Skip to content

虚拟化面板(VirtualizingPanel) ​

仅渲染可见区域的列表控件,适用于大数据量列表。通过模板和数据源绑定,自动创建/回收子控件。

定义位置:GameUI.Control.Primitive.VirtualizingPanel

继承链:VirtualizingPanel → PanelScrollable → Panel → Control

基本用法 ​

csharp
// 创建垂直虚拟列表
var panel = VirtualizingExtensions.VirtualList(itemTemplate, itemSize: new SizeF(0, 40));
panel.Items(dataList);
panel.Parent = parentControl;

构造 ​

csharp
VirtualizingPanel();
VirtualizingPanel(IGameLink<GameDataControlVirtualizingPanel> link);

数据绑定 ​

ItemsSource ​

csharp
IEnumerable<object>? ItemsSource { get; set; }

设置数据源后,调用 GenerateChildren() 触发虚拟化。子控件通过 Control.DataContext 获取绑定的数据项,Control.ItemIndex 获取索引。

模板 ​

csharp
IGameLink<GameDataControl>? ItemTemplate { get; set; }                // 单一模板
ItemTemplateSelector? ItemTemplateSelector { get; set; }              // 按数据项选择模板
delegate IGameLink<GameDataControl>? ItemTemplateSelector(object item);

属性 ​

属性类型默认值说明
ItemSizeSizeF?null固定项尺寸(提高性能)
CacheLengthVirtualizationCacheLength视口外缓冲区大小
CacheLengthUnitVirtualizationCacheLengthUnitPage缓冲区单位
ArrangeOnScrollboolfalse滚动时立即重排
IsVirtualizingbool只读是否正在虚拟化

滚动属性(继承自 PanelScrollable) ​

属性类型说明
ScrollOrientationOrientation滚动方向
FlowOrientationOrientation?子项排列方向
ScrollEnabledbool是否允许滚动
ScrollBarValuefloat滚动位置
ScrollBarSizefloat滚动条大小
ScrollBarColorColorBrush滚动条颜色

方法 ​

csharp
void GenerateChildren();                    // 启动虚拟化
void InvalidateMeasure();                   // 重新计算布局
void InvalidateArrange();                   // 重新排列
void ClearCache();                          // 清除回收池
Control? GetChildByItem(int itemIndex);     // 按数据索引获取控件
bool BringItemIntoView(int itemIndex);      // 滚动到指定项

事件 ​

csharp
// 子控件虚拟化/取消虚拟化时触发(可用于初始化/清理)
event EventHandler<ApplyControlTemplatePhaseEventArgs>? OnChildVirtualizationPhase;

// 滚动变化(继承自 PanelScrollable)
event EventHandler<ScrollChangedEventArgs>? OnScrollChanged;

工厂方法 ​

VirtualizingExtensions 提供快捷创建:

csharp
// 垂直列表
static VirtualizingPanel VirtualList(
    IGameLink<GameDataControl> itemTemplate,
    SizeF? itemSize = null,
    float cachePages = 1.0f);

// 水平列表
static VirtualizingPanel VirtualHorizontalList(
    IGameLink<GameDataControl> itemTemplate,
    SizeF? itemSize = null,
    float cachePages = 1.0f);

// 带模板选择器
static VirtualizingPanel VirtualListWithSelector(
    ItemTemplateSelector selector,
    Orientation orientation = Orientation.Vertical,
    SizeF? itemSize = null,
    float cachePages = 1.0f);

流式配置 ​

csharp
var panel = VirtualizingExtensions.VirtualList(template)
    .ItemSize(0, 60)
    .Cache(0.5f, 0.5f, VirtualizationCacheLengthUnit.Page)
    .Items(dataList)
    .ArrangeOnScroll(true);

流式方法 ​

csharp
static VirtualizingPanel Cache(this VirtualizingPanel panel,
    float beforeViewport, float afterViewport,
    VirtualizationCacheLengthUnit unit = Page);
static VirtualizingPanel ItemSize(this VirtualizingPanel panel, float width, float height);
static VirtualizingPanel ItemSize(this VirtualizingPanel panel, float size);
static VirtualizingPanel Template(this VirtualizingPanel panel, IGameLink<GameDataControl> template);
static VirtualizingPanel TemplateSelector(this VirtualizingPanel panel, ItemTemplateSelector selector);
static VirtualizingPanel Items<T>(this VirtualizingPanel panel, IEnumerable<T> items,
    bool autoVirtualization = true);
static VirtualizingPanel ArrangeOnScroll(this VirtualizingPanel panel, bool enabled = true);

子控件交互 ​

VirtualizingPanel 没有内置选择/点击处理。通过子控件事件实现:

csharp
panel.OnChildVirtualizationPhase += (s, e) =>
{
    var child = e.Control;
    var data = child.DataContext;    // 绑定的数据项
    var index = child.ItemIndex;    // 数据索引

    child.Click(() =>
    {
        Game.Logger.LogInformation("点击了第 {Index} 项", index);
    });
};

回收机制 ​

  1. 设置 ItemsSource + ItemTemplate(或 ItemTemplateSelector)
  2. GenerateChildren() 启动虚拟化
  3. 滚动时,离开视口的控件进入 RecyclePool
  4. 新进入视口的控件优先从回收池取出复用,减少实例化开销
  5. BeforeViewportPlaceHolder / AfterViewportPlaceHolder 占位撑开滚动区域

性能建议 ​

  • 设置固定 ItemSize 避免逐项测量
  • CacheLength 控制预渲染量,过大浪费内存,过小导致闪烁
  • 使用 ArrangeOnScroll(true) 在快速滚动时更流畅

相关文档 ​