使用ObservableCollection未正确更新ListView

时间:2011-06-30 23:02:08

标签: c# listview observablecollection dispatcher

我目前正在使用一个可观察的集合来存储ListView的数据对象。将新对象添加到集合中工作正常,listView正确更新。但是,当我尝试更改集合中对象的某个属性时,listView将无法正确更新。例如,我有一个可观察的集合DataCollection。我试试

_DataCollections.ElementAt(count).Status = "Active";

由于按下按钮,我在长时间操作之前执行此更改。 listView不会反映更改。所以我添加了myListView.Items.Refresh();。这是有效的,但是在button_click方法完成之前,listView不会刷新,这在那时是不行的。 例如:

   button1_Click(...)
    {
      _DataCollections.ElementAt(count).Status = "Active";
      myListView.Items.Refresh();
      ExecuteLongOperation();
      _DataCollections.ElementAt(count).Status = "Finished";
      myListView.Items.Refresh();
    }

状态永远不会转到“活动”,在方法完成后它将直接转到“已完成”。 我也试过使用这样的调度员:

button1_Click(...)
    {
      this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
            (NoArgDelegate)delegate { _DataCollection.ElementAt(count).Status =  "Active"; myListView.Items.Refresh(); });

      ExecuteLongOperation();
     this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
            (NoArgDelegate)delegate { _DataCollection.ElementAt(count).Status =  "Finished"; myListView.Items.Refresh(); });

    }

然而,这似乎也不正常。任何提示或想法将不胜感激。

3 个答案:

答案 0 :(得分:3)

为了解决这个问题,我创建了一个名为VeryObservableCollection的类。对于您添加的每个对象,它将对象的NotifyPropertyChanged事件挂钩到触发CollectionChanged事件的处理程序。对于删除的每个对象,它将删除处理程序。很简单,会给你你想要的。部分代码:

public class VeryObservableCollection<T> : ObservableCollection<T>

/// <summary>
/// Override for setting item
/// </summary>
/// <param name="index">Index</param>
/// <param name="item">Item</param>
protected override void SetItem(int index, T item)
{
    try
    {
        INotifyPropertyChanged propOld = Items[index] as INotifyPropertyChanged;
        if (propOld != null)
            propOld.PropertyChanged -= new PropertyChangedEventHandler(Affecting_PropertyChanged);
    }
    catch (Exception ex)
    {
        Exception ex2 = ex.InnerException;
    }
    INotifyPropertyChanged propNew = item as INotifyPropertyChanged;
    if (propNew != null)
        propNew.PropertyChanged += new PropertyChangedEventHandler(Affecting_PropertyChanged);

    base.SetItem(index, item);
}

答案 1 :(得分:3)

您必须使用正确的数据绑定技术,然后这将自动运行。

必需...

  1. 在ObservableCollection中的类上实现INotifyPropertyChanged(并确保在设置该类的属性时触发事件)
  2. 在ListView的ItemTemplate上,确保使用Binding属性
  3. 如果您执行这两项操作,则无需进行“刷新”调用或其他任何操作。设置触发INotifyPropertyChanged的属性将导致ItemTemplate的绑定更新。

    在ObservableCollection中的类上实现INotifyPropertyChanged ... (如果你还不知道它,请查看BindableBase类)

    public class ToDoItem : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private string _name;
        public string Name
        {
            get { return _name; }
            set { SetProperty(ref _name, value); }
        }
    
        private DateTime _date;
        public DateTime Date
        {
            get { return _date; }
            set { SetProperty(ref _date, value); }
        }
    
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
        {
            if (object.Equals(storage, value)) return false;
    
            storage = value;
            this.OnPropertyChanged(propertyName);
            return true;
        }
    
        protected void OnPropertyChanged(string propertyName)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    您的ListView

    <ListView
        x:Name="listView">
    
        <ListView.ItemTemplate>
            <DataTemplate>
    
                <StackPanel>
    
                    <TextBlock
                        Text="{Binding Name}"/>
    
                    <TextBlock
                        Text="{Binding Date}"/>
    
                </StackPanel>
    
            </DataTemplate>
        </ListView.ItemTemplate>
    
    </ListView>
    

    您的ObservableCollection ......

    private ObservableCollection<ToDoItem> _toDoItems = new ObservableCollection<ToDoItem>();
    
    // Assign the collection to the ListView
    listView.ItemsSource = _toDoItems;
    

    向集合中添加内容有效......

    _toDoItems.Add(new ToDoItem()
    {
        Name = "Item " + (_toDoItems.Count + 1),
        Date = DateTime.Now
    });
    

    更新,你要求的是什么,有效......

    ToDoItem item = _toDoItems[randomIndex];
    
    item.Name = "Updated " + item.Name;
    item.Date = DateTime.Now;
    

    不要求“刷新”或其他任何需要的东西。项目本身会更新,而不会更改列表。

    更新第4项之前......

    Before updating Item 4

    更新第4项......

    After updating Item 4

    此处提供完整的代码示例: CODE SAMPLE

答案 2 :(得分:2)

你遇到了ObservableCollection的经典问题。它只会在添加或删除项目时通知。它不会在集合中项目的属性发生更改时通知。如果您希望收到此类更改的通知,则必须手动创建自己的自定义集合并在单个对象上添加/删除属性更改事件。对不起,老兄。