2011-06-30 39 views
8

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

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

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

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

状态将永远不会GOTO“激活”,这将直接进入“完成”的方法完成后。 我还使用调度这样的尝试:

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

为了解决这个问题,我创建了一个名为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); 
} 
+0

我需要很长时间才能找到Affecting_PropertyChanged方法的正确实现..你只需要一行:'MyBase.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))'并且我推荐覆盖ObservableCollection的InsertItem和RemoveItem,因为SetItem didn不为我工作。 –

+0

@Felix,是的,还有更多。类实现超过800行。我确实说过我已经发布了部分代码,主要是为了给出一般的想法。 –

+0

我写了这个为其他人找到这篇文章,不知道如何实现这些代码行。这不是一个更正,只是延长这个工作。 :)但为什么你的班级有800行的长度?我真的很高兴看到你的全班实施。 –

2

您已经遇到ObservableCollection的经典问题。它只会在项目添加或删除时通知。它不通知集合中某个项目的属性何时发生变化。如果您想要收到此类更改的通知,您将必须制作自己的自定义集合,并手动添加/删除单个对象上的属性已更改事件。对不起,老兄。

3

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

嘀......

  1. 上的ObservableCollection内部类执行INotifyPropertyChanged(并确保你触发,当你设置对类属性的情况下)
  2. 在您的ListView的ItemTemplate,是确定你正在使用绑定到属性

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

实施INotifyPropertyChanged的在里面的ObservableCollection类...(抬头看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> 

Your 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