允许动态项目大小、重新排序和虚拟化的 WinUI 列表控件

WinUI list control that allows for dynamic item size, reordering, and virtualization

我正在尝试在 WinUI/UWP 应用程序中实现类似于 Notion 或 Craft 的文档编辑器。这些应用程序不使用“single-field”编辑器(就像 MS Word 那样),而是显示垂直堆叠的内容行(so-called“块”),其中可以包含文本、媒体、link预览、LaTeX 等。这些行可以用侧面的 :: 句柄重新排列:

所以我一直在寻找一些 list/stack 控件:

不需要需要的功能:headers、列、排序、过滤。我查看了 WinUI 2.x 和 Windows Community Toolkit 中的以下控件,但看起来它们中的每一个都缺少一个或多个必需的功能。

ListView

它似乎是 go-to 列表的拖放控件,但它不能动态调整项目的大小。此外,它的拖动使用了整个项目区域,而我需要使其仅在侧面有一个 :: 手柄时可用。

ItemsStackPanel

StackPanel 的 virtualization-supporting 版本,但据我了解,面板应该用于 child 项目的简单布局,而不是用于呈现基于在数据源上。

VariableSizedWrapGrid

这是唯一一个官方声明支持variable-sized项的list/grid控件,但同时不支持虚拟化。但是,我找到了一个 solution from 2013,它基于 pre-calculating 不可见元素的内容大小。

ItemsRepeater

一个非常基本的控件,它本身不提供虚拟化:“ItemsRepeater 在连接到支持虚拟化的主机时支持虚拟化。”

DataGrid

来自 WCT 的相当严格的控制似乎是唯一 dynamically resize cells 取决于它们的内容。不幸的是,它不允许行重新排序(只能排序),所以我也不能使用它。


我错过了什么吗?如果没有,我想知道哪个是最好的构建基础。谢谢!

不得不说,没有一个控件可以满足所有需求。其实::是用来控制darg指定item的,你可以在ListView控件中实现类似的行为。

ListView 允许您通过将 CanReorderItemsAllowDrop 属性设置为 True 来轻松地重新排序 ListView 中的项目。如果您不想要允许用户拖动特定项目,您可以将 CanDragItems 属性 设置为 True,并为 DragItemsStarting 事件添加事件处理程序。对于事件处理程序,您可以检查任何条件,然后如果要取消拖动操作,只需将事件参数的 Cancel 属性 设置为 true。如下:

Xaml代码:

<ListView x:Name="TargetListView"              
                CanReorderItems="True" CanDragItems="True" AllowDrop="True"         DragItemsStarting="TargetListView_DragItemsStarting">

            <ListViewItem>
                <TextBlock x:Name="item1" Text="item1"/>
            </ListViewItem>
            <ListViewItem >
                <TextBlock x:Name="item2" Text="item2"/>
            </ListViewItem>
            <ListViewItem>
                <TextBlock x:Name="item3" Text="item3"/>
            </ListViewItem>
          
  </ListView>

后面的代码:

private void TargetListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
        {
            e.Cancel = e.Items.Any(o =>
            {
                if (o is TextBlock t && t.Name.ToString() == "item2")
                    return true;

                return false;
            });
        }

可以看到,ListView中有3个textBlock,其中名为item2的textblock的拖动动作会被限制。

事实证明,ListView 可以 动态调整其项目的大小——只需要将 StackPanel 放在 ListViewItem 中,因为堆栈将其父级的大小“向外推”。正如 所说:

Container controls like Grid and RelativePanel automatically scale to the full available size of their parent, while others like StackPanel only grow to the minimal size needed for their child elements.

此外,在 ListViewItem 中添加 TextBox 会用文本输入行为覆盖拖动行为,但列表项仍可拖动到文本框外。我可以这样保留它,也可以按照 .

将可拖动区域缩小到 :: 手柄