【发布时间】:2015-09-30 21:46:48
【问题描述】:
我将在下面发布一些代码,但这里是我正在尝试做的事情的摘要:
我有 2 个视图模型和 1 个模型:
- ModelData - 简单的模型类,只包含一个字符串
- ModelDataViewModel - 带有 ObservableCollection 的 ViewModel 类
- MultipleCollectionsViewModel,带有 ObservableCollection 的 ViewModel 类。
我有一个“父”列表框,绑定到 MultipleCollectionsViewModel。此列表框具有 SelectionMode=Extended。每个项目代表一个 ModelDataViewModel,可以选择其中任意数量。
我有第二个“子”列表框。此列表框应显示父列表框中所有选定项目的所有 ModelData 对象。基本上,它显示集合 (SelectedItems) 的集合 (ChildItems)。
我得到了这个做我想做的事。但是遇到了不少问题。首先,当绑定到 SelectedItem 时,SelectionMode=Extended just plain 不能开箱即用。选择的第一个项目会触发该属性,后续选择不会。有很多关于这个的帖子,我通过向 ListBoxItem 添加 IsSelected 样式来解决这个问题。这将应用于“ModelDataViewModel”对象。
问题在于父视图模型 MultipleCollectionsViewModel 根本不知道其子视图模型之一的属性已设置。应该从 MultipleCollectionsViewModel 而非 ModelDataViewModel 触发正确的 PropertyChanged。
我通过添加一个事件来解决这个问题。我对 MVVM 的理解是它应该解决这些事件,并且只依赖于绑定。但我一直在努力向后弯腰,试图仅通过绑定/xaml 来实现这一点。所以,我发布这个是为了看看是否有人对我正在尝试做的事情有任何建议或不同的方法。
这是一个多选工作的屏幕截图:
代码如下:
ModelData 类非常简单:
public class ModelData
{
public ModelData(string id)
{
this.Identifier = id;
}
public string Identifier
{
get;
set;
}
}
这里是 ModelDataViewModel
public class ModelDataViewModel : ViewModelBase
{
private ObservableCollection<ModelData> _aClassInstances;
private string name;
public ModelDataViewModel() : this("No-Name") { }
public ModelDataViewModel(string name)
{
this.name = name;
this.ChildItems = new ObservableCollection<ModelData>();
this.ChildItems.Add(new ModelData(Name + "-1"));
this.ChildItems.Add(new ModelData(Name + "-2"));
this.ChildItems.Add(new ModelData(Name + "-3"));
this.AVMIsSelected = false;
}
public ObservableCollection<ModelData> ChildItems
{
get
{
return this._aClassInstances;
}
set
{
this._aClassInstances = value;
this.OnPropertyChanged("ChildItems");
}
}
public string Name
{
get
{
return name;
}
set
{
name = value;
this.OnPropertyChanged("Name");
}
}
private bool isSelected;
public bool AVMIsSelected
{
get
{
return this.isSelected;
}
set
{
this.isSelected = value;
this.OnPropertyChanged("AVMIsSelected");
}
}
}
还有 MultipleCollectionsViewModel:
public class MultipleCollectionsViewModel : ViewModelBase
{
ObservableCollection<ModelDataViewModel> firstItems;
ObservableCollection<ModelDataViewModel> _selectedItems;
public MultipleCollectionsViewModel()
{
this.firstItems = new ObservableCollection<ModelDataViewModel>();
this._selectedItems = new ObservableCollection<ModelDataViewModel>();
this.FirstItems.Add(new ModelDataViewModel("First-1"));
this.FirstItems.Add(new ModelDataViewModel("First-2"));
this.FirstItems.Add(new ModelDataViewModel("First-3"));
}
public ObservableCollection<ModelDataViewModel> FirstItems
{
get
{
return firstItems;
}
set
{
firstItems = value;
}
}
public ObservableCollection<ModelDataViewModel> SelectedItems
{
get
{
return this._selectedItems;
}
set
{
this._selectedItems.Clear();
foreach (ModelDataViewModel aViewModel in this.FirstItems.Where(n => n.AVMIsSelected))
{
this._selectedItems.Add(aViewModel);
}
this.OnPropertyChanged("SelectedItems");
this.OnPropertyChanged("SelectedSubItems");
}
}
public ObservableCollection<Model.ModelData> SelectedSubItems
{
get
{
return new ObservableCollection<Model.ModelData>(this.SelectedItems.SelectMany(n => n.ChildItems).Where(m => 1 == 1));
}
}
最后,这是视图和背后的代码:
<Window x:Class="MVVM_Help_Needed.View.SelectedItemSync"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SelectedItemSync" Height="500" Width="600">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" ItemsSource="{Binding Path=FirstItems}" SelectedItem="{Binding FirstSelectedItem, Mode=TwoWay}" SelectionMode="Extended" SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding AVMIsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button Grid.Column="1" Grid.Row="0" Content="{Binding Path=SelectedItems.Count}"></Button>
<ListBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Path=SelectedSubItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Identifier}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
public partial class SelectedItemSync : Window
{
public SelectedItemSync()
{
InitializeComponent();
DataContext = new ViewModel.MultipleCollectionsViewModel();
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ViewModel.MultipleCollectionsViewModel vm = this.DataContext as ViewModel.MultipleCollectionsViewModel;
vm.SelectedItems = null; // this fires the setter, doesn't actually modify anything.
}
}
【问题讨论】: