Datenbindung an SelectedItem in einer WPF-Treeview

Wie kann ich den in einer WPF-Treeview ausgewählten Artikel abrufen? Ich möchte dies in XAML tun, weil ich es binden möchte.

Sie könnten denken, dass es SelectedItem aber anscheinend existiert das nicht, ist nur lesbar und daher unbrauchbar.

Das möchte ich tun:

  

Ich möchte das SelectedItem an eine Eigenschaft in meinem Modell binden.

Aber das gibt mir den Fehler:

Die Eigenschaft ‘SelectedItem’ ist schreibgeschützt und kann nicht über Markup festgelegt werden.

Edit: Ok, so habe ich das getriggers:

  

und in der Codebehind-Datei von meinem XAML:

 private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { Model.SelectedCluster = (Cluster)e.NewValue; } 

Solutions Collecting From Web of "Datenbindung an SelectedItem in einer WPF-Treeview"

Ich weiß, das hat bereits eine Antwort akzeptiert, aber ich habe das zusammen genommen, um das Problem zu lösen. Es verwendet eine ähnliche Idee wie Deltas Lösung, ohne jedoch die TreeView ableiten zu müssen:

 public class BindableSelectedItemBehavior : Behavior { #region SelectedItem Property public object SelectedItem { get { return (object)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged)); private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var item = e.NewValue as TreeViewItem; if (item != null) { item.SetValue(TreeViewItem.IsSelectedProperty, true); } } #endregion protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged; } protected override void OnDetaching() { base.OnDetaching(); if (this.AssociatedObject != null) { this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged; } } private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { this.SelectedItem = e.NewValue; } } 

Sie können dies dann in Ihrem XAML wie folgt verwenden:

      

Hoffentlich wird es jemandem helfen!

Diese Eigenschaft ist vorhanden: TreeView.SelectedItem

Aber es ist schreibgeschützt, also können Sie es nicht durch eine Bindung zuweisen, sondern nur abrufen

Nun, ich habe eine Lösung gefunden. Es bewegt das Chaos, so dass MVVM funktioniert.

Füge zuerst diese class hinzu:

 public class ExtendedTreeView : TreeView { public ExtendedTreeView() : base() { this.SelectedItemChanged += new RoutedPropertyChangedEventHandler(___ICH); } void ___ICH(object sender, RoutedPropertyChangedEventArgs e) { if (SelectedItem != null) { SetValue(SelectedItem_Property, SelectedItem); } } public object SelectedItem_ { get { return (object)GetValue(SelectedItem_Property); } set { SetValue(SelectedItem_Property, value); } } public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(ExtendedTreeView), new UIPropertyMetadata(null)); } 

und füge das zu deinem XAML hinzu:

   .....  

Antwort mit angehängten Eigenschaften und keine externen Abhängigkeiten, sollte es jemals nötig sein!

