2016-09-30 2 views
2

Um der nativen WPF-Strukturansicht Unterstützung für Mehrfachauswahl hinzuzufügen, musste ich eine benutzerdefinierte Abhängigkeitseigenschaft hinzufügen, die die ausgewählten Mehrfachelemente speichert. Das funktioniert großartig, bis sich die Gegenstände des Baums zu verändern begannen.TreeView Items geändertes Ereignis

Zum Beispiel im ersten Baum gibt es ein Element A. Ich habe es ausgewählt, es wird in MultiSelectedItems Liste gespeichert. Dann entfernte ich Element A und fügte Element B hinzu. (Über ViewModel ObservableCollection Bindung)

Ich muss einen Weg finden, um Element A aus MultiSelectedItems Liste zu entfernen, wenn dies passiert.

Ich kann kein Ereignis dafür finden. Das nächste Ereignis, das ich erhalte, ist ItemContainerGenerator.ItemsChanged Ereignis, aber dieses Ereignis wird nur für Stammebenenknoten ausgelöst (feuert nicht für seine untergeordneten Hierarchieobjekte).

Antwort

0

Die Schlüsselidee, dieses Problem zu lösen, besteht darin, Ereignisse innerhalb eines jeden Knotens statt auf Baumebene zu erkennen.

Ich erbte die TreeViewItem Klasse

public class MultiSelectTreeViewItem : TreeViewItem 
{ 
    object _originalHeader; 
    protected override void OnHeaderChanged(object oldHeader, object newHeader) 
    { 
     base.OnHeaderChanged(oldHeader, newHeader); 
     //.NET 4.5 use BindingOperations.DisconnectedSource 
     if (newHeader.ToString() != "{DisconnectedItem}") 
      _originalHeader = newHeader; 
    } 

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) 
    { 
     base.OnItemsChanged(e); 

     if (Header.ToString() == "{DisconnectedItem}" && _originalHeader != null && e.Action == NotifyCollectionChangedAction.Reset) 
     { 
      //Find the parent Tree View and remove this from MultiSelectedList 
     } 
    } 


    protected override DependencyObject GetContainerForItemOverride() 
    { 
     return new MultiSelectTreeViewItem(); 
    } 

    protected override bool IsItemItsOwnContainerOverride(object item) 
    { 
     return item is MultiSelectTreeViewItem; 
    } 
} 

Im geerbt TreeView

protected override DependencyObject GetContainerForItemOverride() 
    { 
     return new MultiSelectTreeViewItem(); 
    } 

    protected override bool IsItemItsOwnContainerOverride(object item) 
    { 
     return item is MultiSelectTreeViewItem; 
    } 

Wenn der Knoten entfernt wurde, wird die Kopfzeile an einem Sentinel-Objekt namens DisconnectedItem gesetzt werden, und die Gegenstände Ereignis Changed wird NotifyCollectionChangedAction.Reset sein.

Beachten Sie, dass wenn Sie eine List.Clear()NotifyCollectionChangedAction.Remove Ereignis wird nicht Feuer, nur NotifyCollectionChangedAction.Reset wird. Also finde ich es die zuverlässigste Methode, Knotenentfernung zu erkennen.

Ein Haken ist, dass dieses Ereignis nicht ausgelöst wird, wenn der Knoten nicht gerendert wurde (das Element wurde nie erweitert).

+0

Wie Sie bereits erwähnt haben, ist dies in keiner Weise eine allgemeine Lösung und hat viele nicht triviale Fehlerfälle. Ich empfehle dringend, meine frühere Antwort zu folgen. –

+0

@AndrewHanlon Macht es Ihnen etwas aus, um ein besseres Verständnis davon zu bekommen? – Steve

1

Leider ist dies ein komplexes Problem, das nur dadurch erschwert wird, dass es mehrere Möglichkeiten gibt, ein TreeView in WPF zu implementieren. Wenn Sie MVVM, Virtualisierung und HierarchicalDataTemplates verwenden, sind die ausgewählten Elemente möglicherweise nicht einmal Teil der visuellen oder logischen Bäume zu einem bestimmten Zeitpunkt - ganz zu schweigen davon, dass selbst der Versuch, die Entfernung eines einzelnen Objekts zu überwachen, nicht ausreichen wird Vorfahren könnten stattdessen entfernt werden.

Mein Vorschlag ist, eine naive Multiselection auf der Steuerungsebene zu implementieren und implementieren eine intelligenten Ansichtsmodell Hierarchie:

erlaubt sowohl ein ‚Eltern‘ und ‚Wurzel‘ Knoten in Ihrer Ansichtsmodell Hierarchie zugegriffen werden, und Gegenstände lassen um ihre Nachkommen aus der Root.SelecteItems Sammlung zu entfernen, wenn sich ihre Child-Auflistung ändert. In meinem MVVM-Framework habe ich eine HierarchicalRootViewModelBase und eine HierarchicalViewModelBase, die ich für alle Hierarchie-VMs verwende. Auf diese Weise wird die gesamte Baumfunktionalität (wie Selektion und Sammlung geänderter Ereignisse) einmal implementiert und automatisch gehandhabt. Jede Basisklasse wird mit einem Verweis auf ihren Elternknoten und den Stammknoten erstellt (oder sie verwendet die Rekursion, um den Stamm zu finden).

Auf diese Weise kann das Entfernen eines Elements in beliebiger Hierarchietiefe leicht Aktionen auf Root-Ebene wie das Überprüfen/Aktualisieren der SelectedItems-Auflistung auslösen.

Verwandte Themen