2011-06-30 4 views
8

Tôi hiện đang sử dụng bộ sưu tập quan sát để lưu trữ các đối tượng dữ liệu của mình cho một ListView. Việc thêm các đối tượng mới vào bộ sưu tập hoạt động tốt và các cập nhật listView đúng. Tuy nhiên khi tôi cố gắng thay đổi một trong các thuộc tính của một đối tượng trong bộ sưu tập thì listView sẽ không cập nhật đúng. Ví dụ, tôi có một bộ sưu tập DataCollection có thể quan sát được. Tôi thửListView không được cập nhật chính xác với ObservableCollection

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

Tôi thực hiện thay đổi này trước khi hoạt động lâu do nhấn nút. ListView sẽ không phản ánh thay đổi. Vì vậy, tôi thêm myListView.Items.Refresh(); Điều này làm việc, tuy nhiên listView không được làm mới cho đến khi phương thức button_click hoàn tất, điều này không tốt bằng cách đó. Ví dụ:

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

Tình trạng sẽ không bao giờ goto "Active", nó sẽ đi thẳng vào "Xong" sau khi phương pháp này hoàn tất. Tôi cũng đã thử sử dụng một điều phối viên như sau:

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

    } 

Tuy nhiên, điều đó dường như không hoạt động chính xác. Bất kỳ lời khuyên hoặc ý tưởng sẽ được đánh giá cao.

Trả lời

3

Để giải quyết vấn đề này, tôi đã tạo một lớp có tên là VeryObservableCollection. Đối với mỗi đối tượng bạn thêm vào, nó móc sự kiện NotifyPropertyChanged của đối tượng vào một trình xử lý kích hoạt một sự kiện CollectionChanged. Đối với mỗi đối tượng bị loại bỏ, nó loại bỏ trình xử lý. Rất đơn giản và sẽ cung cấp cho bạn chính xác những gì bạn muốn. Mã một phần:

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

tôi cần thời gian dài để tìm cách triển khai chính xác phương thức Affecting_PropertyChanged .. bạn chỉ cần một dòng: 'MyBase.OnCollectionChanged (NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset))' và tôi khuyên bạn nên ghi đè InsertItem và RemoveItem của ObservableCollection, vì SetItem đã làm 't làm việc cho tôi. –

+0

@Felix, vâng, đã có nhiều hơn nữa. Việc thực hiện lớp học là hơn 800 dòng. Tôi đã nói rằng tôi đã đăng một phần mã, chủ yếu là để cung cấp cho các ý tưởng chung. –

+0

Tôi đã viết thư này cho những người khác tìm thấy bài đăng này và không biết cách triển khai các dòng mã này. Nó không phải là một sự điều chỉnh, chỉ là một phần mở rộng để làm việc này. :) nhưng tại sao lớp của bạn có độ dài 800 dòng? Tôi thực sự thích thú khi thấy việc thực hiện lớp học của bạn. –

2

Bạn đã gặp sự cố cổ điển với ObservableCollection. nó chỉ thông báo khi một mục được thêm vào hoặc bị xóa. nó KHÔNG thông báo khi một thuộc tính của một mục trong bộ sưu tập thay đổi. nếu bạn muốn được thông báo về những thay đổi như vậy, bạn sẽ phải tạo bộ sưu tập tùy chỉnh của riêng mình và thêm/xóa thuộc tính đã thay đổi sự kiện trên các đối tượng riêng lẻ theo cách thủ công. xin lỗi, anh bạn.

3

Bạn phải sử dụng kỹ thuật ràng buộc dữ liệu thích hợp, sau đó điều này sẽ hoạt động tự động.

buộc ...

  1. Thực hiện INotifyPropertyChanged trên lớp của bạn bên trong ObservableCollection (và chắc chắn rằng bạn đang hiển thị các sự kiện khi bạn đang thiết lập các thuộc trên lớp đó)
  2. On ItemTemplate của ListView của bạn, hãy chắc chắn bạn đang sử dụng Liên kết với các thuộc tính

Nếu bạn thực hiện hai việc này, bạn không cần phải thực hiện cuộc gọi "Làm mới" hoặc bất kỳ điều gì khác. Việc thiết lập một thuộc tính kích hoạt INotifyPropertyChanged sẽ gây ra sự ràng buộc của ItemTemplate để cập nhật.

thực hiện INotifyPropertyChanged trên lớp của bạn bên trong ObservableCollection ... (nhìn lên lớp BindableBase nếu bạn không biết về nó đã)

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 bạn

<ListView 
    x:Name="listView"> 

    <ListView.ItemTemplate> 
     <DataTemplate> 

      <StackPanel> 

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

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

      </StackPanel> 

     </DataTemplate> 
    </ListView.ItemTemplate> 

</ListView> 

ObservableCollection của bạn ...

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

// Assign the collection to the ListView 
listView.ItemsSource = _toDoItems; 

Thêm điều cần thu hoạt động ...

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

Và cập nhật, những gì bạn đã yêu cầu, hoạt động ...

ToDoItem item = _toDoItems[randomIndex]; 

item.Name = "Updated " + item.Name; 
item.Date = DateTime.Now; 

Không cuộc gọi để "Làm mới" hoặc bất kỳ thứ gì khác cần thiết. Bản thân mục sẽ cập nhật mà không có danh sách thay đổi.

Trước khi cập nhật Khoản 4 ...

Before updating Item 4

Sau khi cập nhật Khoản 4 ...

After updating Item 4

mẫu mã đầy đủ có sẵn ở đây:CODE SAMPLE