C# WPF/MVVM中ObservableCollection的双向绑定和过滤

C# WPF/MVVM中ObservableCollection的双向绑定和过滤,c#,wpf,mvvm,data-binding,binding,C#,Wpf,Mvvm,Data Binding,Binding,我正在学习MVVM模式,同时将应用程序重构为MVVM 我有一个模型类Machine,它以可观察收集安装的形式提供安装列表 在其中一个窗口(视图)中,我只需要显示那些有更新的安装(因此满足以下条件): 我通过在我的ViewModel中过滤来实现这一点: class NewVersionViewModel : ViewModelBase { private Machine machine = App.Machine; ... public NewVersionViewMod

我正在学习MVVM模式,同时将应用程序重构为MVVM

我有一个模型类
Machine
,它以
可观察收集安装的形式提供安装列表

在其中一个窗口(视图)中,我只需要显示那些有更新的安装(因此满足以下条件):

我通过在我的ViewModel中过滤来实现这一点:

class NewVersionViewModel : ViewModelBase
{
    private Machine machine = App.Machine;
    ...

    public NewVersionViewModel(...)
    {
        ...

        InstallationsToUpdate.CollectionChanged += (s, e) => 
        { 
            OnPropertyChanged("NewVersionsAvailableMessage");
            OnFilterChanged();
        };

        installationsToUpdateSource = new CollectionViewSource();
        installationsToUpdateSource.Source = InstallationsToUpdate;
        installationsToUpdateSource.Filter += InstallationsToUpdateFilter;

    }

    public ObservableCollection<Installation> InstallationsToUpdate
    {
        get { return machine.Installations; }
        set { machine.Installations = value; }
    }

    internal CollectionViewSource installationsToUpdateSource { get; set; }
    public ICollectionView InstallationsToUpdateSourceCollection
    {
        get { return installationsToUpdateSource.View; }
    }
    ...
}
class NewVersionViewModel:ViewModelBase
{
专用机器=应用程序机器;
...
公共NewVersionViewModel(…)
{
...
InstallationsToUpdate.CollectionChanged+=(s,e)=>
{ 
OnPropertyChanged(“新版本可用消息”);
OnFilterChanged();
};
installationsToUpdateSource=new CollectionViewSource();
installationsToUpdateSource.Source=InstallationsToUpdate;
installationsToUpdateSource.Filter+=InstallationsToUpdateFilter;
}
公共ObservableCollection安装更新
{
获取{return machine.Installations;}
设置{machine.Installations=value;}
}
内部集合ViewSource安装更新资源{get;set;}
公共ICollectionView安装更新资源集合
{
获取{return installationsToUpdateSource.View;}
}
...
}
这是通过自定义ListView完成的:

<ListView ItemsSource="{Binding InstallationsToUpdateSourceCollection}" ... >
            ...
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid ...>
                        <Grid ...>
                            <CheckBox Style="{StaticResource LargeCheckBox}"
                                      IsChecked="{Binding Path=MarkedForUpdate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                      IsEnabled="{Binding Path=HasNewVersion}"
                                      />
                        </Grid>
                        <Label Content="{Binding Path=InstalledVersion.Major}" Grid.Column="1" Grid.Row="0" FontSize="50" FontFamily="Segoe UI Black" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-10,0,0"/>
                        ...
                        <Grid.ContextMenu>
                            <ContextMenu>
                                ...
                            </ContextMenu>
                        </Grid.ContextMenu>
                    </Grid>                        
                </DataTemplate>
            </ListView.ItemTemplate>                
        </ListView>

...
...
...

所有这些都有效-直到我尝试“发送”
我才发现问题所在。虽然这是一个愚蠢的错误,但我还是想分享它,以节省别人的时间

模型本身在上述配置中更新。问题是什么模型属性(
Machine.Installations
在我的例子中)没有实现INotifyPropertyChanged接口,因此其他视图(通过其相应的ViewModels)没有意识到更改。因此,不仅应在视图模型中,而且还应在模型中使用
on属性更改/RaisePropertyChanged


希望这能对其他人有所帮助。

在WPF中过滤视图的典型方法是使用CollectionViewSorce。试着用它来实现它。
<ListView ItemsSource="{Binding InstallationsToUpdateSourceCollection}" ... >
            ...
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid ...>
                        <Grid ...>
                            <CheckBox Style="{StaticResource LargeCheckBox}"
                                      IsChecked="{Binding Path=MarkedForUpdate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                      IsEnabled="{Binding Path=HasNewVersion}"
                                      />
                        </Grid>
                        <Label Content="{Binding Path=InstalledVersion.Major}" Grid.Column="1" Grid.Row="0" FontSize="50" FontFamily="Segoe UI Black" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-10,0,0"/>
                        ...
                        <Grid.ContextMenu>
                            <ContextMenu>
                                ...
                            </ContextMenu>
                        </Grid.ContextMenu>
                    </Grid>                        
                </DataTemplate>
            </ListView.ItemTemplate>                
        </ListView>