简体   繁体   中英

Two-way binding and filtering of ObservableCollection in WPF/MVVM

I am learning MVVM pattern while refactoring an app to MVVM.

I have a model class Machine that provides a list of installations in a form of ObservableCollection<Installation> Installations .

In one of the windows (views) I need to display only those installations that have updates (thus meet the following criteria):

    private void InstallationsToUpdateFilter(object sender, FilterEventArgs e)
    {
        var x = (Installation)e.Item;
        bool hasNewVersion = ShowAllEnabledInstallations ?  true : x.NewVersion != null;
        bool isSetAndOn = !String.IsNullOrEmpty(x.Path) && x.CheckForUpdatesFlag;
        e.Accepted = isSetAndOn && hasNewVersion;
    }

    private void OnFilterChanged()
    {
        installationsToUpdateSource?.View?.Refresh();
    }

I am doing this by filtering in my 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; }
    }
    ...
}

This is done by custom 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>

All of this works - until I try to "send" <CheckBox IsChecked="{Binding Path=MarkedForUpdate... back to my model - so it will be stored there.

How it can be done? (Can I have some kind of setter on ICollectionView ?)

Current architecture can be changed. What I ultimately need:

  1. Display items ( installations ) from model in ListView (currently: works )
  2. Filter/Show only installations that meet some criteria (currentrly: works )
  3. Reflect changes in MarkedForUpdate checkbox back to model (currently: not working )

I've googled a lot but was unable to find a relevant solution or suggestions. Any help would be greatly appreciated. Thanks!

I figured the problem out. Although it was a silly mistake, I still want to share it to save someone's time.

The model itself updates in the configuration described above. The problem was that what model property ( Machine.Installations in my case) did not implement INotifyPropertyChanged interface so other Views (through their corresponding ViewModels) were not aware of changes. Thus one should use OnPropertyChanged/RaisePropertyChanged not only in ViewModel , but in Model as well.

Hope this may help someone.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM