Wpf MVVM:SelectionMode=Extended的列表框绑定,can';如果不选择Changed events(已更改事件),则无法运行此功能

Wpf MVVM:SelectionMode=Extended的列表框绑定,can';如果不选择Changed events(已更改事件),则无法运行此功能,wpf,xaml,mvvm,listbox,Wpf,Xaml,Mvvm,Listbox,我将在下面发布一些代码,但以下是我尝试执行的操作的摘要: 我有2个视图模型和1个模型: ModelData-简单的模型类,只包含一个字符串 ModelDataViewModel-带有可观察集合的ViewModel类 MultipleCollectionsViewModel,具有ObservableCollection的ViewModel类 我有一个“父”列表框,绑定到MultipleCollectionsViewModel。此列表框具有SelectionMode=Extended。每个项表示

我将在下面发布一些代码,但以下是我尝试执行的操作的摘要:

我有2个视图模型和1个模型:

  • ModelData-简单的模型类,只包含一个字符串
  • ModelDataViewModel-带有可观察集合的ViewModel类
  • MultipleCollectionsViewModel,具有ObservableCollection的ViewModel类
我有一个“父”列表框,绑定到MultipleCollectionsViewModel。此列表框具有SelectionMode=Extended。每个项表示一个ModelDataViewModel,可以选择任意数量的项

我有第二个“孩子”列表框。此列表框应显示父列表框中所有选定项的所有ModelData对象。基本上,它显示集合(SelectedItems)的集合(ChildItems)

我要做我想做的事。但是有很多问题。首先,当绑定到SelectedItem时,SelectionMode=Extended just plain无法开箱即用。选定的第一个项目将激发属性,而后续选择不会激发属性。有很多关于这个的帖子,我通过向ListBoxItem添加一个IsSelected样式来解决这个问题。这将应用于“ModelDataViewModel”对象

问题是父视图模型MultipleCollectionsViewModel没有任何线索表明其子视图模型之一的属性已设置。应从MultipleCollectionsViewModel而不是ModelDataViewModel激发正确的PropertyChanged

我通过添加一个事件来解决这个问题。我对MVVM的理解是,它应该围绕这些事件工作,并且只依赖于绑定。但我一直在竭尽全力,试图通过binding/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");         
        }
    }
}    
事实上,这是一个列表框(和数据网格)限制。基本上有两种解决方案:一种使用附加属性,另一种稍微增强ListBox控件并添加缺少的属性。这是一个品味的问题,我更喜欢第二种方式,因为它给了我智能感知的帮助。在XAML中,您使用
MultiSelectionListBox
而不是ListBox,并且可以绑定到
AllSelectedItems
。请注意,此解决方案仅观察新列表(在视图模型中设置)的更改/分配。当选择更改时,它总是创建一个新列表。如果在视图模型中设置了类型,则是否还要观察
可观察集合
,这取决于您的需要

public class MultiSelectionListBox : ListBox
{
  public static readonly DependencyProperty AllSelectedItemsProperty = DependencyProperty.Register(
    "AllSelectedItems",
    typeof(IList),
    typeof(MultiSelectionListBox),
    new FrameworkPropertyMetadata(null, OnAllSelectedItems));

  public MultiSelectionDataGrid()
  {
    this.SelectionChanged += this.MultiSelectionDataGridSelectionChanged;
  }

  public IList AllSelectedItems
  {
    get
    {
      return (IList)this.GetValue(AllSelectedItemsProperty);
    }

    set
    {
      this.SetValue(AllSelectedItemsProperty, value);
    }
  }

  private static void OnAllSelectedItems(object sender, DependencyPropertyChangedEventArgs e)
  {
    var me = (MultiSelectionListBox)sender;
    var newItemList = e.NewValue as IList;

    me.SetAllSelectedItems(newItemList);
  }

  private void MultiSelectionDataGridSelectionChanged(
    object sender, SelectionChangedEventArgs e)
  {
    IEnumerable<object> tempListSrc = this.SelectedItems.Cast<object>();
    var tempListDest = new List<object>();
    tempListDest.AddRange(tempListSrc.ToList());

    this.AllSelectedItems = tempListDest;
  }

  private void SetAllSelectedItems(IList list)
  {
    if (list == null)
    {
      return;
    }

    this.SelectedItems.Clear();

    foreach (object item in list)
    {
      this.SelectedItems.Add(item);
    }
  }
}
公共类MultiSelectionListBox:ListBox { 公共静态只读DependencyProperty AllSelectedItemsProperty=DependencyProperty.Register( “AllSelectedItems”, 类型(IList), 类型(多选列表框), 新的FrameworkPropertyMetadata(null,OnAllSelectedItems)); 公共MultiSelectionDataGrid() { this.SelectionChanged+=this.MultiSelectionDataGridSelectionChanged; } 公共IList AllSelectedItems { 收到 { 返回(IList)this.GetValue(AllSelectedItemsProperty); } 设置 { 此.SetValue(AllSelectedItemsProperty,value); } } 私有静态无效OnAllSelectedItems(对象发送方,DependencyPropertyChangedEventArgs e) { 变量me=(MultiSelectionListBox)发送方; var newItemList=e.NewValue作为IList; me.SetAllSelectedItems(新项目列表); } 私有void MultiSelectionDataGridSelectionChanged( 对象发送者,选择ChangedEventArgs(e) { IEnumerable tempListSrc=this.SelectedItems.Cast(); var templastdest=新列表(); tempListDest.AddRange(tempListSrc.ToList()); this.AllSelectedItems=templastdest; } 私有void SetAllSelectedItems(IList列表) { if(list==null) { 回来 } this.SelectedItems.Clear(); foreach(列表中的对象项) { 此项。选择编辑项。添加(项); } } }
<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. 
    }
}
public class MultiSelectionListBox : ListBox
{
  public static readonly DependencyProperty AllSelectedItemsProperty = DependencyProperty.Register(
    "AllSelectedItems",
    typeof(IList),
    typeof(MultiSelectionListBox),
    new FrameworkPropertyMetadata(null, OnAllSelectedItems));

  public MultiSelectionDataGrid()
  {
    this.SelectionChanged += this.MultiSelectionDataGridSelectionChanged;
  }

  public IList AllSelectedItems
  {
    get
    {
      return (IList)this.GetValue(AllSelectedItemsProperty);
    }

    set
    {
      this.SetValue(AllSelectedItemsProperty, value);
    }
  }

  private static void OnAllSelectedItems(object sender, DependencyPropertyChangedEventArgs e)
  {
    var me = (MultiSelectionListBox)sender;
    var newItemList = e.NewValue as IList;

    me.SetAllSelectedItems(newItemList);
  }

  private void MultiSelectionDataGridSelectionChanged(
    object sender, SelectionChangedEventArgs e)
  {
    IEnumerable<object> tempListSrc = this.SelectedItems.Cast<object>();
    var tempListDest = new List<object>();
    tempListDest.AddRange(tempListSrc.ToList());

    this.AllSelectedItems = tempListDest;
  }

  private void SetAllSelectedItems(IList list)
  {
    if (list == null)
    {
      return;
    }

    this.SelectedItems.Clear();

    foreach (object item in list)
    {
      this.SelectedItems.Add(item);
    }
  }
}