#wpf #collectionviewsource
#wpf #collectionviewsource
Вопрос:
Я разделяю одну и ту же коллекцию элементов между несколькими моделями представления в DataGrid. Когда выбран один элемент в этом общем списке, мне нужно, чтобы он исчез из списка других элементов.
Здесь используются разные модели представления:
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();
}
}
}
}
Для этого я пытаюсь использовать разные CollectionViewSource
для каждой строки:
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
}
Когда я запускаю приложение, общие элементы хорошо привязаны к спискам со списком. Когда я выбираю один элемент в одном выпадающем списке, Visible
свойство правильно задано, и PropertyChanged
событие хорошо поднято.
Моя проблема в том, что я никогда (ни при загрузке, ни при изменении Visible
свойства) не нажимал на Filter
обработчик в моем коде (проверено с точкой останова).
Вы знаете, что не так с этим кодом?
Ответ №1:
Я не уверен, почему событие не вызывается, но вы можете либо напрямую отфильтровать Items
исходную коллекцию, либо попытаться установить Filter
свойство представления при Grid
загрузке в CellTemplate
:
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">
...
Комментарии:
1. Событие filter вызывается этим решением, это прогресс ! Но я не могу выполнить свою логику фильтрации только с
SharedItemViewModel
помощью . Мне также нужно получить доступ кChildViewModel
(это не так просто, как мояVisible
собственность в моем примере: s).
Ответ №2:
Я нашел обходной путь, но я не буду отмечать его как ответ, поскольку он немного «ломает» шаблон MVVM, добавляя зависимость WindowsBase
в мою ViewModel.
Решение состоит в создании CollectionViewSource
в 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;
}
}