根据同一 ListView 中的所选项目 属性 过滤 ICollectionView - MVVM

Filter a ICollectionView based on the selected items Property in the same ListView - MVVM

我似乎无法找到解决方案。感谢任何帮助。

编辑:解决方案是通过@BionicCode 提供的过滤器逻辑组合实现的,在 XMAL 中每个 and adding a simple bool Converter er for visibility. I have also discovered that Items Control would also work for this situation and allow greater control per .

实现了一个 ListView selection

我有一个 ListView:

编辑:不幸的是,一个还不够。两个 ListViews 将起作用。请注意添加的命名空间 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"。此外,我添加了一个“后退”按钮以允许用户返回到 select 不同的类别。

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"  
        xmlns:vm="clr-namespace:WpfApp1.ViewModels" 
        Title="MainWindow" Height="300" Width="500" WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="15"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid Grid.Column="0" Grid.Row="1">
            <Button x:Name="BackBtn" Content="Back" Command="{Binding BacktoCatView}"/>
        </Grid>

        <Grid Grid.Column="1" Grid.Row="1"  >
            <ListView x:Name="CategoryListView" ItemsSource="{Binding CategoryView}" SelectedItem="{Binding SelectedCategory}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Category}"/>
                    </DataTemplate>
                </ListView.ItemTemplate>
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding SelectedCategoryCommand}" CommandParameter="{Binding SelectedItem, ElementName=CategoryListView}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </ListView>
        </Grid>

        <Grid Grid.Column="1" Grid.Row="1" Visibility="{Binding IsBrandView, Converter={StaticResource VisibilityConverter}}">
            <ListView ItemsSource="{Binding BrandView}">
                <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Brand}"/>
                </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
    </Grid> </Window>

我有一个模型:

public class Car : NotifyPropertyChanged
{
    private string _brand;
    public string Brand
    {
        get => _brand;
        set
        {
            _brand = value;
            OnPropertyChanged();
        }
    }

    private string _category;
    public string Category
    {
        get => _category;
        set
        {
            _category = value;
            OnPropertyChanged();
        }
    }
}

ListView 绑定到名为“CategoryView”的 ICollectionView,它基于 MainViewModel 中的 ObservableCollection“CarsCollection”。

编辑:每个 Listview 都绑定到它各自的 ICollectionView,唉,“CategoryView”和“BrandView”。 SelectedCategory 绑定到“CategoryView”中的 SelectedItem。