Sie können eine angefügte Eigenschaft erstellen, die bindbar ist und Getter und Setter enthält:

 public class TreeViewHelper { private static Dictionary behaviors = new Dictionary(); public static object GetSelectedItem(DependencyObject obj) { return (object)obj.GetValue(SelectedItemProperty); } public static void SetSelectedItem(DependencyObject obj, object value) { obj.SetValue(SelectedItemProperty, value); } // Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc... public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(TreeViewHelper), new UIPropertyMetadata(null, SelectedItemChanged)); private static void SelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is TreeView)) return; if (!behaviors.ContainsKey(obj)) behaviors.Add(obj, new TreeViewSelectedItemBehavior(obj as TreeView)); TreeViewSelectedItemBehavior view = behaviors[obj]; view.ChangeSelectedItem(e.NewValue); } private class TreeViewSelectedItemBehavior { TreeView view; public TreeViewSelectedItemBehavior(TreeView view) { this.view = view; view.SelectedItemChanged += (sender, e) => SetSelectedItem(view, e.NewValue); } internal void ChangeSelectedItem(object p) { TreeViewItem item = (TreeViewItem)view.ItemContainerGenerator.ContainerFromItem(p); item.IsSelected = true; } } } 

Fügen Sie die Namespacedeklaration, die diese class enthält, zu Ihrem XAML hinzu und binden Sie sie wie folgt (lokal habe ich die Namespacedeklaration benannt):

    

Jetzt können Sie das ausgewählte Element binden und es auch in Ihrem Ansichtsmodell festlegen, um es programmgesteuert zu ändern, sollte diese Anforderung jemals auftreten. Dies setzt natürlich voraus, dass Sie INotifyPropertyChanged für diese bestimmte Eigenschaft implementieren.

Dies kann auf eine “nettere” Art und Weise erreicht werden, indem nur die Bindung und der EventToCommand der GalaSoft MVVM Light Bibliothek verwendet wird. Fügen Sie in Ihrer VM einen Befehl hinzu, der aufgerufen wird, wenn das ausgewählte Element geändert wird, und initialisieren Sie den Befehl, um die erforderliche Aktion auszuführen. In diesem Beispiel habe ich einen RelayCommand verwendet und nur die SelectedCluster-Eigenschaft festgelegt.

 public class ViewModel { public ViewModel() { SelectedClusterChanged = new RelayCommand( c => SelectedCluster = c ); } public RelayCommand SelectedClusterChanged { get; private set; } public Cluster SelectedCluster { get; private set; } } 

Fügen Sie dann das EventToCommand-Verhalten in Ihrem XAML hinzu. Dies ist sehr einfach mit Blend.

        

Es antwortet ein wenig mehr, als das OP erwartet … Aber ich hoffe, es könnte wenigstens jemandem helfen.

Wenn Sie einen ICommand bei ICommand SelectedItem ausführen ICommand , können Sie einen Befehl für ein Ereignis binden, und die Verwendung einer Eigenschaft SelectedItem im ViewModel wird nicht mehr benötigt.

Um dies zu tun:

1- Fügen Sie einen Verweis auf System.Windows.Interactivity

 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

2- Binden Sie den Befehl an das Ereignis SelectedItemChanged

           

Alles zu kompliziert … Gehen Sie mit Caliburn Micro (http://caliburnmicro.codeplex.com/)

Aussicht:

  

Ansichtsmodell:

 public void SetSelectedItem(YourNodeViewModel item) {}; 

Ich bin über diese Seite gestolpert und habe nach der gleichen Antwort wie der ursprüngliche Autor gesucht und bewiesen, dass es immer mehr als nur eine Möglichkeit gibt. Die Lösung für mich war sogar einfacher als die hier gegebenen Antworten, also dachte ich mir, ich könnte hinzufügen zu dem Haufen.

Die Motivation für die Bindung ist es, nett zu bleiben und MVVM. Die wahrscheinliche Verwendung des ViewModels besteht darin, eine Eigenschaft mit einem Namen wie “CurrentThingy” zu haben, und woanders ist der DataContext in einer anderen Sache an “CurrentThingy” gebunden.

Anstatt zusätzliche Schritte zu durchlaufen (zB: benutzerdefiniertes Verhalten, Kontrolle durch Dritte), um eine nette Bindung von der TreeView zu meinem Model und dann von etwas anderem zu meinem Model zu unterstützen, bestand meine Lösung darin, das einfache Element zu verwenden, das die andere Sache bindet TreeView.SelectedItem, anstatt die andere Sache an mein ViewModel zu binden, wodurch die zusätzliche Arbeit übersprungen wird.

XAML:

  .... stuff    

Natürlich ist das großartig, um das aktuell ausgewählte Element zu lesen, aber nicht einzustellen, was alles ist, was ich brauche.

Möglicherweise können Sie auch die TreeViewItem.IsSelected-Eigenschaft verwenden

Es gibt auch eine Möglichkeit, eine XAML-bindbare SelectedItem-Eigenschaft zu erstellen, ohne Interaction.Behaviors zu verwenden.

 public static class BindableSelectedItemHelper { #region Properties public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(BindableSelectedItemHelper), new FrameworkPropertyMetadata(null, OnSelectedItemPropertyChanged)); public static readonly DependencyProperty AttachProperty = DependencyProperty.RegisterAttached("Attach", typeof(bool), typeof(BindableSelectedItemHelper), new PropertyMetadata(false, Attach)); private static readonly DependencyProperty IsUpdatingProperty = DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), typeof(BindableSelectedItemHelper)); #endregion #region Implementation public static void SetAttach(DependencyObject dp, bool value) { dp.SetValue(AttachProperty, value); } public static bool GetAttach(DependencyObject dp) { return (bool)dp.GetValue(AttachProperty); } public static string GetSelectedItem(DependencyObject dp) { return (string)dp.GetValue(SelectedItemProperty); } public static void SetSelectedItem(DependencyObject dp, object value) { dp.SetValue(SelectedItemProperty, value); } private static bool GetIsUpdating(DependencyObject dp) { return (bool)dp.GetValue(IsUpdatingProperty); } private static void SetIsUpdating(DependencyObject dp, bool value) { dp.SetValue(IsUpdatingProperty, value); } private static void Attach(DependencyObject sender, DependencyPropertyChangedEventArgs e) { TreeListView treeListView = sender as TreeListView; if (treeListView != null) { if ((bool)e.OldValue) treeListView.SelectedItemChanged -= SelectedItemChanged; if ((bool)e.NewValue) treeListView.SelectedItemChanged += SelectedItemChanged; } } private static void OnSelectedItemPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { TreeListView treeListView = sender as TreeListView; if (treeListView != null) { treeListView.SelectedItemChanged -= SelectedItemChanged; if (!(bool)GetIsUpdating(treeListView)) { foreach (TreeViewItem item in treeListView.Items) { if (item == e.NewValue) { item.IsSelected = true; break; } else item.IsSelected = false; } } treeListView.SelectedItemChanged += SelectedItemChanged; } } private static void SelectedItemChanged(object sender, RoutedEventArgs e) { TreeListView treeListView = sender as TreeListView; if (treeListView != null) { SetIsUpdating(treeListView, true); SetSelectedItem(treeListView, treeListView.SelectedItem); SetIsUpdating(treeListView, false); } } #endregion } 

Sie können dies dann in Ihrem XAML wie folgt verwenden:

  

Ich habe alle Lösungen dieser Fragen versucht. Niemand hat mein Problem vollständig getriggers. Daher denke ich, dass es besser ist, eine solche geerbte class mit der neu definierten Eigenschaft SelectedItem zu verwenden. Es wird perfekt funktionieren, wenn Sie ein Baumelement aus der GUI auswählen und wenn Sie diesen Eigenschaftswert in Ihrem Code festlegen

 public class TreeViewEx : TreeView { public TreeViewEx() { this.SelectedItemChanged += new RoutedPropertyChangedEventHandler(TreeViewEx_SelectedItemChanged); } void TreeViewEx_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { this.SelectedItem = e.NewValue; } #region SelectedItem ///  /// Gets or Sets the SelectedItem possible Value of the TreeViewItem object. ///  public new object SelectedItem { get { return this.GetValue(TreeViewEx.SelectedItemProperty); } set { this.SetValue(TreeViewEx.SelectedItemProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public new static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(TreeViewEx), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedItemProperty_Changed)); static void SelectedItemProperty_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { TreeViewEx targetObject = dependencyObject as TreeViewEx; if (targetObject != null) { TreeViewItem tvi = targetObject.FindItemNode(targetObject.SelectedItem) as TreeViewItem; if (tvi != null) tvi.IsSelected = true; } } #endregion SelectedItem public TreeViewItem FindItemNode(object item) { TreeViewItem node = null; foreach (object data in this.Items) { node = this.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem; if (node != null) { if (data == item) break; node = FindItemNodeInChildren(node, item); if (node != null) break; } } return node; } protected TreeViewItem FindItemNodeInChildren(TreeViewItem parent, object item) { TreeViewItem node = null; bool isExpanded = parent.IsExpanded; if (!isExpanded) //Can't find child container unless the parent node is Expanded once { parent.IsExpanded = true; parent.UpdateLayout(); } foreach (object data in parent.Items) { node = parent.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem; if (data == item && node != null) break; node = FindItemNodeInChildren(node, item); if (node != null) break; } if (node == null && parent.IsExpanded != isExpanded) parent.IsExpanded = isExpanded; if (node != null) parent.IsExpanded = true; return node; } } 

Ich schlage eine Ergänzung zu dem von Steve Greaterrex gebotenen Verhalten vor. Sein Verhalten spiegelt keine Änderungen von der Quelle wider, da es sich möglicherweise nicht um eine Sammlung von TreeViewItems handelt. Es kommt also darauf an, das TreeViewItem in der Baumstruktur zu finden, dessen Datenkontext der selectedValue aus der Quelle ist. Die TreeView verfügt über eine geschützte Eigenschaft namens “ItemsHost”, die die TreeViewItem-Auflistung enthält. Wir können es durch reflection erhalten und den Baum auf der Suche nach dem ausgewählten Gegenstand gehen.

 private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var behavior = sender as BindableSelectedItemBehaviour; if (behavior == null) return; var tree = behavior.AssociatedObject; if (tree == null) return; if (e.NewValue == null) foreach (var item in tree.Items.OfType()) item.SetValue(TreeViewItem.IsSelectedProperty, false); var treeViewItem = e.NewValue as TreeViewItem; if (treeViewItem != null) { treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true); } else { var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (itemsHostProperty == null) return; var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel; if (itemsHost == null) return; foreach (var item in itemsHost.Children.OfType()) if (WalkTreeViewItem(item, e.NewValue)) break; } } public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue) { if (treeViewItem.DataContext == selectedValue) { treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true); treeViewItem.Focus(); return true; } foreach (var item in treeViewItem.Items.OfType()) if (WalkTreeViewItem(item, selectedValue)) return true; return false; } 

