我有两个ListBox
。一个 ListBox
显示一些项目。每个项目都有一个子项目列表。第二个 ListBox
显示第一个 ListBox
中当前项的子项。一个典型的项目/子项目场景。当我向子项列表添加值时,我无法更新第二个 ListBox
。如何强制第二个 ListBox
更新我的子项?
我的简化 XAML 和视图模型如下。请注意,我的视图模型继承自 Prism 的 BindableBase
。在我的视图模型中,我有一种方法可以将值添加到子项列表并使用 Items
更新 RaisePropertyChanged
属性。
<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");
}
}
答案 0 :(得分:0)
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>
答案 1 :(得分:0)
问题在于您的 Item
类型包含一个 List<int>
,它不会通知集合更改,因为它没有实现 INotifyCollectionChanged
接口。因此,当修改集合时,不会通知绑定更新它们在用户界面中的值。使用实现此接口的集合,例如ObservableCollection<int>
将解决问题。
顺便说一下,这同样适用于 ItemValue
和 SubItems
属性以及 INotifyPropertyChanged
。这两个属性都有 setter,这意味着它们可以重新分配,但绑定也不会得到通知。由于您使用 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
,您可以在 null
之后检查 as
或使用模式匹配来处理成功和错误两种情况({{1} } 不属于 CurrentItem
) 类型。