代码之家  ›  专栏  ›  技术社区  ›  erotavlas

如何根据项的子属性筛选ICollectionView?

  •  0
  • erotavlas  · 技术社区  · 6 年前

    我正试图按集合中项目(处理器对象)的子属性进行筛选,但无法。属性是一个简单的布尔值,用于指示项是启用还是禁用。列表中的已启用项应列在第一个选项卡(已启用处理器)上的其他列表框中。

    下面是一个完整的工作示例,可以粘贴到starter wpf应用程序中

    如果您取消注释此行

    _selectedProcessorsView.Filter = processorFilter;
    

    在mainwindow_vm()构造函数中,配置选项卡中不再显示处理器。

    两个主要问题:

    1. 为什么过滤会影响两个列表?
    2. 如何修复此问题,以便第一个选项卡上的“已启用处理器”列表框仅绑定到第二个选项卡中的选中项?

    注: 代码末尾的三个类(config、settings和processor)是类库的一部分,我无法修改。wpf应用程序只是那个库周围的一个ui。

    主窗口.xaml

    <Window x:Class="WpfApplication1.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:WpfApplication1"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.DataContext>
            <local:MainWindow_VM></local:MainWindow_VM>
        </Window.DataContext>
    
        <Grid>
            <TabControl>
                <TabItem Header="enabled processors">
                    <ListBox   ItemsSource="{Binding selectedProcessorsView}"   VerticalAlignment="Top" FontSize="14">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=processorType}"></TextBlock>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </TabItem>
                <TabItem Header="configuration">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <ListView Grid.Column="0" ItemsSource="{Binding Path=reportTypes}"   SelectedItem="{Binding selectedConfiguration}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <TextBlock Text="{Binding Path=id, Mode=OneWay}"></TextBlock>
                                    </StackPanel>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                        <ListView Grid.Column="1" ItemsSource="{Binding reportProcessors}"  SelectedItem="{Binding selectedProcessor}">
                            <ListView.View>
                                <GridView>
                                    <GridViewColumn Header="Enabled"  >
                                        <GridViewColumn.CellTemplate>
                                            <DataTemplate>
                                                <CheckBox IsChecked="{Binding Path=Settings.isEnabled}" ></CheckBox>
                                            </DataTemplate>
                                        </GridViewColumn.CellTemplate>
                                    </GridViewColumn>
                                    <GridViewColumn Header="Processor"  DisplayMemberBinding="{Binding Path=processorType, Mode=OneWay}" />
                                    <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Path=processorId, Mode=OneWay}"   >
                                    </GridViewColumn>
                                </GridView>
                            </ListView.View>
                        </ListView>
                    </Grid>
                </TabItem>
            </TabControl>
    
        </Grid>
    </Window>
    

    主窗口.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApplication1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
        public abstract class BindableBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
            {
                if (object.Equals(storage, value)) return false;
    
                storage = value;
                this.OnPropertyChanged(propertyName);
                return true;
            }
    
            protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                var eventHandler = this.PropertyChanged;
                if (eventHandler != null)
                {
                    eventHandler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    
        public class MainWindow_VM : BindableBase
        {
            public MainWindow_VM()
            {
                reportTypes.Add(new Config()
                {
                    id = Guid.NewGuid().ToString(),
                    Processors = new Dictionary<string, Processor>() {
                        { "Processor 1", new Processor() { processorType = "Blue" } },
                        { "Processor 2", new Processor() { processorType = "Yellow" } }
                    }
                });
    
                reportTypes.Add(new Config()
                {
                    id = Guid.NewGuid().ToString(),
                    Processors = new Dictionary<string, Processor>() {
                        { "Processor 3", new Processor() { processorType = "Green" } },
                        { "Processor 4", new Processor() { processorType = "Red" } }
                    }
                });
    
                _selectedProcessorsView = CollectionViewSource.GetDefaultView(reportProcessors);
                //_selectedProcessorsView.Filter = processorFilter;
            }
    
            public bool processorFilter(object item)
            {
                bool result = true;
                Processor p = item as Processor;
    
                if (p.Settings.isEnabled == false)
                    result = false;
    
                return result;
            }
    
            private Config _selectedConfiguration;
            public Config selectedConfiguration
            {
                get { return _selectedConfiguration; }
                set
                {
                    SetProperty(ref _selectedConfiguration, value);
    
                    reportProcessors.Clear();
    
                    foreach (KeyValuePair<string, Processor> kvp in value.Processors)
                        reportProcessors.Add(kvp.Value);
                }
            }
    
            public ObservableCollection<Config> reportTypes { get; } = new ObservableCollection<Config>();
    
            public ObservableCollection<Processor> reportProcessors { get; } = new ObservableCollection<Processor>();
    
            private ICollectionView _selectedProcessorsView;
            public ICollectionView selectedProcessorsView
            {
                get { return _selectedProcessorsView; }
            }
        }
    
    ///These three classes below are part of a separate class library that I cannot modify
    
        public class Config
        {
            public string id { get; set; }
            public Dictionary<string, Processor> Processors { get; set; } 
        }
        public class Processor
        {
            public string processorType { get; set; }
            public Settings Settings { get; set; } = new Settings() {
                isEnabled = false
            };
        }
        public class Settings
        {
            public bool isEnabled { get; set; }
        }
    }
    
    2 回复  |  直到 6 年前
        1
  •  0
  •   mm8    6 年前

    为什么过滤会影响两个列表?

    {Binding reportProcessors} {Binding selectedProcessorsView} 绑定到同一视图。当你绑定到 ObservableCollection<T> ,或任何其他类型的源集合,实际上是绑定到自动生成的 CollectionView 而不是实际的源集合本身。

    如何修复此问题,以便第一个选项卡上的“已启用处理器”列表框仅绑定到第二个选项卡中的选中项?

    绑定到两个不同的视图。但您还需要在 isEnabled 属性实际上被设置为一个新值。否则在检查 CheckBoxes . 事实上你正在使用 Setting 类使这稍微复杂一点,但您可以将属性添加到 Processor 对这个进行类和实时筛选。两者 Settings 处理器 需要实施 INotifyPropertyChanged 事件

    请参考以下示例代码。它应该给你一个主意。

    public class MainWindow_VM : BindableBase
    {
        public MainWindow_VM()
        {
            ...
    
            ListCollectionView selectedProcessorsView = new ListCollectionView(reportProcessors);
            selectedProcessorsView.LiveFilteringProperties.Add(nameof(Processor.IsEnabled));
            selectedProcessorsView.IsLiveFiltering = true;
            selectedProcessorsView.Filter = processorFilter;
    
            _selectedProcessorsView = selectedProcessorsView;
        }
        ...
     }
    
    public class Processor : INotifyPropertyChanged
    {
        public string processorType { get; set; }
    
        public Settings Settings { get; set; } = new Settings()
        {
            isEnabled = false
        };
    
        public bool IsEnabled => Settings.isEnabled;
    
        public Processor()
        {
            Settings.PropertyChanged += Settings_PropertyChanged;
        }
    
        private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            OnPropertyChanged(nameof(IsEnabled));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    public class Settings : INotifyPropertyChanged
    {
        private bool _isEnabled;
        public bool isEnabled
        {
            get { return _isEnabled; }
            set { _isEnabled = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
        2
  •  0
  •   erotavlas    6 年前

    最后,我采用了以下方法,将mm8对我的第一个问题的回答与我自己的包装器类结合起来,它充当处理器对象(模型)的viewmodel。

    无论何时选中或取消选中复选框,都会刷新ListViewCollection,从而更新第一个选项卡中显示的集合。

    主窗口.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApplication1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
        public abstract class BindableBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
            {
                if (object.Equals(storage, value)) return false;
    
                storage = value;
                this.OnPropertyChanged(propertyName);
                return true;
            }
    
            protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                var eventHandler = this.PropertyChanged;
                if (eventHandler != null)
                {
                    eventHandler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    
        public class MainWindow_VM : BindableBase
        {
            public MainWindow_VM()
            {
                reportTypes.Add(new Config()
                {
                    id = Guid.NewGuid().ToString(),
                    Processors = new Dictionary<string, Processor>() {
                        { "Processor 1", new Processor() { processorType = "Blue" } },
                        { "Processor 2", new Processor() { processorType = "Yellow" } }
                    }
                });
    
                reportTypes.Add(new Config()
                {
                    id = Guid.NewGuid().ToString(),
                    Processors = new Dictionary<string, Processor>() {
                        { "Processor 3", new Processor() { processorType = "Green" } },
                        { "Processor 4", new Processor() { processorType = "Red" } }
                    }
                });
    
                selectedProcessorsView = new ListCollectionView(processors);
                selectedProcessorsView.Filter = processorFilter;
            }
    
            public bool processorFilter(object item)
            {
                bool result = true;
                ProcessorWrapper p = item as ProcessorWrapper;
    
                if (p.isEnabled == false)
                    result = false;
    
                return result;
            }
    
            private Config _selectedConfiguration;
            public Config selectedConfiguration
            {
                get { return _selectedConfiguration; }
                set
                {
                    SetProperty(ref _selectedConfiguration, value);
    
                    processors.Clear();
    
                    foreach (KeyValuePair<string, Processor> kvp in value.Processors)
                        processors.Add(new ProcessorWrapper(kvp.Value, this.selectedProcessorsView));
                }
            }
    
            public ObservableCollection<Config> reportTypes { get; } = new ObservableCollection<Config>();
    
            public ObservableCollection<ProcessorWrapper> processors { get; } = new ObservableCollection<ProcessorWrapper>();
    
            public ListCollectionView selectedProcessorsView { get; set; }
        }
    
        public class ProcessorWrapper : BindableBase
        {
            public ProcessorWrapper(Processor processor, ListCollectionView lcv)
            {
                _processor = processor;
                _lcv = lcv;
            }
    
            private Processor _processor;
            private ListCollectionView _lcv;
    
            private string _processorType;
            public string processorType
            {
                get {
    
                    _processorType = _processor.processorType;
                    return _processorType;
                }
            }
    
            private bool _isEnabled;
            public bool isEnabled
            {
                get {
                    _isEnabled = this._processor.Settings.isEnabled;
                    return _isEnabled; }
                set
                {
                    SetProperty(ref _isEnabled, value);
                    this._processor.Settings.isEnabled = _isEnabled;
                    _lcv.Refresh();
                }
            }
        }
    
    
        /// <summary>
        /// These classes belong to a separate class library, should not be modified.  
        /// </summary>
    
        public class Config
        {
            public string id { get; set; }
            public Dictionary<string, Processor> Processors { get; set; } 
        }
        public class Processor
        {
            public string processorType { get; set; }
            public Settings Settings { get; set; } = new Settings() {
                isEnabled = false
            };
        }
        public class Settings
        {
            public bool isEnabled { get; set; }
        }
    }
    

    主窗口.xaml

    <Window x:Class="WpfApplication1.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:WpfApplication1"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.DataContext>
            <local:MainWindow_VM></local:MainWindow_VM>
        </Window.DataContext>
    
        <Grid>
            <TabControl>
                <TabItem Header="enabled processors">
                    <ListBox ItemsSource="{Binding selectedProcessorsView}" VerticalAlignment="Top" FontSize="14">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=processorType}"></TextBlock>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </TabItem>
                <TabItem Header="configuration">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <ListView Grid.Column="0" ItemsSource="{Binding Path=reportTypes}"   SelectedItem="{Binding selectedConfiguration}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <TextBlock Text="{Binding Path=id, Mode=OneWay}"></TextBlock>
                                    </StackPanel>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                        <ListView Grid.Column="1" ItemsSource="{Binding processors}"  SelectedItem="{Binding selectedProcessor}">
                            <ListView.View>
                                <GridView>
                                    <GridViewColumn Header="Enabled"  >
                                        <GridViewColumn.CellTemplate>
                                            <DataTemplate>
                                                <CheckBox IsChecked="{Binding Path=isEnabled}" />
                                            </DataTemplate>
                                        </GridViewColumn.CellTemplate>
                                    </GridViewColumn>
                                    <GridViewColumn Header="Processor"  DisplayMemberBinding="{Binding Path=processorType, Mode=OneWay}" />
                                </GridView>
                            </ListView.View>
                        </ListView>
                    </Grid>
                </TabItem>
            </TabControl>
        </Grid>
    </Window>