Auf diese Weise funktioniert das Verhalten für Zweiwege-Bindungen. Alternativ ist es möglich, die ItemsHost-Erfassung in die OnAttached-Methode von Behavior zu verschieben, wodurch der Aufwand für die Verwendung der Reflektion bei jeder Bindungsaktualisierung gespart wird.

WPF MVVM TreeView SelectedItem

… ist eine bessere Antwort, erwähnt aber keine Möglichkeit, das SelectedItem im ViewModel zu erhalten / setzen.

  1. Fügen Sie Ihrem ItemViewModel eine IsSelected boolean-Eigenschaft hinzu, und binden Sie in einem Style-Setter für das TreeViewItem an es.
  2. Fügen Sie Ihrem ViewModel eine SelectedItem -Eigenschaft hinzu, die als DataContext für die TreeView verwendet wird. Dies ist das fehlende Teil in der obigen Lösung.
     'GegenstandVM ...
     Öffentliche Eigenschaft wird als boolesch ausgewählt
         Bekommen
             Zurück _func.SelectedNode Ist ich
         Ende erhalten
         Set (Wert als boolesch)
             Wenn IsSelected-Wert Then
                 _func.SelectedNode = If (Wert, Ich, Nichts)
             Ende If
             RaisePropertyChange ()
         Ende festgelegt
     End-Eigenschaft
     'BaumVM ...
     Öffentliche Eigenschaft SelectedItem als ItemVM
         Bekommen
             Rückgabe _selectedItem
         Ende erhalten
         Set (Wert als ItemVM)
             Wenn _selectedItem ist Wert Then
                 Rückkehr
             Ende If
             Dim prev = _selectedItem
             _selectedItem = Wert
             Wenn prev isnot nichts dann
                 prev.IsSelected = False
             Ende If
             Wenn _selectedItem IsNot Nichts dann
                 _selectedItem.IsSelected = True
             Ende If
         Ende festgelegt
     End-Eigenschaft
           

