【问题标题】:MVVM : Listbox binding with SelectionMode=Extended, can't get this to work without SelectionChanged eventsMVVM:与 SelectionMode=Extended 绑定的列表框,如果没有 SelectionChanged 事件,则无法使其工作
【发布时间】: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. 
    }
}

【问题讨论】:

    标签: wpf xaml mvvm listbox


    【解决方案1】:

    这确实是 ListBox(和 DataGrid)的限制。基本上有两种解决方案:一种使用附加属性,另一种增强 ListBox 控件并添加缺少的属性。这是一个口味问题,我更喜欢第二种方式,因为它为我提供了 IntelliSense 帮助。在您的 XAML 中,您使用 MultiSelectionListBox 而不是 ListBox,您可以绑定到 AllSelectedItems。请注意,此解决方案仅观察新列表的更改/分配(在视图模型中设置)。 And it always creates a new list when the selection changes.如果在视图模型中设置了该类型,是否还需要观察 ObservableCollection 取决于您的需要。

    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);
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-24
      • 2015-11-16
      相关资源
      最近更新 更多