Привязка данных к SelectedItem в WPF Treeview
как я могу получить элемент, который выбран в WPF-treeview? Я хочу сделать это в XAML, потому что я хочу связать его.
вы можете подумать, что это SelectedItem но, видимо, что не существует только для чтения и поэтому непригодным.
вот что я хочу сделать:
<TreeView ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource ClusterTemplate}"
SelectedItem="{Binding Path=Model.SelectedCluster}" />
Я хочу связать SelectedItem к свойству на моей модели.
но это дает мне ошибку:
'SelectedItem' свойство только для чтения и не может быть установлен из разметки.
Edit:
Хорошо, вот как я решил это:
<TreeView
ItemsSource="{Binding Path=Model.Clusters}"
ItemTemplate="{StaticResource HoofdCLusterTemplate}"
SelectedItemChanged="TreeView_OnSelectedItemChanged" />
и в codebehindfile моего xaml:
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
Model.SelectedCluster = (Cluster)e.NewValue;
}
19 ответов:
Я понимаю, что это уже было принято ответ, но я собрал это вместе, чтобы решить проблему. Он использует аналогичную идею для решения Delta, но без необходимости подкласса TreeView:
public class BindableSelectedItemBehavior : Behavior<TreeView> { #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<object> e) { this.SelectedItem = e.NewValue; } }затем вы можете использовать это в своем XAML как:
<TreeView> <e:Interaction.Behaviors> <behaviours:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem, Mode=TwoWay}" /> </e:Interaction.Behaviors> </TreeView>надеюсь, это поможет кому-то!
отель : TreeView.Элемент
но это только для чтения, так что вы не можете назначить его через привязку, только получить его
Ну, я нашел решение. Он перемещает беспорядок, так что MVVM работает.
Сначала добавьте этот класс:
public class ExtendedTreeView : TreeView { public ExtendedTreeView() : base() { this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH); } void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> 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)); }и добавьте это в свой xaml:
<local:ExtendedTreeView ItemsSource="{Binding Items}" SelectedItem_="{Binding Item, Mode=TwoWay}"> ..... </local:ExtendedTreeView>
ответ с прикрепленными свойствами и без внешних зависимостей, если необходимость когда-либо возникнет!
Вы можете создать вложенное свойство, которое является привязываемым и есть геттер и сеттер:
public class TreeViewHelper { private static Dictionary<DependencyObject, TreeViewSelectedItemBehavior> behaviors = new Dictionary<DependencyObject, TreeViewSelectedItemBehavior>(); 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; } } }добавьте объявление пространства имен, содержащее этот класс, в свой XAML и свяжите его следующим образом (local-это то, как я назвал объявление пространства имен):
<TreeView ItemsSource="{Binding Path=Root.Children}" local:TreeViewHelper.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}"> </TreeView>теперь вы можете привязать выбранный элемент, а также установить его в модели представления, чтобы изменить его программно, следует это требование всегда возникало. Это, конечно, при условии, что вы реализуете INotifyPropertyChanged на этом конкретном свойстве.
он отвечает немного больше, чем ожидает ОП... Но я надеюсь, что это может помочь хотя бы кому-то.
если вы хотите выполнить
ICommand, когдаSelectedItemизменено, вы можете привязать команду к событию и использовать свойствоSelectedItemнаViewModelбольше не нужен.для этого:
1 - Добавить ссылку на
System.Windows.Interactivityxmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"2 - привязать команду к событию
SelectedItemChanged<TreeView x:Name="myTreeView" Margin="1" ItemsSource="{Binding Directories}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectedItemChanged"> <i:InvokeCommandAction Command="{Binding SomeCommand}" CommandParameter=" {Binding ElementName=myTreeView ,Path=SelectedItem}"/> </i:EventTrigger> </i:Interaction.Triggers> <TreeView.ItemTemplate> <!-- ... --> </TreeView.ItemTemplate> </TreeView>
Это можно сделать "более приятным"способом, используя только привязку и EventToCommand библиотеки GalaSoft MVVM Light. В вашей виртуальной машине добавьте команду, которая будет вызвана при изменении выбранного элемента, и инициализируйте команду для выполнения любых необходимых действий. В этом примере я использовал RelayCommand и просто установлю свойство SelectedCluster.
public class ViewModel { public ViewModel() { SelectedClusterChanged = new RelayCommand<Cluster>( c => SelectedCluster = c ); } public RelayCommand<Cluster> SelectedClusterChanged { get; private set; } public Cluster SelectedCluster { get; private set; } }затем добавьте поведение EventToCommand в свой xaml. Это очень легко с помощью смеси.
<TreeView x:Name="lstClusters" ItemsSource="{Binding Path=Model.Clusters}" ItemTemplate="{StaticResource HoofdCLusterTemplate}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectedItemChanged"> <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding SelectedClusterChanged}" CommandParameter="{Binding ElementName=lstClusters,Path=SelectedValue}"/> </i:EventTrigger> </i:Interaction.Triggers> </TreeView>
все сложнее... Перейти с Caliburn Micro (http://caliburnmicro.codeplex.com/)
View:
<TreeView Micro:Message.Attach="[Event SelectedItemChanged] = [Action SetSelectedItem($this.SelectedItem)]" />ViewModel:
public void SetSelectedItem(YourNodeViewModel item) {};
я наткнулся на эту страницу, ища тот же ответ, что и оригинальный автор, и доказывая, что всегда есть более чем один способ сделать это, решение для меня было еще проще, чем ответы, представленные здесь до сих пор, поэтому я решил, что могу также добавить в кучу.
мотивация для привязки заключается в том, чтобы сохранить его приятным & MVVM. Вероятное использование ViewModel должно иметь свойство с именем, таким как "CurrentThingy", и где-то еще, DataContext на какой-то другой вещи привязан "CurrentThingy".
вместо того, чтобы проходить дополнительные шаги, необходимые (например: пользовательское поведение, сторонний контроль) для поддержки хорошей привязки от TreeView к моей модели, а затем от чего-то еще к моей модели, мое решение состояло в том, чтобы использовать простой элемент, связывающий другую вещь с TreeView.SelectedItem, а не привязка другой вещи к моему ViewModel, тем самым пропуская дополнительную работу, необходимую.
XAML:
<TreeView x:Name="myTreeView" ItemsSource="{Binding MyThingyCollection}"> .... stuff </TreeView> <!-- then.. somewhere else where I want to see the currently selected TreeView item: --> <local:MyThingyDetailsView DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}" />конечно, это отлично подходит для чтения выбранный в данный момент элемент, но не установка его, что все, что мне нужно.
существует также способ создать свойство XAML Bindable SelectedItem без использования взаимодействия.Поведения.
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 }затем вы можете использовать это в своем XAML как:
<TreeView helper:BindableSelectedItemHelper.Attach="True" helper:BindableSelectedItemHelper.SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
Я перепробовал все решения этого вопроса. Никто не решил мою проблему полностью. Поэтому я думаю, что лучше использовать такой унаследованный класс с переопределенным свойством SelectedItem. Он будет работать отлично, если вы выберете элемент дерева из GUI и если вы установите это значение свойства в своем коде
public class TreeViewEx : TreeView { public TreeViewEx() { this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(TreeViewEx_SelectedItemChanged); } void TreeViewEx_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { this.SelectedItem = e.NewValue; } #region SelectedItem /// <summary> /// Gets or Sets the SelectedItem possible Value of the TreeViewItem object. /// </summary> 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; } }
Я предлагаю дополнение к поведению, предоставленному Стивом Грейтрексом. Его поведение не отражает изменения от источника, потому что это может быть не коллекция TreeViewItems. Таким образом, речь идет о поиске TreeViewItem в дереве, которое datacontext является selectedValue из источника. TreeView имеет защищенное свойство под названием "ItemsHost", которое содержит коллекцию TreeViewItem. Мы можем получить его через отражение и ходить по дереву в поисках выбранного элемента.
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<TreeViewItem>()) 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<TreeViewItem>()) 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<TreeViewItem>()) if (WalkTreeViewItem(item, selectedValue)) return true; return false; }таким образом, поведение работает для двухсторонних Привязок. Кроме того, можно переместить приобретение ItemsHost в метод Onattached поведения, экономя накладные расходы на использование отражения каждый раз при обновлении привязки.
WPF MVVM TreeView SelectedItem
... это лучший ответ, но не упоминает способ получить/установить SelectedItem в ViewModel.
- добавить IsSelected логическое свойство к ItemViewModel, и привязать к нему в установщик стиля для TreeViewItem.
- добавьте свойство SelectedItem в ViewModel, используемое в качестве DataContext для TreeView. Это недостающая часть в решении выше.
' ItemVM... Public Property IsSelected As Boolean Get Return _func.SelectedNode Is Me End Get Set(value As Boolean) If IsSelected value Then _func.SelectedNode = If(value, Me, Nothing) End If RaisePropertyChange() End Set End Property ' TreeVM... Public Property SelectedItem As ItemVM Get Return _selectedItem End Get Set(value As ItemVM) If _selectedItem Is value Then Return End If Dim prev = _selectedItem _selectedItem = value If prev IsNot Nothing Then prev.IsSelected = False End If If _selectedItem IsNot Nothing Then _selectedItem.IsSelected = True End If End Set End Property<TreeView ItemsSource="{Binding Path=TreeVM}" BorderBrush="Transparent"> <TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding IsExpanded}"/> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Name}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
мое требование было для решения на основе PRISM-MVVM, где был необходим TreeView, а Связанный объект имеет тип Collection и, следовательно, нуждается в HierarchicalDataTemplate. По умолчанию BindableSelectedItemBehavior не сможет идентифицировать дочерний TreeViewItem. Чтобы заставить его работать в этом случае.
public class BindableSelectedItemBehavior : Behavior<TreeView> { #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<TreeViewItem>()) 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<TreeViewItem>()) { 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<TreeViewItem>()) { 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<object> e) { this.SelectedItem = e.NewValue; } }Это позволяет перебрать все элементы, независимо от уровня.
после изучения интернета в течение дня я нашел свое собственное решение для выбора элемента после создания нормальный treeview в нормальный среда WPF/C#
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<SortList.Count; i++) { TreeViewItem itTempNEW = new TreeViewItem(); itTempNEW.Header = SortList[i]; prev.Items.Add(itTempNEW); itTempNEW.IsExpanded = true; if (i == sel) itTempNEW.IsSelected = true; prev = itTempNEW ; } }
Это также можно сделать с помощью свойства IsSelected элемента TreeView. Вот как мне это удалось,
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); } } }затем в ViewModel, который содержит данные, к которым привязан ваш TreeView, просто подпишитесь на событие в классе TreeViewItem.
TreeViewItem.OnItemSelected += TreeViewItemSelected;и, наконец, реализовать этот обработчик в том же ViewModel,
private void TreeViewItemSelected(TreeViewItem item) { //Do something }и переплет конечно,
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
Я приношу вам мое решение, которое предлагает следующие возможности:
поддерживает 2 способа привязки
автоматическое обновление TreeViewItem.IsSelected свойства (в соответствии с SelectedItem)
нет подклассов TreeView
элементы, привязанные к ViewModel, могут быть любого типа (даже null)
1 / вставьте следующий код в CS:
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<TreeViewItem> onBrowsedTreeViewItem) { var collectionsToVisit = new System.Collections.Generic.List<Tuple<ItemContainerGenerator, ItemCollection>> { new Tuple<ItemContainerGenerator, ItemCollection>(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<ItemContainerGenerator, ItemCollection>(tvi.ItemContainerGenerator, tvi.Items)); } onBrowsedTreeViewItem(tvi); } collectionIndex++; } } }2/ Пример использования в вашем файле XAML
<TreeView myNS:BindableSelectedItem.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" />
(давайте просто все согласимся, что TreeView явно сломан в отношении этой проблемы. Привязка к SelectedItem была бы очевидна. вздох)
мне нужно было решение, чтобы правильно взаимодействовать с IsSelected свойством TreeViewItem, поэтому вот как я это сделал:
// 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<object>(SelectedItemChanged_CustomHandler); } void SelectedItemChanged_CustomHandler(object sender, RoutedPropertyChangedEventArgs<object> 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; } }С этим XAML:
<local:CustonTreeView ItemsSource="{Binding TreeRoot}" SelectedCustomThing="{Binding SelectedNode,Mode=TwoWay}"> <TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> </Style> </TreeView.ItemContainerStyle> </local:CustonTreeView>
Если XAML имеет
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />Что плохого в том, чтобы найти этот пункт в список? Я также использую
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />чтобы убедиться, что когда я устанавливаю IsSelected = true в виртуальной машине, родители расширяются.
Comments