在WPF的时代,我们多是使用ListBox和ListView来展示,纵向滚动条显示的集合数据。这两个控件的默认样式,以及对触控的支持,已完全落后于时代。他们两个分别长这样,和Win10及Win11的风格完全不沾边。

今天我来看下WinUI 3中适用于Desktop App的ListView,我们略过ListBox是因为ListBox使用较为简单,通常用于将少量数据同时显示在页面上。
而显示需要滚动条的大量数据,会推荐使用ListView,同时ListView的表现形式也较为丰富。例如分组,拖拽,缩放,以及页头页脚的自定义。
本篇我们将试着应用以上这些特性到Desktop App中,通过WinUI 3库,我们不再需要Xaml Islands这样曲线救国的做法。而是真正在Desktop App中用上原生的新ListView。
首先我们来看分组,WinUI 3中的ListView通过CollectionViewSource这个组件来实现分组功能。当然我们也可以通过嵌套集合的方式来实现,例如将ListView的ListViewItem同样也包含一个ListView/ItemsControl。但是这样做有一些缺陷,第一是会破坏UI虚拟化(UI virtualization),因为用于虚拟化的容器(Item Container)在面对重复的Item才会起到效果,用ListView/ItemsControl这样的可变集合作为Item,在数量增多后会有明显的性能问题。第二是嵌套集合的话,SeletedItem和ItemClick处理起来会比较困难。所以我更推荐使用CollectionViewSource。

ListView的分组并不复杂,在XAML中我们需要于Resources节点中放置CollectionViewSource对象,该对象是真正数据源的一个视图。我们可以对这个视图做分组和排序,但不会自动影响到真正的数据源。
假设我们有一个Person类:

    public class Person
{
public string Name { get; set; }
}

并构建了PersionList作为数据源,同时创建了分组视图PersonGroup。

            this.PersonList = new List<Person>
{
new Person{ Name = "Abe"},
new Person{ Name = "Alice"},
new Person{ Name = "Bell"},
new Person{ Name = "Ben"},
new Person{ Name = "Bob"},
new Person{ Name = "Fox"},
new Person{ Name = "Gray"},
new Person{ Name = "James"},
new Person{ Name = "Jane"},
new Person{ Name = "Roy"},
new Person{ Name = "Vincent"}
};
PersonGroup = PersonList.GroupBy(p => p.Name.First().ToString());

那么我们在XAML中,将通过如下形式的binding来使用该分组视图:

    <Grid>
<Grid.Resources>
<CollectionViewSource x:Name="personListCVS" IsSourceGrouped="True" Source="{x:Bind PersonGroup}"/>
</Grid.Resources>
<ListView ItemsSource="{x:Bind personListCVS.View}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

上面的XAML中定义简单的仅TextBlock的HeaderTemplate和ItemTemplate,实际生产中可按需编写更复杂的模板。
接下来我们看拖拽的实现,这里我们创建一个DragDropPage.xaml,假设ListView作为被拖走数据的一方,在DataTemplate的子节点上,将CanDrag设置为True,因为接受数据的需要,我们要明确被拖拽走的是个什么东西,通过DragStarting事件可以获得被操作的UIElement。

        <ListView Grid.Column="0" ItemsSource="{x:Bind PersonList}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" CanDrag="True" DragStarting="TextBlock_DragStarting"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

作为接受拖拽对象的ComboxBox,除了设置AllowDrop=True以外,还需要通过DragOver来响应图标的变化(假设在鼠标移过来后,图标从“无效”的样式转变成了“复制”,表示可以接受该数据),以及通过Drop来处理接受数据。

        <ComboBox Grid.Column="1" ItemsSource="{x:Bind PersonList}" SelectedItem="{x:Bind SelectedPerson,Mode=TwoWay}"
AllowDrop="True" DragOver="ComboBox_DragOver" Drop="ComboBox_Drop">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

