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

问题描述

我正在尝试在 WinUI/UWP 应用程序中实现类似于 Notion 或 Craft 的文档编辑器。这些应用程序不使用“单字段”编辑器(如 MS Word 那样),而是显示垂直堆栈的内容行(所谓的“块”),其中可以包含文本、媒体、链接预览、LaTeX 等。这些行可以通过侧面的 :: 手柄重新排列:

Notion UI recording

所以我一直在寻找一些列表/堆栈控件:

  • 回收不可见的行视图(即 XAML 术语中的“虚拟化”)。
  • 允许通过拖放行重新排序(单个或多个)。
  • 根据内容的高度增长或缩小(由于用户的输入或因为应用的窗口水平调整大小并包裹一些内容自动调整行高。

不需要功能标题、列、排序、过滤。我已经查看了 WinUI 2.x 和 Windows Community Toolkit 中的以下控件,但似乎每个控件都缺少一项或多项必需的功能

ListView

它似乎是带有拖放功能的列表的首选控件,但它无法动态调整项目的大小。此外,它的拖动使用了整个项目区域,而我只需要让它在侧面带有 :: 句柄即可使用。

ItemsStackPanel

支持虚拟化的 StackPanel 版本,但据我所知,面板应该用于子项的简单布局,而不是用于呈现基于数据源的长列表。

VariableSizedWrapGrid

这是唯一正式声明支持可变大小项目的列表/网格控件,但同时不支持虚拟化。但是,我发现了一个 solution from 2013,它基于预先计算不可见元素的内容大小。

ItemsRepeater

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

DataGrid

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


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

解决方法

我不得不说没有任何控件可以满足所有要求。实际上,::是用来控制darg指定项的,你可以在ListView控件中实现类似的行为。

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

XML 代码:

<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 中,因为堆栈会“推送”其父级的大小向外。正如this answer所说:

GridRelativePanel 等容器控件会自动缩放到其父元素的完整可用大小,而其他类似 StackPanel 的容器控件只会扩展到其子元素所需的最小大小。>

此外,在 TextBox 内添加 ListViewItem 会使用文本输入行为覆盖拖动行为,但列表项仍可拖动到文本框外。我可以保持原样,也可以按照 Arya's answer 将可拖动区域缩小到 :: 句柄。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...