6

我一直在尝试扩展 Josh Smith 的演示 MVVM 应用程序,以便更好地理解其背后的原理,并且在尝试使用 ListView 在 View 上实现过滤器功能时遇到了困难。

我花了几个小时研究和涉足,但它只是不起作用。

我的第一步是将视图中的文本框绑定到 ViewModel 中的属性:

<TextBox Height="25" Name="txtFilter" Width="150" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}"/>

这在我的虚拟机中匹配:

public string Filter
    {
        get { return this.filter; }
        set
        {
            this.filter = value;
            OnFilterChanged();
        }
    }

我的虚拟机使用 ObservableCollection 作为数据源,但我在阅读教程后尝试将其转换为 ICollectionView:

internal ObservableCollection<StaffViewModel> InnerStaff { get; set; }
    internal CollectionViewSource CvsStaff { get; set; }
    public ICollectionView AllStaff
    {
        get { return CvsStaff.View; }
    }

在我的构造函数中,我指定了:

CvsStaff = new CollectionViewSource();
CvsStaff.Source = this.InnerStaff;
CvsStaff.Filter += ApplyFilter;

当我的过滤器属性更新时,它会调用 OnFilterChanged,即:

private void OnFilterChanged()
    {
        CvsStaff.View.Refresh();
    }

我的 ApplyFilter 功能是:

void ApplyFilter(object sender, FilterEventArgs e)
    {
        StaffViewModel svm = (StaffViewModel)e.Item;

        if (this.Filter.Length == 0)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = svm.LastName.Contains(Filter);
        }
    }

我是否犯了一个愚蠢的错误,任何人都可以帮助我发现?我对 WPF 和 MVVM 模式还很陌生,所以我还在学习!

编辑

在视图中,我将集合绑定到:

<CollectionViewSource
  x:Key="StaffGroup"
  Source="{Binding Path=AllStaff}"
  />

ListView 是这样的:

<ListView
      Name="staffList"
      AlternationCount="2" 
      DataContext="{StaticResource StaffGroup}" 
      ItemContainerStyle="{StaticResource StaffItemStyle}"
      ItemsSource="{Binding}"
        Grid.Row="1">
4

1 回答 1

17

绑定不正确。您需要进行一些更改。首先要确保 DataContext 设置正确。通常,您将在 ListView 的父级上执行此操作,而不是直接在 ListView 控件上设置它。这可能是 UserControl / Window / 等。

所以假设你有一个视图模型:

public class MainViewModel
{
    public MainViewModel()
    {
        //Create some fake data 
        InnerStaff = new ObservableCollection<StaffViewModel>();
        InnerStaff.Add(new StaffViewModel {FirstName = "Sue", LastName = "Bucknell"});
        InnerStaff.Add(new StaffViewModel {FirstName = "James", LastName = "Bucknell"});
        InnerStaff.Add(new StaffViewModel {FirstName = "John", LastName = "Harrod"});

        CvsStaff = new CollectionViewSource();
        CvsStaff.Source = this.InnerStaff;
        CvsStaff.Filter += ApplyFilter;
    }

    private string filter;

    public string Filter
    {
        get { return this.filter; }
        set
        {
            this.filter = value;
            OnFilterChanged();
        }
    }

    private void OnFilterChanged()
    {
        CvsStaff.View.Refresh();
    }

    internal ObservableCollection<StaffViewModel> InnerStaff { get; set; }
    internal CollectionViewSource CvsStaff { get; set; }
    public ICollectionView AllStaff
    {
        get { return CvsStaff.View; }
    }

    void ApplyFilter(object sender, FilterEventArgs e)
    {
        StaffViewModel svm = (StaffViewModel)e.Item;

        if (string.IsNullOrWhiteSpace(this.Filter) || this.Filter.Length == 0)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = svm.LastName.Contains(Filter);
        }
    }
}

并且假设您有一个 Window MainWindow.cs(后面的代码),您可以(对于此示例)在此处连接 DataContext。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

然后,您有几个选择来进行绑定,您可以在 XAML 或代码中指定您的 CollectionViewSource,但您已经完成了这两种操作。即 xaml 之一,键 x:key="StaffGroup" 和 VM 一个 CvsStaff。假设我们完全摆脱了 xaml 并使用正确设置的 VM 。然后您将使用 ItemsSource 属性进行绑定,如下所示:

<ListView Name="staffList" 
      AlternationCount="2" 
      ItemsSource="{Binding AllStaff}" 
      Grid.Row="1" />

还有一件小事,我更改了过滤器以检查空值和空格。您可能还需要将其更改为不区分大小写。

我在这里没有提到但至关重要的另一件事是在您的 StaffViewModel 上实现 INotifyPropertyChanged - 我假设您有,如果没有的话,这里有一些代码。您通常也会在大多数视图模型上执行此操作,以通知视图属性更改。

internal class StaffViewModel : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            OnPropertyChanged("LastName");
        }
    }
    public override string ToString()
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
于 2012-08-30T17:46:36.203 回答