在创建本篇的Sample Code时,WinUI 3 的SDK版本已经从0.5更新到0.8了(稳定版)。但是在Drop事件里依然遇到了bug,无法访问DragEventArgs中的DataView来获取DragStarting时存放的数据。这个问题已经有大量的issue开给了某软,并不是WinUI 3库本身的bug,锅已丢给CsWinRT项目,但什么时候修好就不知道了。
Unable to drop files onto Grid in WinUI3 Desktop · Issue #2715 · microsoft/microsoft-ui-xaml (github.com)
DataView无法使用,直接影响了应用间的数据传递。对于本篇的APP内部的拖拽,倒是可以写个Property绕过去。拖拽操作的流程先是通过DragStarting来获取和存放要传递的数据。DragOver负责更改图标,反馈给用户某个控件可以接受Drop操作。最后由Drop来接受数据和更新UI。

        private void ComboBox_Drop(object sender, DragEventArgs e)
{
//if (e.DataView.Contains(StandardDataFormats.Text))
//{
// var name = await e.DataView.GetTextAsync();
// this.SelectedPerson = this.PersonList.FirstOrDefault(p => p.Name == name);
//} this.SelectedPerson = DragPerson;
} private void ComboBox_DragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
} private void TextBlock_DragStarting(UIElement sender, DragStartingEventArgs args)
{
DragPerson = (sender as TextBlock).DataContext as Person;
//args.Data.SetData(StandardDataFormats.Text, DragPerson.Name);
}

本篇最后我想讨论下<ListView.Header>和<ListView.Footer>,通常认为额外做一个<StackPanel>或<Grid>放置在ListView的上下方即可。不过我想说的是,如果遇到Layout变化,通过VisaulState等方式来位移控件的情况,一个整体的ListView会比较方便。在ListViewHeaderFooterPage中,假设ListView上方存在MenuBar,下部需要CommandBar。如果这两个控件一直和ListView保持紧密的联系,那就可以放到Header和Footer中作为一个整体。

以上就是本篇对WinUI 3中ListView的一些讨论。缩放视图打算后面结合SemanticZoom 再来演示。感谢阅读到这里的同学们!

Sample Code:
https://github.com/manupstairs/WinUI3Samples/tree/main/WinUI3Samples/ListViewSample

以下链接,是MS Learn上Windows开发的入门课程,单个课程三十分钟到60分钟不等,如需补充基础知识的同学点这里:

开始使用 Visual Studio 开发 Windows 10 应用

开发 Windows 10 应用程序

编写首个 Windows 10 应用

创建 Windows 10 应用的用户界面 (UI)

增强 Windows 10 应用的用户界面

在 Windows 10 应用中实现数据绑定

