专栏首页林德熙的博客WPF 列表控件数据源绑定多个数据集合方法

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 代码看起来简单

本文所有代码放在 githubgitee 欢迎小伙伴访问

参考

本文以上方法参考了如下博客

c# - CompositeCollection + CollectionContainer: Bind CollectionContainer.Collection to property of ViewModel that is used as DataTemplates DataType - Stack Overflow

wpf - How do you bind a CollectionContainer to a collection in a view model? - Stack Overflow

WPF 很少人知道的科技 - walterlv


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/WPF-%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E6%95%B0%E6%8D%AE%E6%BA%90%E7%BB%91%E5%AE%9A%E5%A4%9A%E4%B8%AA%E6%95%B0%E6%8D%AE%E9%9B%86%E5%90%88%E6%96%B9%E6%B3%95.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

无盈利,不卖课,做纯粹的技术博客

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于webform中控件的列表数据的绑定

    静心物语313
  • .Net5 WPF快速入门系列教程

    在工作中大家会遇到需要学习新的技术或者临时被抽调到新的项目当中进行开发。通常这样的情况比较紧急没有那么多的时间去看书学习。所以这里向wpf技术栈的开发者分享一套...

    zls365
  • dotnet 从入门到放弃的 500 篇文章合集

    博客包括 C#、WPF、UWP、dotnet core 、git 和 VisualStudio 和一些算法,所有博客使用 docx 保存

    林德熙
  • ObservableCollection 类

    Windows Presentation Foundation (WPF) 在 Microsoft .NET Framework 中添加了一些功能,您可以可靠地...

    张善友
  • 《深入浅出WPF》学习笔记之深入浅出话Binding

      如果把Binding比作数据的桥梁,那么它的两端分别是Binding的源(Source)和目标(Target)。一般源是逻辑层对象,目标是UI层控件对象.

    zls365
  • “老坛泡新菜”:SOD MVVM框架,让WinForms焕发新春

    火热的MVVM框架 最近几年最热门的技术之一就是前端技术了,各种前端框架,前端标准和前端设计风格层出不穷,而在众多前端框架中具有MVC,MVVM功能的框架成为耀...

    用户1177503
  • 【我们一起写框架】MVVM的WPF框架(五)—完结篇

    框架设计不应该局限于任何一种设计模式,我们在设计框架时,应该将设计模式揉碎,再重组;这样设计出来的框架才具有一战之力。

    Kiba518
  • WPF架构学习总结

    预期读者     1. 初学者。     2. 懒得总结的人。:)     3. 想大致了解WPF框架主要类的功能的人。 前言     学习WPF也有段时间了,...

    用户1172223
  • 【译】Visual Studio 2019 中 WPF & UWP 的 XAML 开发工具新特性

    自Visual Studio 2019推出以来,我们为使用WPF或UWP桌面应用程序的XAML开发人员发布了许多新功能。在本周的 Visual Studio 2...

    郑子铭

扫码关注云+社区

领取腾讯云代金券