2011-06-30 12 views
8

Aktualnie używam obserwowalnej kolekcji do przechowywania moich obiektów danych dla ListView. Dodawanie nowych obiektów do kolekcji działa dobrze, a lista wyświetla się poprawnie. Jednak gdy próbuję zmienić jedną z właściwości obiektu w kolekcji, listView nie zaktualizuje się poprawnie. Na przykład mam obserwowalną kolekcję DataCollection. PróbujęListView nie jest poprawnie aktualizowany za pomocą ObservableCollection

Dokonuję tej zmiany przed długą operacją z powodu naciśnięcia przycisku. ListView nie odzwierciedli zmiany. Więc dodaję myListView.Items.Refresh();. To działa, jednak listView nie zostanie odświeżony, dopóki metoda button_click nie zostanie zakończona, co nie jest do tego dobre. Na przykład:

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

Stan nigdy nie będzie goto „Active”, to pójdzie prosto do „Zakończone” po zakończeniu metody. Próbowałem też za pomocą dyspozytora tak:

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

    } 

jednak, że nie wydaje się działać prawidłowo albo. Wszelkie wskazówki i pomysły będą mile widziane.

Odpowiedz

3

Aby rozwiązać ten problem, stworzyłem klasę o nazwie VeryObservableCollection. Dla każdego dodawanego obiektu przechwytuje zdarzenie NotifyPropertyChanged obiektu do programu obsługi, który wyzwala zdarzenie CollectionChanged. Dla każdego usuniętego obiektu usuwa on program obsługi. Bardzo proste i daje dokładnie to, co chcesz. Kod częściowy:

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

Potrzebuję dużo czasu, aby znaleźć poprawną implementację metody Affecting_PropertyChanged. Potrzebny jest tylko jeden wiersz: 'MyBase.OnCollectionChanged (New NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset))' i polecam nadpisanie InsertItem i RemoveItem z ObservableCollection, ponieważ SetItem didn Działają dla mnie. –

+0

@Felix, tak, było w tym coś więcej. Wdrożenie klasy przekracza 800 linii. Powiedziałem, że napisałem częściowy kod, głównie po to, aby dać ogólny pomysł. –

+0

Napisałem to dla innych osób, które znajdują ten wpis i nie wiedzą, jak zaimplementować te linie kodu. To nie była korekta, tylko przedłużenie, żeby to działało. :), ale dlaczego twoja klasa ma długość 800 linii? Naprawdę chciałbym zobaczyć twoją pełną klasę implementacji. –

2

Wystąpił klasyczny problem z ObservableCollection. informuje tylko o dodaniu lub usunięciu przedmiotu. NIE powiadamia o zmianie właściwości elementu w kolekcji. Jeśli chcesz być powiadamiany o takich zmianach, będziesz musiał ręcznie utworzyć własną kolekcję i dodać/usunąć właściwość, która zmieniła wydarzenia w poszczególnych obiektach. przepraszam, koleś.

3

Musisz użyć właściwych technik wiązania danych, a następnie zadziała to automagicznie.

Wymagane ...

  1. zaimplementować INotifyPropertyChanged na klasy wewnątrz ObservableCollection (i upewnij się, że wyzwolenie zdarzenia, gdy jesteś na właściwości tej klasy ustawienia)
  2. na własną ListView w ItemTemplate, być pewnie używasz wiązania do właściwości

Jeśli robisz te dwie rzeczy, nie ma potrzeby wywołania "Odśwież" ani niczego innego. Ustawienie właściwości, która wyzwala INotifyPropertyChanged spowoduje aktualizację wiązania obiektu ItemTemplate.

wykonawcze INotifyPropertyChanged na klasy wewnątrz ObservableCollection ... (patrzeć klasę BindableBase jeśli nie wie o tym już)

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

Twój ListView

<ListView 
    x:Name="listView"> 

    <ListView.ItemTemplate> 
     <DataTemplate> 

      <StackPanel> 

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

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

      </StackPanel> 

     </DataTemplate> 
    </ListView.ItemTemplate> 

</ListView> 

Twoja ObservableCollection ...

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

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

Dodawanie rzeczy do zbierania działa ...

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

i aktualizacji, co pan prosi o, działa ...

ToDoItem item = _toDoItems[randomIndex]; 

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

Brak połączenia do "Odświeżania" lub czegokolwiek, co jest potrzebne. Sam przedmiot aktualizuje się bez zmiany listy.

Przed aktualizacją pkt 4 ...

Before updating Item 4

Po aktualizacji pozycja 4 ...

After updating Item 4

Pełny kod przykładowy dostępne tutaj:CODE SAMPLE

Powiązane problemy