-1

I am sharing a the same collection of items among several view models in a DataGrid. When one item on this shared list is selected, I need it to disappear from the list of the others items.

Here are the different view models used:

ParentViewModel.cs

public class ParentViewModel
{
    private readonly ObservableCollection<SharedItemViewModel> _sharedItems;

    public ParentViewModel()
    {
        // Shared items
        _sharedItems = new ObservableCollection<SharedItemViewModel>
        {
            new SharedItemViewModel { Visible = true },
            new SharedItemViewModel { Visible = true }
        };
        
        this.Children = new ObservableCollection<ChildViewModel>
        {
            // Each child references the same list
            new ChildViewModel(_sharedItems),
            new ChildViewModel(_sharedItems)
        }
    }
    
    public ObservableCollection<ChildViewModel> Children { get; }
}

SharedItemViewModel.cs

public class SharedItemViewModel
{
    private bool _visible;
    
    public bool Visible
    {
        get => _visible;
        set => this.UpdateProperty(ref _visible, value); // Raises the PropertyChanged event properly
    }
}

ChildViewModel.cs

public class ChildViewModel
{
    private SharedItemViewModel _selectedItem;

    public ChildViewModel(ObservableCollection<SharedItemViewModel> sharedItems)
    {
        this.Items = sharedItems;
    }
    
    public ObservableCollection<SharedItemViewModel> Items { get; }
    
    public SharedItemViewModel SelectedItem
    {
        get => _selectedItem;
        set 
        {
            if(_selectedItem != value)
            {
                // Set the old item visible again
                if (_selectedItem != null)
                {
                    _selectedItem.Visible = true;
                }
                
                _selectedItem = value;
                
                // Set the new item not visible
                if (value != null)
                {
                    _selectedItem.Visible = false;
                }
                
                this.NotifyPropertyChanged();
            }
        }
    }
}

To do so, I am trying to use a different CollectionViewSource for each row:

MyView.xaml

<DataGrid 
    ItemsSource="{Binding Children}"
    AutoGenerateColumns="False">
    
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Item">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.Resources>
                            <CollectionViewSource 
                                                    x:Key="LocalItems"
                                                    Source="{Binding Items, Mode=OneWay}" 
                                                    Filter="CollectionViewSource_Filter"
                                                    IsLiveFilteringRequested="True">
                                <CollectionViewSource.LiveFilteringProperties>
                                    <sys:String>Visible</sys:String>
                                </CollectionViewSource.LiveFilteringProperties>
                            </CollectionViewSource>
                        </Grid.Resources>
                        
                        <ComboBox 
                                    ItemsSource="{Binding Source={StaticResource LocalItems}}"
                                    SelectedItem="{Binding SelectedItem}"/>

                    </Grid>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

MyView.xaml.cs

private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
    e.Accepted = [..]; // Some work here to do to decide whether the item should be visible
}

When I run the application, the shared items are well bound to the comboboxes. When I select one item in one combobox, the Visible property is well set and the PropertyChanged event is well raised.

My issue is that I never (neither at loading nor when the Visible property changed) hit the Filter handler in my code-behind (tested with a breakpoint).

Do you know what is wrong with this code?

fharreau
  • 2,105
  • 1
  • 23
  • 46

2 Answers2

0

I am not sure why the event isn't raised but you could either filter the Items source collection directly, or you could try to set the Filter property of the view when the Grid in the CellTemplate is loaded:

private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    Grid grid = (Grid)sender;
    CollectionViewSource cvs = grid.Resources["LocalItems"] as CollectionViewSource;
    if (cvs != null)
        cvs.View.Filter = (s) => true; //your filtering logic here...
}

XAML:

<Grid Loaded="Grid_Loaded">
    <Grid.Resources>
        <CollectionViewSource x:Key="LocalItems">
        ...
mm8
  • 163,881
  • 10
  • 57
  • 88
  • The filter event is raised with this solution, it's a progress ! But I cannot perform my filtering logic only with the `SharedItemViewModel`. I also need to access the `ChildViewModel` (It's not as simple as my `Visible` property in my exemple :s). – fharreau Dec 15 '20 at 17:09
0

I found a workaround but I will not mark it as an answer since it "breaks" a little bit the MVVM pattern by adding a dependency to WindowsBase into my ViewModel.

The solution consists in building the CollectionViewSource in the ChildViewModel:

public class ChildViewModel
{
    private SharedItemViewModel _selectedItem;

    public ChildViewModel(ObservableCollection<SharedItemViewModel> sharedItems)
    {
        var cvs = new CollectionViewSource();
        cvs.Source = sharedItems;
        cvs.IsLiveFilteringRequested = true;
        cvs.LiveFilteringProperties.Add(nameof(SharedItemViewModel.Visible));
        cvs.View.Filter = this.Filter;

        this.Items = cvs.View;
    }
    
    public ICollectionView Items { get; }
    
    public SharedItemViewModel SelectedItem {..}
    
    private bool Filter(object obj)
    {
        var vm = obj as SharedItemViewModel;
        
        return vm.Visible;
    }
}
fharreau
  • 2,105
  • 1
  • 23
  • 46