У меня есть модель представления, которая представляет элементы в ListBox. Существует две коллекции: Source
содержит все элементы и Checked
содержит только отмеченные элементы. Есть две кнопки SelectAll и ClearAll. Когда я нажимаю sh на одну из этих кнопок, View Model работает хорошо и обновляются коллекции Source
и Checked
, но никаких изменений в Listbox не происходит.
CheckItemPresenterVM<T>
- элемент, который сохраняет состояние одной кнопки и реализует INotifyPropertyChange, но при изменении свойства IsChecked
событие CollectionChanged
не возникает.
Вопрос в том, как заставить это работать?
<UserControl.Resources>
<ItemsPanelTemplate x:Key="listPanelTemplate">
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<Style TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ListBoxItemIsCheckedBinding"
TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected"
Value="{Binding IsChecked, Mode=TwoWay}" />
<Setter Property="Padding"
Value="5,3" />
<Setter Property="Background"
Value="LightCyan"></Setter>
</Style>
<Style TargetType="Button">
<Setter Property="Padding"
Value="7, 3" />
<Setter Property="Margin"
Value="1" />
</Style>
<Style TargetType="ToggleButton">
<Setter Property="Padding"
Value="7, 3" />
<Setter Property="Margin"
Value="1" />
</Style>
</UserControl.Resources>
<Grid>
<Expander Header="Categories"
IsExpanded="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0"
Margin="0, 5">
<Button Command="{Binding SelectAll}">Select All</Button>
<Button Command="{Binding ClearAll}">Clear All</Button>
<Button Command="{Binding InvertSelection}">Invert Selection</Button>
</WrapPanel>
<ListBox Grid.Row="1" />
<ListBox Name="CategoriesListBox"
Grid.Row="2"
SelectionMode="Multiple"
ItemsSource="{Binding CategoryVM.Source, Mode=TwoWay}"
ItemContainerStyle="{DynamicResource ListBoxItemIsCheckedBinding}"
ItemsPanel="{StaticResource listPanelTemplate}" />
<ListBox Name="CheckedListBox"
Grid.Row="3"
SelectionMode="Multiple"
ItemsSource="{Binding CategoryVM.Checked}"/>
</Grid>
</Expander>
</Grid>
class TestViewModel
{
public CheckItemVM<string> CategoryVM { get; set; }
public TestViewModel()
{
logger.FileName = "TestSelectionFilter.txt";
logger.AddEventRecord(this);
CategoryVM = new CheckItemVM<string>(
new List<string>() {
"01 elem",
"02 elem",
"03 elem",
"04 elem",
"05 elem",
"06 elem",
"07 elem",
"08 elem",
"09 elem",
"10 elem",
"11 elem",
"12 elem",
"13 elem",
"14 elem",
"15 elem",
"16 elem",
"17 elem",
"18 elem",
"19 elem",
"20 elem",
"21 elem",
"22 elem",
"23 elem",
"24 elem",
"25 elem"});
}
public ICommand SelectAll
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
CategoryVM.SelectAll();
},
CanExecutePredicate = p =>
{
return !CategoryVM.IsAllSelected();
}
};
}
}
public ICommand InvertSelection
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
CategoryVM.InvertSelection();
},
CanExecutePredicate = p =>
{
return true;
}
};
}
}
public ICommand ClearAll
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
CategoryVM.ClearAll();
},
CanExecutePredicate = p =>
{
return CategoryVM.Checked.Any();
}
};
}
}
class CheckItemVM<T>
{
protected ObservableCollection<CheckItemPresenterVM<T>> _checked = new ObservableCollection<CheckItemPresenterVM<T>>();
public ObservableCollection<CheckItemPresenterVM<T>> Source { get; set; }
public ObservableCollection<CheckItemPresenterVM<T>> Checked { get => _checked; }
public CheckItemVM(ICollection<T> _source)
{
Source = new ObservableCollection<CheckItemPresenterVM<T>>();
UpdateSource(_source);
}
protected void UpdateSource(ICollection<T> _source)
{
foreach (var item in _source)
{
Source.Add(new CheckItemPresenterVM<T>(ref _checked)
{ Item = item, IsChecked = false });
}
}
public void SetSelection(CheckItemPresenterVM<T> sourceItem, bool flag)
{
if (sourceItem.IsChecked != flag)
{
sourceItem.IsChecked = flag;
}
}
public void SelectAll()
{
foreach (var item in Source)
{
item.IsChecked = true;
}
}
public void ClearAll()
{
foreach (var item in Source)
{
item.IsChecked = false;
}
}
public void InvertSelection()
{
foreach (var item in Source)
{
if (item.IsChecked) item.IsChecked = false;
else item.IsChecked = true;
}
}
public bool IsAllSelected()
{
return Source.Count == Checked.Count;
}
}
class CheckItemPresenterVM<T> : INotifyPropertyChanged
{
protected bool _isChecked;
protected ObservableCollection<CheckItemPresenterVM<T>> _checked;
public CheckItemPresenterVM(ref ObservableCollection<CheckItemPresenterVM<T>> Checked)
{
_checked = Checked;
}
public T Item { get; set; }
public string Name { get; set; }
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
if (value)
{
if (!_checked.Contains(this))
{
_checked.Add(this);
NotifyPropertyChanged("Item was Checked");
}
}
else
{
if (_checked.Contains(this))
{
_checked.Remove(this);
NotifyPropertyChanged("Item Unchecked");
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return Item.ToString();
}
}