使用ObservableCollection未正确更新ListView

use*_*491 8 c# listview dispatcher observablecollection

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

_DataCollections.ElementAt(count).Status = "Active";
Run Code Online (Sandbox Code Playgroud)

由于按下按钮,我在长时间操作之前执行此更改.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();
    }
Run Code Online (Sandbox Code Playgroud)

状态将永远不会转到"活动",它将在方法完成后直接转到"已完成".我也尝试使用这样的调度程序:

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(); });

    }
Run Code Online (Sandbox Code Playgroud)

但是,这似乎也不正常.任何提示或想法将不胜感激.

And*_*der 5

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

必需的...

  1. 在 ObservableCollection 中的类上实现 INotifyPropertyChanged(并确保在该类上设置属性时触发事件)
  2. 在你的 ListView 的 ItemTemplate 上,确保你使用了 Binding 到属性

如果你做了这两件事,就不需要“刷新”调用或其他任何东西。设置触发 INotifyPropertyChanged 的​​属性将导致 ItemTemplate 的 Binding 更新。

在 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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你的列表视图

<ListView
    x:Name="listView">

    <ListView.ItemTemplate>
        <DataTemplate>

            <StackPanel>

                <TextBlock
                    Text="{Binding Name}"/>

                <TextBlock
                    Text="{Binding Date}"/>

            </StackPanel>

        </DataTemplate>
    </ListView.ItemTemplate>

</ListView>
Run Code Online (Sandbox Code Playgroud)

你的 ObservableCollection...

private ObservableCollection<ToDoItem> _toDoItems = new ObservableCollection<ToDoItem>();

// Assign the collection to the ListView
listView.ItemsSource = _toDoItems;
Run Code Online (Sandbox Code Playgroud)

将东西添加到集合中工作...

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

和更新,你所要求的,工作......

ToDoItem item = _toDoItems[randomIndex];

item.Name = "Updated " + item.Name;
item.Date = DateTime.Now;
Run Code Online (Sandbox Code Playgroud)

无需调用“刷新”或其他任何需要。项目本身会更新,列表不会改变。

在更新第 4 项之前...

更新第 4 项之前

更新第 4 项后...

更新第 4 项后

此处提供完整代码示例: 代码示例


Ed *_*tes 2

为了解决这个问题,我创建了一个名为 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);
}
Run Code Online (Sandbox Code Playgroud)