WinUI 3学习笔记(2)—— 用ListView来展示集合的更多相关文章

  1. WinUI 3学习笔记(3)—— ComboBox & DropDownButton & SplitButton

    本篇想介绍相对小众但颇具使用价值的控件SplitButton,提到SplitButton难免会拿来与ComboBox进行比较,同时在WinUI 3的控件库中,还有一个默默无闻的DropDownButt ...

  2. springmvc学习笔记(13)-springmvc注解开发之集合类型參数绑定

    springmvc学习笔记(13)-springmvc注解开发之集合类型參数绑定 标签: springmvc springmvc学习笔记13-springmvc注解开发之集合类型參数绑定 数组绑定 需 ...

  3. Java学习笔记——浅谈数据结构与Java集合框架(第一篇、List)

    横看成岭侧成峰,远近高低各不同.不识庐山真面目,只缘身在此山中. --苏轼 这一块儿学的是云里雾里,咱们先从简单的入手.逐渐的拨开迷雾见太阳.本次先做List集合的三个实现类的学习笔记 List特点: ...

  4. Android学习笔记(20)————利用ListView制作带竖线的多彩表格

    http://blog.csdn.net/conowen/article/details/7421805 /********************************************** ...

  5. AppCan学习笔记----关闭页面listview动态加载数据

    AppCan页面关闭 AppCan 的页面是由两个HTML组成,如果要完全关闭的话需要在主HTML eg.index.html中关闭,关闭方法:appcan.window.close(-1); 管道 ...

  6. ANDROID_MARS学习笔记_S04_008_用Listview、自定义adapter显示返回的微博数据

    一.简介 运行结果 二.代码1.xml(1)activity_main.xml <?xml version="1.0" encoding="utf-8"? ...

  7. Android(java)学习笔记194:ListView编写步骤(重点)

    1.ListView在我们的手机android编写程序中使用是十分广泛的,比如如下图中 短信 和 手机设置 都是ListView的效果: 手机设置:             短信:    2.正因为这 ...

  8. Android(java)学习笔记137:ListView编写步骤(重点)

    1.ListView在我们的手机android编写程序中使用是十分广泛的,比如如下图中 短信 和 手机设置 都是ListView的效果: 手机设置:             短信:    2.正因为这 ...

  9. JavaScript学习笔记——简单无缝循环滚动展示图片的实现

    今天做了一个简单的无缝循环滚动的实例,这种实例在网页中其实还挺常见的,下面分享一下我的学习收获. 首先,无缝滚动的第一个重点就是——动.关于怎么让页面的元素节点动起来,这就得学明白关于JavaScri ...

随机推荐

  1. Arduino库和STM32的寄存器、标准库、HAL库、LL库开发比较之GPIO

    标题: Arduino库和STM32的寄存器.标准库.HAL库.LL库开发比较之GPIO 作者: 梦幻之心星 sky-seeker@qq.com 标签: [#Arduino,#STM32,#库,#开发 ...

  2. 如何在国产龙芯架构平台上运行c/c++、java、nodejs等编程语言

    高能预警:本文内容过于硬核,涉及编译器原理.cpu指令集.机器码.编程语言原理.跨平台原理等计算机专业基础知识,建议具有c.c++.java.nodejs等多种编程语言开发能力,且实战经验丰富的资深开 ...

  3. 『心善渊』Selenium3.0基础 — 14、Selenium对单选和多选按钮的操作

    目录 1.页面中的单选按钮和多选按钮 2.判断按钮是否选中is_selected() 3.单选按钮的操作 4.多选按钮的操作 5.选择部分多选按钮的操作 1.页面中的单选按钮和多选按钮 页面中的单选按 ...

  4. 区分DDD中的Domain, Subdomain, Bounded Context, Problem/Solution Space

    区分DDD中的Domain, Subdomain, Bounded Context, Problem/Solution Space 译自: Domain, Subdomain, Bounded Con ...

  5. 17、lnmp_php编译安装

    17.1.FastCGI介绍: 1.什么是CGI: CGI的全称为"通用网关接口",为http服务器与其他机器上的程序服务通信交流的一种工具,CGI程序 必须运行在网络服务器上:传 ...

  6. C#Expression合集

    一:总体概览 1:获取属性值: 2:调用方法 3:动态构造条件 4:创建对象 5:Switch Case 6:Try Catch 以及捕获异常信息并输出 7:if  esle 8:+  / += 9: ...

  7. 开始前端三大基础的js之途

                                                     初识   js                                             ...

  8. 12 shell case in语句

    Shell也支持两种分支结构(选择结构),分别是 if else 语句和 case in 语句.当分支较多,并且判断条件比较简单时,使用 case in 语句就比较方便了. if else 语句与ca ...

  9. 每日单词 —— cut to the chase

    cut to the chase: 词面意思--切奶酪 是美国俚语--开门见山:直奔主题 看一看相关的例句或者电影读白吧! 1. - 嫌疑人:Shall we cut to the chase ? 侦 ...

  10. Spring学习总结(一)---谈谈对Spring IOC的理解(一:理论知识理解)

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...