无法更新子项列表框
Cannot update subitems ListBox
我有两个 ListBox
。一个 ListBox
显示一些项目。每个项目都有一个子项目列表。第二个 ListBox
显示第一个 ListBox
中当前项目的子项目。一个典型的 item/subitem 场景。当我向子项列表添加值时,无法更新第二个 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");
}
}
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>
问题是您的 Item
类型包含 List<int>
,它不会通知集合更改,因为它没有实现 INotifyCollectionChanged
interface. Consequently, bindings do not get notified to update their values in the user interface when the collection is modified. Using a collection that implements this interface, e.g. an ObservableCollection<int>
来解决问题。
顺便说一句,这同样适用于 ItemValue
和 SubItems
属性以及 INotifyPropertyChanged
。这两个属性都有 setters,这意味着它们可以被重新分配,但是绑定也不会得到通知。由于您使用 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
。
When the source is a collection view, the current item can be specified with a slash (/). For example, the clause Path=/ sets the binding to the current item in the view. When the source is a collection, this syntax specifies the current item of the default collection view.
有一个special binding syntax, where you can use a /
表示当前项目,例如:
<ListBox Grid.Row="1" Name="SubItems" ItemsSource="{Binding Items/SubItems}">
另外一些关于您的视图模型代码的评论。
_items
字段初始值设定项是无用的,因为您无论如何都在构造函数中重新分配字段。
- 如果只使用集合视图而不使用
_items
,可以在构造函数中使用局部变量。
Items
的privatesetter不用了,可以去掉
- 将
Items.CurrentItem
直接转换为 Item
以获得无效转换异常,该异常比您稍后在使用 as
访问值时将获得的空引用异常更具体并获得 null
。如果您期望该值可能不是 Item
,您可以在 as
之后检查 null
或使用模式匹配来处理这两种情况,成功和错误 (CurrentItem
的类型不正确 Item
)。
我有两个 ListBox
。一个 ListBox
显示一些项目。每个项目都有一个子项目列表。第二个 ListBox
显示第一个 ListBox
中当前项目的子项目。一个典型的 item/subitem 场景。当我向子项列表添加值时,无法更新第二个 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");
}
}
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>
问题是您的 Item
类型包含 List<int>
,它不会通知集合更改,因为它没有实现 INotifyCollectionChanged
interface. Consequently, bindings do not get notified to update their values in the user interface when the collection is modified. Using a collection that implements this interface, e.g. an ObservableCollection<int>
来解决问题。
顺便说一句,这同样适用于 ItemValue
和 SubItems
属性以及 INotifyPropertyChanged
。这两个属性都有 setters,这意味着它们可以被重新分配,但是绑定也不会得到通知。由于您使用 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
。
When the source is a collection view, the current item can be specified with a slash (/). For example, the clause Path=/ sets the binding to the current item in the view. When the source is a collection, this syntax specifies the current item of the default collection view.
有一个special binding syntax, where you can use a /
表示当前项目,例如:
<ListBox Grid.Row="1" Name="SubItems" ItemsSource="{Binding Items/SubItems}">
另外一些关于您的视图模型代码的评论。
_items
字段初始值设定项是无用的,因为您无论如何都在构造函数中重新分配字段。- 如果只使用集合视图而不使用
_items
,可以在构造函数中使用局部变量。 Items
的privatesetter不用了,可以去掉- 将
Items.CurrentItem
直接转换为Item
以获得无效转换异常,该异常比您稍后在使用as
访问值时将获得的空引用异常更具体并获得null
。如果您期望该值可能不是Item
,您可以在as
之后检查null
或使用模式匹配来处理这两种情况,成功和错误 (CurrentItem
的类型不正确Item
)。