简体   繁体   English

无法更新子项列表框

[英]Cannot update subitems ListBox

I have two ListBox es.我有两个ListBox One ListBox displays some items.一个ListBox显示一些项目。 Each item has a list of subitems.每个项目都有一个子项目列表。 The second ListBox displays the subitems of the current item in the first ListBox .第二个ListBox显示第一个ListBox中当前项的子项。 A typical item/subitem scenario.典型的项目/子项目场景。 When I add a value to the subitem list, I cannot get the second ListBox to update.当我向子项列表添加一个值时,我无法让第二个ListBox更新。 How can I force the second ListBox to update my subitems?如何强制第二个ListBox更新我的子项?

My simplified XAML and view model are below.我的简化 XAML 和视图 model 如下。 Note that my view model inherits from Prism's BindableBase .请注意,我的观点 model 继承自 Prism 的BindableBase In my view model I have a method to add a value to the subitems list and update the Items property with RaisePropertyChanged .在我看来 model 我有一种方法可以将值添加到子项列表并使用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>

The issue is that your Item type contains a List<int> , which does not notify on collection changes, because it does not implement the INotifyCollectionChanged interface.问题是您的Item类型包含一个List<int> ,它不会通知集合更改,因为它没有实现INotifyCollectionChanged接口。 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, eg an ObservableCollection<int> will solve the issue.使用实现此接口的集合,例如ObservableCollection<int>将解决该问题。

By the way, the same applies to ItemValue and SubItems properties as well with INotifyPropertyChanged .顺便说一句,这同样适用于ItemValueSubItems属性以及INotifyPropertyChanged Both properties have setters, which means they can be reassigned, but the bindings will not get notified then either.这两个属性都有设置器,这意味着它们可以被重新分配,但绑定也不会得到通知。 Since you use BindableBase , you can use the SetProperty method with backing fields to automatically raise the PropertyChanged event when a property is set.由于您使用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);
   }
}

A remark about your project.关于您的项目的评论。 What you try to do is a master-detail view for hierarchical data .您尝试做的是分层数据的主从视图 Since you already expose an ICollectionView , you do not have to use the CurrentItem explicitly.由于您已经公开了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.例如,子句 Path=/ 将绑定设置为视图中的当前项。 When the source is a collection, this syntax specifies the current item of the default collection view.当源是集合时,此语法指定默认集合视图的当前项。

There is a special binding syntax, where you can use a / to indicate the current item, eg:有一种特殊的绑定语法,您可以在其中使用/来指示当前项,例如:

<ListBox Grid.Row="1" Name="SubItems" ItemsSource="{Binding Items/SubItems}">

Additionally a few remarks about your view model code.另外,关于您查看 model 代码的一些评论。

  • The _items field initializer is useless, as you reassign the field in the constructor anyway. _items字段初始值设定项是无用的,因为无论如何您都在构造函数中重新分配了该字段。
  • If you only use the collection view and not _items , you can use a local variable in the constructor.如果只使用集合视图而不使用_items ,则可以在构造函数中使用局部变量。
  • The private setter of Items is not used, you can remove it.不使用Items的私有 setter,您可以将其移除。
  • Cast the Items.CurrentItem directly to Item to get an invalid cast exception that is more specific that the null reference exception that you would get later when accessing the value from using as and getting null .Items.CurrentItem直接转换为Item以获得一个无效的转换异常,该异常比 null 引用异常更具体,您稍后在访问使用as和获取null的值时会得到该异常。 If you expect that the value could be something else than an Item , you could check for null after as or use pattern matching to handle both cases, success and error ( CurrentItem is not of type Item ) appropriately.如果您希望该值可能不是Item ,您可以在as之后检查null或使用模式匹配来适当地处理成功和错误这两种情况( CurrentItem不是Item类型)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM