使用ICollectionView.Filter對ObservableCollection篩選

前提:以前對於ObservableCollection的篩選都是在viewmodel裏面添加一個額外屬性(例如Visibility或者bool來控制元素的顯示隱藏)。
下面例子是用MVVMlight框架的,先上xaml代碼

<Window
        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"
        x:Class="WpfApp.MainWindow"
        mc:Ignorable="d" DataContext="{Binding Main,Source={StaticResource Locator}}"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Style TargetType="DataGridRow">
            <Setter Property="Visibility" Value="{Binding Visibility}"></Setter>
            <Style.Triggers>
                <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                    <Setter Property="Background" Value="Transparent" />
                </Trigger>
                <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                    <Setter Property="Background" Value="#F8F8F8" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel>
        <DataGrid ItemsSource="{Binding Students}">
            <DataGrid.Columns>
                <DataGridTextColumn Width="*" ElementStyle="{StaticResource CenterAlignmentStyle}" Header="ID" Binding="{Binding ID}"></DataGridTextColumn>
                <DataGridTextColumn Width="*" ElementStyle="{StaticResource CenterAlignmentStyle}" Header="姓名" Binding="{Binding Name}"></DataGridTextColumn>
                <DataGridTextColumn Width="*" ElementStyle="{StaticResource CenterAlignmentStyle}" Header="年齡" Binding="{Binding Age}"></DataGridTextColumn>
                <DataGridTextColumn Width="*" ElementStyle="{StaticResource CenterAlignmentStyle}" Header="班級" Binding="{Binding ClsName}"></DataGridTextColumn>
                <DataGridTextColumn Width="*" ElementStyle="{StaticResource CenterAlignmentStyle}" Header="學校" Binding="{Binding SchoolName}"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
        <WrapPanel>
            <Label Content="姓名:" VerticalContentAlignment="Center"></Label>
            <TextBox Width="100" x:Name="TbName" VerticalContentAlignment="Center"></TextBox>
            <Button Content="篩選" Height="30" Width="100" Command="{Binding FilterCommand}" CommandParameter="{Binding ElementName=TbName,Path=Text}"></Button>
            <Button Content="重置" Height="30" Width="100" Command="{Binding ClearFilterCommand}"></Button>
        </WrapPanel>
    </StackPanel>

</Window>

以前思路的viewmodel代碼在

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace WpfApp.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        public ObservableCollection<Student> Students { get; set; }

        public ICommand FilterCommand { get; private set; }

        public ICommand ClearFilterCommand { get; private set; }

        public MainViewModel()
        {
            Students = new ObservableCollection<Student>() {
                    new Student(){ ID  = 1,Name ="小王",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 2,Name ="小張",Age = 12 , ClsName ="五班",SchoolName ="北大"},
                    new Student(){ ID  = 3,Name ="小韓",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 4,Name ="小李",Age = 15 , ClsName ="二班",SchoolName ="哈佛"},
                    new Student(){ ID  = 5,Name ="小朱",Age = 13 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 6,Name ="小桂",Age = 12 , ClsName ="一班",SchoolName ="湖大"},
                    new Student(){ ID  = 7,Name ="小肖",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 8,Name ="小錢",Age = 11 , ClsName ="三班",SchoolName ="交大"},
                    new Student(){ ID  = 9,Name ="小孫",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 10,Name ="小宋",Age = 12 , ClsName ="四班",SchoolName ="清華"},
               };

            FilterCommand = new RelayCommand<string>((name) =>
            {
                foreach (var student in Students)
                {
                    student.Visibility = student.Name == name ? Visibility.Visible : Visibility.Collapsed;
                }
            });

            ClearFilterCommand = new RelayCommand(() =>
            {
                foreach (var student in Students)
                {
                    student.Visibility = Visibility.Visible;
                }
            });
        }
    }

    public class Student : ViewModelBase
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public string ClsName { get; set; }

        public string SchoolName { get; set; }

        private Visibility _visibility = Visibility.Visible;
        public Visibility Visibility { get { return _visibility; } set { Set(ref _visibility, value); } }

    }
}

當然 也可以定義一個bool屬性,然後用轉換器轉爲Visibility ;
使用ICollectionView後 代碼:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace WpfApp.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        public ObservableCollection<Student> Students { get; set; }

        public ICommand FilterCommand { get; private set; }

        public ICommand ClearFilterCommand { get; private set; }

        private ICollectionView collectionView
        {
            get
            {
                return CollectionViewSource.GetDefaultView(Students);
            }
        }

        public MainViewModel()
        {
            Students = new ObservableCollection<Student>() {
                    new Student(){ ID  = 1,Name ="小王",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 2,Name ="小張",Age = 12 , ClsName ="五班",SchoolName ="北大"},
                    new Student(){ ID  = 3,Name ="小韓",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 4,Name ="小李",Age = 15 , ClsName ="二班",SchoolName ="哈佛"},
                    new Student(){ ID  = 5,Name ="小朱",Age = 13 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 6,Name ="小桂",Age = 12 , ClsName ="一班",SchoolName ="湖大"},
                    new Student(){ ID  = 7,Name ="小肖",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 8,Name ="小錢",Age = 11 , ClsName ="三班",SchoolName ="交大"},
                    new Student(){ ID  = 9,Name ="小孫",Age = 12 , ClsName ="二班",SchoolName ="清華"},
                    new Student(){ ID  = 10,Name ="小宋",Age = 12 , ClsName ="四班",SchoolName ="清華"},
               };

            FilterCommand = new RelayCommand<string>((name) =>
            {
                collectionView.Filter = x => x is Student stu && stu.Name == name;
            });

            ClearFilterCommand = new RelayCommand(() =>
            {
				collectionView.Filter = x => true;
            });
        }
    }

    public class Student
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public string ClsName { get; set; }

        public string SchoolName { get; set; }
    }
}

看下前後對比就知道了,基礎Model不用繼承ViewModel了,因爲不用修改屬性來更改UI了。
前後代碼運行結果都是一樣的,本文只是做一個簡單實驗,寫的不好,多見諒!
附贈一個擴展方法,這樣就可以把過濾條件封裝起來啦:

原先的:
	collectionView.Filter = x => x is Student stu && stu.Name == name;
新的:
	collectionView.Filter = predicate(name).ToObject();
    public static Predicate<object> ToObject<TFrom>(this Predicate<TFrom> predicate) {
         return x => x is TFrom from && predicate.Invoke(from);
    }
    private Predicate<Student> predicate(string name) {
   		return x => x.Name == name;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章