6

Josh Smith のデモ MVVM アプリケーションを拡張して、その背後にあるプリンシパルをよりよく理解しようとしてきましたが、ListView を使用してビューにフィルター関数を実装しようとすると、壁にぶつかりました。

私は数時間を調査して手を出しましたが、うまくいきません。

私の最初のステップは、ビューのテキスト ボックスを ViewModel のプロパティにバインドすることでした。

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

これは私の VM で一致します。

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

私の VM はデータソースに 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 を指定できますが、両方を行っています。つまり、キーが x:key="StaffGroup" の xaml と CvsStaff の VM です。xaml を完全に取り除き、正しくセットアップされた VM を使用するとします。次に、次のように、ItemsSource プロパティを使用してバインドします。

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

また、小さなことですが、フィルターを変更して、ヌルと空白をチェックしました。大文字と小文字を区別しないように変更する必要がある場合もあります。

ここでは触れていませんが、重要なもう 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 に答える