MainViewModel:

 public class MainViewModel : NotifyPropertyChanged
        {
            private ObservableCollection<Car> _carsCollection;
            public ObservableCollection<Car> CarsCollection
            {
                get { return _carsCollection; }
                set
                {
                    _carsCollection = value;
                    OnPropertyChanged();
                }
            }
    
            private ICollectionView _categoryView;
            public ICollectionView CategoryView
            {
                get { return _categoryView; }
                set
                {
                    _categoryView = value;
                    OnPropertyChanged();
                }
            }
    
            private ICollectionView _brandView;
            public ICollectionView BrandView
            {
                get { return _brandView; }
                set
                {
                    _brandView = value;
                    OnPropertyChanged();
                }
            }
    
            private Car _selectedCategory;
            public Car SelectedCategory
            {
                get { return _selectedCategory; }
                set
                {
                    _selectedCategory = value;
                    OnPropertyChanged();
                }
            }
            public ICommand BacktoCatView { get; private set; }
            public ICommand SelectedCategoryCommand { get; private set; }
    
            
            private bool _isBrandView;
            public bool IsBrandView
            {
                get { return _isBrandView; }
                set
                {
                    _isBrandView = value;
                    OnPropertyChanged();
                }
            }
    
            public MainViewModel()
            {
                CarsCollection = new ObservableCollection<Car>
                    {
                        new Car { Brand = "Chevy", Category = "Sedan" },
                        new Car { Brand = "Mazda", Category = "Sports Car" },
                        new Car { Brand = "Toyota", Category = "Sedan" },
                        new Car { Brand = "Honda", Category = "Sports Car" },
                        new Car { Brand = "Volkswagon", Category = "Sedan" },
                        new Car { Brand = "Tesla", Category = "Sedan" },
                        new Car { Brand = "Dodge", Category = "Sports Car" },
                        new Car { Brand = "Jeep", Category = "Off Road" },
                    };
    
                BacktoCatView = new RelayCommand(ShowCategoryView);
                SelectedCategoryCommand = new RelayCommand(FilterbyBrand);
                //Filter by Category on app start
                CategoryView = CollectionViewSource.GetDefaultView(CarsCollection);
                CategoryView.Filter = item => !IsDuplicate((IEnumerable<Car>)CategoryView.SourceCollection, (Car)item);
                //Setup BrandView for filtering
                BrandView = (CollectionView)new CollectionViewSource { Source = CarsCollection }.View;
    
                OnPropertyChanged("CategoryView");
                OnPropertyChanged("BrandView");
            }
    
            static bool IsDuplicate(IEnumerable<Car> collection, Car target)
            {
                foreach (var item in collection)
                {
                    // NOTE: Check only the items BEFORE the one in question
                    if (ReferenceEquals(item, target)) break;
                    // If more than one Category is present, only show one instance of it
                    if (item.Category == target.Category) return true;
                }
                return false;
            }
    
            private void ShowCategoryView()
            {
                IsBrandView = false;
            }
    
            public void FilterbyBrand()
            {
                IsBrandView = true;
                BrandView.Filter = item => (item as Car).Category.Equals(_selectedCategory.Category, StringComparison.OrdinalIgnoreCase);
                OnPropertyChanged("BrandView");
            }
        }
    }  

编辑:将 IValueConverter 添加到名为“Helpers”的新命名空间中名为“Converters”的 class 以处理可见性。我还在 Utility 命名空间中添加了一个 RelayCommand class 来简化 ICommands 的使用。

namespace WpfApp1.Helpers
{
    public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var boolValue = (bool)value;

            if (boolValue)
                return Visibility.Visible;

            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

第一个过滤器出现在构造函数中:

    // Filter by Category
    CategoryView = CollectionViewSource.GetDefaultView(CarsCollection);
    CategoryView.Filter = item => !IsDuplicate((IEnumerable<Car>)CategoryView.SourceCollection, (Car)item);

它使用以下内容删除 Category 的重复实例:

static bool IsDuplicate(IEnumerable<Car> collection, Car target)
    {
        foreach (var item in collection)
        {
            // NOTE: Check only the items BEFORE the one in question
            if (ReferenceEquals(item, target)) break;
            // If more than one Category is present, only show one instance of it
            if (item.Category == target.Category) return true;
        }
        return false;
    }

工作完美,并在 CategoryListView 中生成以下内容:

Sedan
Sports Car
Off Road

挑战:

当用户 select 从 ListView 中选择“类别”时,我希望接下来对 IListCollection 应用第二个过滤器来过滤 ListView,以便“类别”中的每个“品牌” " 用户已 selected 显示在 ListView 中。

编辑:现在,当用户 select 属于一个类别时,他们会看到该特定 CategoryBrand 列表。过滤对用户是无缝发生的。最重要的是,这为用户提供了应用程序启动时可用类别的即时视图,并且不需要用户在组合框中找到所需的类别。

非常感谢@BionicCode 在过滤逻辑方面的帮助。

品牌过滤器和 IsBrandView 布尔值:

private void ShowCategoryView()
{
    IsBrandView = false;
}

public void FilterbyBrand()
{
    IsBrandView = true;
    BrandView.Filter = item => (item as Car).Category.Equals(_selectedCategory.Category, StringComparison.OrdinalIgnoreCase);
    OnPropertyChanged("BrandView");
}

我通过@BionicCode 提供的过滤器逻辑组合解决了这个问题,根据这个 post 在 XMAL 中实现了一个 ListView 选择,并添加了一个简单的 bool Converter er 以提高可见性。

原post已添加解决方案。