c# - 单个集合上的多个 ItemControl 同时将过滤器应用于所有 View

标签 c# wpf xaml listview

先决条件:.NET 4.5.1

我有三个TreeView显示单个集合实例的三个过滤变体的控件。当我尝试对 Items 应用过滤器时此过滤器自动传播到其他控件的控件之一的集合,这阻止我在不同的控件上使用不同的过滤器。

有什么方法可以实现相同的结果,而不必同时维护三个集合实例?

下面是一个显示问题的示例。前两个 ListView 直接绑定(bind)到同一个集合实例。第三个通过 CompositeCollection 绑定(bind)到该实例。 。第四是绑定(bind)独立采集。当我按下“设置过滤器”按钮时ItemsControl.Items.Filter属性如果首先 ListView设置为IsAllowedItem WTest 窗口的方法。在这一秒之后istView.Items.Filter属性以某种方式指向相同的方法,而第三个和第四个 ListView 返回 null。另一个影响是,虽然第三个 ListView显示空过滤器,它的集合仍然被过滤,如果您运行该示例,您可以看到。这种非常奇怪的效果是由 ItemCollection 类基于 ItemsSource 时的行为引起的。所有者元素的属性获取底层 CollectionView通过CollectionViewSource.GetDefaultCollectionView从一些应用程序范围的存储中方法。我不知道这个实现的原因,但怀疑它的性能。

测试窗口WTest.xaml:

<Window x:Class="Local.WTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:s="clr-namespace:System;assembly=mscorlib"
        xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
        xmlns:local="clr-namespace:Local"
        Name="_WTest" Title="WTest" Height="300" Width="600">
    <Window.Resources>
        <c:ArrayList x:Key="MyArray">
            <s:String>Letter A</s:String>
            <s:String>Letter B</s:String>
            <s:String>Letter C</s:String>
        </c:ArrayList>
        <CompositeCollection x:Key="MyCollection" >
            <CollectionContainer Collection="{StaticResource ResourceKey=MyArray}"/>
        </CompositeCollection>
        <c:ArrayList x:Key="AnotherArray">
            <s:String>Letter A</s:String>
            <s:String>Letter B</s:String>
            <s:String>Letter C</s:String>
        </c:ArrayList>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Name="FilterLabel1"/>
        <TextBlock Grid.Row="0" Grid.Column="1" Name="FilterLabel2"/>
        <TextBlock Grid.Row="0" Grid.Column="2" Name="FilterLabel3"/>
        <TextBlock Grid.Row="0" Grid.Column="3" Name="FilterLabel4"/>
        <ListView Grid.Row="1" Grid.Column="0" Name="View1" ItemsSource="{StaticResource ResourceKey=MyArray}"/>
        <ListView Grid.Row="1" Grid.Column="1" Name="View2" ItemsSource="{StaticResource ResourceKey=MyArray}"/>
        <ListView Grid.Row="1" Grid.Column="2" Name="View3" ItemsSource="{StaticResource ResourceKey=MyCollection}"/>
        <ListView Grid.Row="1" Grid.Column="3" Name="View4" ItemsSource="{StaticResource ResourceKey=AnotherArray}"/>
        <Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Content="Set Filter" Click="OnSetFilterButtonClick"/>
    </Grid>
</Window>

WTest.xaml.cs 背后的代码

namespace Local
{
    using System.Windows;

    public partial class WTest : Window
    {
        public WTest()
        {
            InitializeComponent();
            UpdateFilterLabels();
        }

        private bool IsAllowedItem(object item)
        {
            return "Letter A" == (string)item;
        }

        private void OnSetFilterButtonClick(object sender, RoutedEventArgs e)
        {
            View1.Items.Filter = IsAllowedItem;
            UpdateFilterLabels();
        }

        private void UpdateFilterLabels()
        {
            FilterLabel1.Text = (null == View1.Items.Filter) ? "No Filter" : View1.Items.Filter.Method.Name;
            FilterLabel2.Text = (null == View2.Items.Filter) ? "No Filter" : View2.Items.Filter.Method.Name;
            FilterLabel3.Text = (null == View3.Items.Filter) ? "No Filter" : View3.Items.Filter.Method.Name;
            FilterLabel4.Text = (null == View4.Items.Filter) ? "No Filter" : View4.Items.Filter.Method.Name;
        }
    }
}

单击“设置过滤器”按钮后的结果: Example: result of clicking "Set Filter" button

最佳答案

  1. 创建 CollectionViewSource 作为资源

    <CollectionViewSource x:Key="CVSKey" Source="{DynamicResource MyArray}"/>
    
  2. 使用此 CollectionViewSource 作为您的 ItemsSource 。将您的 View1 替换为:

    <!--<ListView Grid.Row="1" Grid.Column="0" Name="View1" ItemsSource="{DynamicResource ResourceKey=MyArray}"/>-->
    <ListView Grid.Row="1" Grid.Column="0" Name="View1" ItemsSource="{Binding Source={StaticResource ResourceKey=CVSKey}}"/>
    

就是这样,现在一切都会按照你想要的方式进行。

此外,现在您可以对此CollectionViewSource而不是View1应用过滤:

((CollectionViewSource)this.Resources["CVSKey"]).Filter += List_Filter;

void List_Filter(object sender, FilterEventArgs e)
{
    e.Accepted = (e.Item.ToString() == "Letter A") ? true : false;
}

为单独的ListBox创建单独的CollectionViewSource,以从同一底层集合创建单独的 View 。

在 google 中搜索 CollectionViewSource

关于c# - 单个集合上的多个 ItemControl 同时将过滤器应用于所有 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38542781/

相关文章:

c# - 枚举应该以 0 还是 1 开头?

c# - 创建不可见的用户控件

c# - 是否在可扩展的客户端-服务器应用程序上使用多线程

wpf - 类似终端的 WPF 文本框?

wpf - 是否可以强制窗口的大小永远不会低于其子项所需的大小?

c# - 带有源代码的 UWP 社区工具包

c# - 不返回值的sql中的存储过程

c# - 主窗口关闭时如何关闭所有窗口

wpf - 如何将 xaml 中的绑定(bind)设置为我自己的变量

c# - Windows 应用商店应用程序用户界面更新