WPF 列表控件数据源绑定多个数据集合方法
在 WPF 用的多的列表控件如 ListBox 或 ListView 等,本文告诉大家在这些列表控件上进行绑定多个数据集合来源的多个实现方法。如有一个显示动物列表的控件,需要绑定的数据来源是阿猫和阿狗两个 ObservableCollection 列表,不在后台代码编写合并集合的代码情况下,可以通过 XAML 的编写,绑定多个数据集合
准备
在开始之前,咱先搭建一点测试使用的代码,假定咱有一个 列表控件 准备绑定到的数据源是两个 ObservableCollection 对象,下面来定义这两个 ObservableCollection 对象和对应的 阿猫和阿狗 的代码
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 10; i++)
{
Dogs.Add(new Dog()
{
Name = "Dog" + i
});
Cats.Add(new Cat()
{
Name = "Cat" + i
});
}
DataContext = this;
}
public ObservableCollection<Dog> Dogs { get; } = new ObservableCollection<Dog>();
public ObservableCollection<Cat> Cats { get; } = new ObservableCollection<Cat>();
}
public class Dog : Animal
{
}
public class Cat : Animal
{
}
public class Animal
{
public string Name { get; set; }
}
可以看到以上代码里面存在两个 ObservableCollection 对象,同时 MainWindow 的 DataContext 就是 MainWindow 对象。咱需要将两个 ObservableCollection 对象作为数据源,放在相同的一个 ListBox 里面
下面是多个不同的实现方式,解决如何在 WPF 中在 ListBox 或 ListView 绑定多个数据集合 ObservableCollection 对象
通过 CollectionViewSource 方式
在 ListView 或 ListBox 资源里面,添加 CollectionViewSource 绑定到集合里面,然后在 ItemsSource 使用 CompositeCollection 进行绑定,代码如下
<ListBox>
<ListBox.Resources>
<CollectionViewSource x:Key="DogCollection" Source="{Binding Dogs}"/>
<CollectionViewSource x:Key="CatCollection" Source="{Binding Cats}"/>
</ListBox.Resources>
<ListBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource DogCollection}}"/>
<CollectionContainer Collection="{Binding Source={StaticResource CatCollection}}"/>
</CompositeCollection>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
这个方法的优势在于可以完全使用 XAML 编写内容,但是缺点在于有重复的代码,如有多个绑定的集合对象,就需要在资源和 CompositeCollection 里面定义多个 CollectionViewSource 和 CollectionContainer 对象
如果绑定的集合数量不多,那么此写法还成,但如果集合数量比较多,而且需要不断变更顺序,那以上写法就有坑
此方法请参考 WPF 很少人知道的科技 - walterlv
通过 CompositeCollection 动态绑定
在 ListView 或 ListBox 的资源里面定义了 CompositeCollection 通过控件的 DataContext 绑定多个集合,代码如下
<CompositeCollection x:Key="MyColl">
<CollectionContainer Collection="{Binding DataContext.Dogs, Source={x:Reference MyList}}"/>
<CollectionContainer Collection="{Binding DataContext.Cats, Source={x:Reference MyList}}"/>
</CompositeCollection>
以上代码的 MyList 就是集合控件,此方法需要用到 x:Reference 获取对象的引用,同时需要通过 DataContext 的某个属性获取到对应的属性,全部代码如下
<ListBox x:Name="MyList" ItemsSource="{DynamicResource MyColl}">
<ListBox.Resources>
<CompositeCollection x:Key="MyColl">
<CollectionContainer Collection="{Binding DataContext.Dogs, Source={x:Reference MyList}}"/>
<CollectionContainer Collection="{Binding DataContext.Cats, Source={x:Reference MyList}}"/>
</CompositeCollection>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
对比上面的方法,此方法可以让绑定集合的代码只写一次,看起来代码更少一点。但不足的地方在于绑定 ItemsSource 需要用到 DynamicResource 的方式,相对性能不如上面方法。为什么需要 DynamicResource 资源?原因是资源本身定义在 Resources 里面。为什么资源需要定义在控件里面的 Resource 里面?原因是为了获取到控件的 x:Reference 对象。也就是说需要在控件创建出来之后,才能通过 x:Reference 获取控件,而控件的数据内容需要依赖资源的定义,因此也只有以上方式的写法
如果能从控件的上层容器拿到数据对象,那可以将资源定义在容器里面,通过 StaticResource 绑定到静态资源。如放在 Window 的 Resources 里
<Window x:Class="CibairyafocairluYerkinemde.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CibairyafocairluYerkinemde"
mc:Ignorable="d"
x:Name="Root"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<CompositeCollection x:Key="MyColl">
<CollectionContainer Collection="{Binding DataContext.Dogs, Source={x:Reference Root}}"/>
<CollectionContainer Collection="{Binding DataContext.Cats, Source={x:Reference Root}}"/>
</CompositeCollection>
</Window.Resources>
<Grid>
<ListBox x:Name="MyList" ItemsSource="{StaticResource MyColl}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
以上写法没有啥缺点,也不存在动态资源的性能问题。但实际上在有动态资源下,性能问题也是很小的问题,对比渲染控件本身,动态绑定性能可以忽略
通过多绑定方法
此方法需要添加一点后台代码,定义 CompositeCollectionConverter 转换器,实现逻辑是通过多绑定的方法,将多个数据集合当成多个参数进行绑定
<ListBox>
<ListBox.ItemsSource>
<MultiBinding Converter="{x:Static local:CompositeCollectionConverter.Default}">
<Binding Path="Dogs" />
<Binding Path="Cats" />
</MultiBinding>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
可以看到此方法的 XAML 代码量最小,只是需要一个辅助的 CompositeCollectionConverter 类,代码如下
public class CompositeCollectionConverter : IMultiValueConverter
{
public static readonly CompositeCollectionConverter Default = new CompositeCollectionConverter();
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var compositeCollection = new CompositeCollection();
foreach (var value in values)
{
if (value is IEnumerable enumerable)
{
compositeCollection.Add(new CollectionContainer { Collection = enumerable });
}
else
{
compositeCollection.Add(value);
}
}
return compositeCollection;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException("CompositeCollectionConverter ony supports oneway bindings");
}
}
可以将 CompositeCollectionConverter 放在库里面,这样就可以让 XAML 代码看起来简单
本文所有代码放在 github 和 gitee 欢迎小伙伴访问
参考
本文以上方法参考了如下博客
wpf - How do you bind a CollectionContainer to a collection in a view model? - Stack Overflow
WPF 列表控件数据源绑定多个数据集合方法的更多相关文章
- ASP .NET MVC HtmlHelper扩展——简化“列表控件”的绑定
在众多表单元素中,有一类<select>元素用于绑定一组预定义列表.传统的ASP.NET Web Form中,它对应着一组重要的控件类型,即ListControl,我们经常用到DropDo ...
- 在Bootstrap开发框架中使用dataTable直接录入表格行数据(2)--- 控件数据源绑定
在前面随笔<在Bootstrap开发框架中使用dataTable直接录入表格行数据>中介绍了在Web页面中使用Jquery DataTable插件进行对数据直接录入操作,这种处理能够给用户 ...
- C#.NET 通用控件数据源绑定类
using System.Data; using System.Collections; using System.Collections.Generic; using System.Web.UI; ...
- WPF Image控件的绑定
在我们平时的开发中会经常用到Image控件,通过设置Image控件的Source属性,我们可以加载图片,设置Image的source属性时可以使用相对路径也可以使用绝对路径,一般情况下建议使用绝对路径 ...
- WPF 列表控件中的子控件上下文绑定
<DataGrid Grid.ColumnSpan=" Height="Auto" SelectedItem="{Binding Path=SelectP ...
- silverlight列表控件ComboBox 托管代码绑订数据集合
.xaml <ComboBox x:Name="myCombobox" Width="300" Height="30"> < ...
- .NET各大平台数据列表控件绑定原理及比较(WebForm、Winform、WPF)
说说WebForm: 数据列表控件: WebForm 下的列表绑定控件基本就是GridView.DataList.Repeater:当然还有其它DropDownList.ListBox等. 它们的共同 ...
- WPF DevExpress Chart控件 界面绑定数据源,不通过C#代码进行绑定
<Grid x:Name="myGrid" Loaded="Grid_Loaded" DataContext="{Binding PartOne ...
- 【WPF开发备忘】使用MVVM模式开发中列表控件内的按钮事件无法触发解决方法
实际使用MVVM进行WPF开发的时候,可能会用到列表控件中每行一个编辑或删除按钮,这时直接去绑定,发现无法响应: <DataGridTemplateColumn Header="操作& ...
- WPF: 实现带全选复选框的列表控件
本文将说明如何创建一个带全选复选框的列表控件.其效果如下图: 这个控件是由一个复选框(CheckBox)与一个 ListView 组合而成.它的操作逻辑: 当选中“全选”时,列表中所有的项目都 ...
随机推荐
- Linux快速入门(二)Linux基础操作
绝对路径和相对路径 Linux中绝对路径指从根目录开始,即/目录. 相对路径是指相对于当前路径开始,即./ 目录,../表示上一级目录路径. pwd pwd命令用于查看当前所在目录. gubeiqin ...
- oracle job 带参数
declare job number; begin sys.dbms_job.submit(job => job, what => 'DECLARE S1 VARCHAR2(4000); ...
- AXI4的主机协议代码分析
AXI4的主机协议代码分析 一.模块分析 (1)端口列表 input wire INIT_AXI_TXN, // Asserts when ERROR is detected output reg E ...
- 探索华为云CCE敏捷版金融级高可用方案实践案例
本文分享自华为云社区<华为云CCE敏捷版金融级高可用方案实践>,作者: 云容器大未来. 一.背景 1.1. CCE 敏捷版介绍 云原生技术有利于各组织在公有云.私有云和混合云等新型动态环境 ...
- RelationNet:学习目标间关系来增强特征以及去除NMS | CVPR 2018
论文基于NLP的注意力机制提出了目标关系模块,通过与其它目标的比对增强当前目标的特征,而且还可以代替NMS进行端到端的重复结果去除,思想十分新颖,效果也不错 来源:晓飞的算法工程笔记 公众号 论文 ...
- Linux服务器程序规范化
Linux日志体系 rsyslogd守护进程既能接收用户进程输出的日志,又能接收内核日志.用户进程是通过调用syslog函数生成系统日志的.该函数将日志输出到一个UNIX本地域socket类型(AF_ ...
- 鸿蒙HarmonyOS实战-ArkUI组件(TextInput/TextArea)
一.TextInput/TextArea TextInput和TextArea组件通常用于收集用户输入的文本数据. TextInput组件通常用于单行文本的输入,它允许用户通过一个光标来输入文字,并支 ...
- Java 继承与多态:代码重用与灵活性的巧妙结合
Java 继承(子类和超类) 在 Java 中,可以从一个类继承属性和方法到另一个类.我们将"继承概念"分为两类: 子类(child): 从另一个类继承的类 超类(parent): ...
- 数据库操作入门:PyMongo 和 MongoDB 的基本用法
MongoDB MongoDB是一种流行的NoSQL数据库,它将数据存储在类似JSON的文档中,使数据库非常灵活和可扩展 PyMongo Python需要一个MongoDB驱动程序来访问MongoDB ...
- C语言 06 无符号数
所有的数据底层都是采用二进制来进行保存的. 第一位用于保存符号位. 如果不考虑这个符号位,那么所有的数都是按照正数来表示. 比如考虑了符号位的 char 类型: 考虑符号表示范围:-128 ~ 127 ...