Meine Anforderung war eine PRISM-MVVM-basierte Lösung, bei der ein TreeView benötigt wurde und das gebundene Objekt vom Typ Collection <> ist und daher HierarchicalDataTemplate benötigt. Das vorgegebene BindableSelectedItemBehavior kann das untergeordnete TreeViewItem nicht identifizieren. Um es in diesem Szenario zu arbeiten.

 public class BindableSelectedItemBehavior : Behavior { #region SelectedItem Property public object SelectedItem { get { return (object)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged)); private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var behavior = sender as BindableSelectedItemBehavior; if (behavior == null) return; var tree = behavior.AssociatedObject; if (tree == null) return; if (e.NewValue == null) foreach (var item in tree.Items.OfType()) item.SetValue(TreeViewItem.IsSelectedProperty, false); var treeViewItem = e.NewValue as TreeViewItem; if (treeViewItem != null) treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true); else { var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (itemsHostProperty == null) return; var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel; if (itemsHost == null) return; foreach (var item in itemsHost.Children.OfType()) { if (WalkTreeViewItem(item, e.NewValue)) break; } } } public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue) { if (treeViewItem.DataContext == selectedValue) { treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true); treeViewItem.Focus(); return true; } var itemsHostProperty = treeViewItem.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (itemsHostProperty == null) return false; var itemsHost = itemsHostProperty.GetValue(treeViewItem, null) as Panel; if (itemsHost == null) return false; foreach (var item in itemsHost.Children.OfType()) { if (WalkTreeViewItem(item, selectedValue)) break; } return false; } #endregion protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged; } protected override void OnDetaching() { base.OnDetaching(); if (this.AssociatedObject != null) { this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged; } } private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { this.SelectedItem = e.NewValue; } } 

Dies ermöglicht es, alle Elemente unabhängig von der Ebene zu durchlaufen.

Nachdem ich einen Tag lang das Internet studiert hatte, fand ich meine eigene Lösung für die Auswahl eines Elements nach dem Erstellen einer normalen Baumansicht in einer normalen WPF / C # -Umgebung

 private void BuildSortTree(int sel) { MergeSort.Items.Clear(); TreeViewItem itTemp = new TreeViewItem(); itTemp.Header = SortList[0]; MergeSort.Items.Add(itTemp); TreeViewItem prev; itTemp.IsExpanded = true; if (0 == sel) itTemp.IsSelected= true; prev = itTemp; for(int i = 1; i 

Dies kann auch mithilfe der IsSelected-Eigenschaft des TreeView-Elements erfolgen. So habe ich es geschafft,

 public delegate void TreeviewItemSelectedHandler(TreeViewItem item); public class TreeViewItem { public static event TreeviewItemSelectedHandler OnItemSelected = delegate { }; public bool IsSelected { get { return isSelected; } set { isSelected = value; if (value) OnItemSelected(this); } } } 

In dem ViewModel, das die Daten enthält, an die Ihr TreeView gebunden ist, abonnieren Sie einfach das Ereignis in der TreeViewItem-class.

 TreeViewItem.OnItemSelected += TreeViewItemSelected; 

Und schließlich implementieren Sie diesen Handler im selben ViewModel,

 private void TreeViewItemSelected(TreeViewItem item) { //Do something } 

Und die Bindung natürlich,

  

Ich bringe Ihnen meine Lösung mit folgenden Features:

  • Unterstützt 2-Wege-Bindung

  • Automatische Aktualisierung der TreeViewItem.IsSelected-Eigenschaften (gemäß SelectedItem)

  • Keine TreeView-Unterklasse

  • Elemente, die an ViewModel gebunden sind, können von jedem Typ sein (sogar null)

1 / Fügen Sie den folgenden Code in Ihrem CS ein:

 public class BindableSelectedItem { public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached( "SelectedItem", typeof(object), typeof(BindableSelectedItem), new PropertyMetadata(default(object), OnSelectedItemPropertyChangedCallback)); private static void OnSelectedItemPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { var treeView = d as TreeView; if (treeView != null) { BrowseTreeViewItems(treeView, tvi => { tvi.IsSelected = tvi.DataContext == e.NewValue; }); } else { throw new Exception("Attached property supports only TreeView"); } } public static void SetSelectedItem(DependencyObject element, object value) { element.SetValue(SelectedItemProperty, value); } public static object GetSelectedItem(DependencyObject element) { return element.GetValue(SelectedItemProperty); } public static void BrowseTreeViewItems(TreeView treeView, Action onBrowsedTreeViewItem) { var collectionsToVisit = new System.Collections.Generic.List> { new Tuple(treeView.ItemContainerGenerator, treeView.Items) }; var collectionIndex = 0; while (collectionIndex < collectionsToVisit.Count) { var itemContainerGenerator = collectionsToVisit[collectionIndex].Item1; var itemCollection = collectionsToVisit[collectionIndex].Item2; for (var i = 0; i < itemCollection.Count; i++) { var tvi = itemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; if (tvi == null) { continue; } if (tvi.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) { collectionsToVisit.Add(new Tuple(tvi.ItemContainerGenerator, tvi.Items)); } onBrowsedTreeViewItem(tvi); } collectionIndex++; } } } 

2/ Example of use in your XAML file

  

(Let’s just all agree that TreeView is obviously busted in respect to this problem. Binding to SelectedItem would have been obvious. Sigh )

I needed the solution to interact properly with the IsSelected property of TreeViewItem, so here’s how I did it:

 // the Type CustomThing needs to implement IsSelected with notification // for this to work. public class CustomTreeView : TreeView { public CustomThing SelectedCustomThing { get { return (CustomThing)GetValue(SelectedNode_Property); } set { SetValue(SelectedNode_Property, value); if(value != null) value.IsSelected = true; } } public static DependencyProperty SelectedNode_Property = DependencyProperty.Register( "SelectedCustomThing", typeof(CustomThing), typeof(CustomTreeView), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.None, SelectedNodeChanged)); public CustomTreeView(): base() { this.SelectedItemChanged += new RoutedPropertyChangedEventHandler(SelectedItemChanged_CustomHandler); } void SelectedItemChanged_CustomHandler(object sender, RoutedPropertyChangedEventArgs e) { SetValue(SelectedNode_Property, SelectedItem); } private static void SelectedNodeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var treeView = d as CustomTreeView; var newNode = e.NewValue as CustomThing; treeView.SelectedCustomThing = (CustomThing)e.NewValue; } } 

With this XAML:

      

If the XAML has

  

What’s wrong with just finding that item in the list? I also use

  

To make sure when I’m setting IsSelected = true in the VM the parents are expanded.