无法更新子项列表框
Posted
技术标签:
【中文标题】无法更新子项列表框【英文标题】:Cannot update subitems ListBox 【发布时间】:2021-07-16 22:59:06 【问题描述】:我有两个ListBox
es。一个ListBox
显示一些项目。每个项目都有一个子项目列表。第二个ListBox
显示第一个ListBox
中当前项的子项。典型的项目/子项目场景。当我向子项列表添加一个值时,我无法让第二个 ListBox
更新。如何强制第二个ListBox
更新我的子项?
我的简化 XAML 和视图模型如下。请注意,我的视图模型继承自 Prism 的BindableBase
。在我的视图模型中,我有一种方法可以将值添加到子项列表并使用RaisePropertyChanged
更新Items
属性。
<ListBox Name="Items" ItemsSource="Binding Items" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="Binding ItemValue, Mode=TwoWay" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Name="SubItems" ItemsSource="Binding Items.CurrentItem.SubItems" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="Binding" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
class Item
public int ItemValue get; set;
public List<int> SubItems get; set;
class MyViewModel : BindableBase
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public MyViewModel()
List<Item> myItems = new List<Item>() /* a bunch of items */ ;
_items = new ObservableCollection<Item>(myItems);
Items = new ListCollectionView(_items);
public ICollectionView Items get; private set;
private void AddNewSubItem(object obj)
Item currentItem = Items.CurrentItem as Item;
currentItem.SubItems.Add(123);
RaisePropertyChanged("Items");
【问题讨论】:
<ListBox Name="SubItems" ItemsSource="Binding SelectedItem.SubItems, ElementName=Items" >
这不起作用。将值添加到 SubItems 就好了,只是不更新 ListBox。我试过 Items.InvalidateVisual 和 Items.UpdateLayout - 也没有用。
Item.SubItems 是 ObservableCollection 吗?
【参考方案1】:
问题在于您的Item
类型包含List<int>
,它不会通知集合更改,因为它没有实现INotifyCollectionChanged
接口。因此,当集合被修改时,绑定不会在用户界面中收到更新其值的通知。使用实现此接口的集合,例如ObservableCollection<int>
将解决问题。
顺便说一句,ItemValue
和 SubItems
属性也适用于 INotifyPropertyChanged
。这两个属性都有设置器,这意味着它们可以被重新分配,但绑定也不会得到通知。由于您使用BindableBase
,因此您可以使用带有支持字段的SetProperty
方法在设置属性时自动引发PropertyChanged
事件。
public class Item : BindableBase
private int _itemValue;
private ObservableCollection<int> _subItems;
public int ItemValue
get => _itemValue;
set => SetProperty(ref _itemValue, value);
public ObservableCollection<int> SubItems
get => _subItems;
set => SetProperty(ref _subItems, value);
关于您的项目的评论。您尝试做的是master-detail view for hierarchical data。由于您已经公开了ICollectionView
,因此您不必显式使用CurrentItem
。
当源是集合视图时,当前项可以用斜杠 (/) 指定。例如,子句 Path=/ 将绑定设置为视图中的当前项。当源是集合时,此语法指定默认集合视图的当前项。
有一个special binding syntax, where you can use a /
表示当前项,例如:
<ListBox Grid.Row="1" Name="SubItems" ItemsSource="Binding Items/SubItems">
另外一些关于您的视图模型代码的评论。
_items
字段初始值设定项毫无用处,因为无论如何都要在构造函数中重新分配字段。
如果只使用集合视图而不使用_items
,则可以在构造函数中使用局部变量。
Items
的私有 setter 没有使用,可以移除。
将Items.CurrentItem
直接转换为Item
以获得一个无效转换异常,该异常比稍后在使用as
访问值并获取null
时获得的空引用异常更具体。如果您希望该值可能不是Item
,您可以在as
之后检查null
,或者使用模式匹配来处理成功和错误这两种情况(CurrentItem
不是Item
类型) 适当。
【讨论】:
非常感谢!一个非常完整的答案。我也非常感谢 cmets 关于代码的其他部分。【参考方案2】:class Item
public int ItemValue get; set;
public ObservableCollection<int> SubItems get;
= new ObservableCollection<int>();
class MyViewModel : BindableBase
publc ObservableCollection<Item> Items get;
= new ObservableCollection<Item>();
public MyViewModel()
List<Item> myItems = new List<Item>() /* a bunch of items */ ;
myItems.ForEach(item => Items.Add(item));
private Item _currentItem;
private Item CurrentItem
get => _currentItem;
set
if(Equals(_currentItem, value))
return;
_currentItem = value;
RaisePropertyChanged(nameof(CurrentItem));
private void AddNewSubItem(object obj)
CurrentItem.SubItems.Add(123);
<ListBox ItemsSource="Binding Items"
SelectedItem="Binding CurrentItem">
********
********
</ListBox>
<ListBox ItemsSource="Binding CurrentItem.SubItems" >
********
********
</ListBox>
【讨论】:
以上是关于无法更新子项列表框的主要内容,如果未能解决你的问题,请参考以下文章
如果子项从 RTK 查询中的获取请求更新,我如何更新列表中的数据