首页 文章

数据绑定到WPF Treeview中的SelectedItem

提问于
浏览
212

如何检索在WPF树视图中选择的项目?我想在XAML中这样做,因为我想绑定它 .

您可能认为它是 SelectedItem 但显然不存在是只读的,因此无法使用 .

这就是我想要做的:

<TreeView ItemsSource="{Binding Path=Model.Clusters}" 
            ItemTemplate="{StaticResource ClusterTemplate}"
            SelectedItem="{Binding Path=Model.SelectedCluster}" />

我想将 SelectedItem 绑定到我的Model上的属性 .

但这给了我错误:

'SelectedItem'属性是只读的,不能通过标记设置 .

Edit: 好的,这就是我解决这个问题的方法:

<TreeView
          ItemsSource="{Binding Path=Model.Clusters}" 
          ItemTemplate="{StaticResource HoofdCLusterTemplate}"
          SelectedItemChanged="TreeView_OnSelectedItemChanged" />

在我的xaml的codebehindfile中:

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

19 回答

  • 38

    一切都很复杂......跟Caliburn Micro一起去(http://caliburnmicro.codeplex.com/)

    视图:

    <TreeView Micro:Message.Attach="[Event SelectedItemChanged] = [Action SetSelectedItem($this.SelectedItem)]" />
    

    视图模型:

    public void SetSelectedItem(YourNodeViewModel item) {};
    
  • 2

    我意识到这已经接受了答案,但我把它放在一起解决问题 . 它使用与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>
    

    希望它会帮助别人!

  • 3

    此属性存在:TreeView.SelectedItem

    但它只是readonly,因此您无法通过绑定分配它,只能检索它

  • 3

    好吧,我找到了解决方案 . 它移动了混乱,以便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>
    
  • 1

    如果需要,请回答附加属性并且没有外部依赖性!

    您可以创建一个可绑定的附加属性,并具有一个getter和setter:

    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并按如下方式绑定(本地是我命名名称空间声明的方式):

    <TreeView ItemsSource="{Binding Path=Root.Children}" local:TreeViewHelper.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}">
    
        </TreeView>
    

    现在,您可以绑定所选项目,并在视图模型中将其设置为以编程方式更改它,如果该要求出现的话 . 当然,这是假设您在该特定属性上实现INotifyPropertyChanged .

  • 5

    它的回答比OP预期的要多一点......但我希望它至少可以帮助一些人 .

    如果要在 SelectedItem 更改时执行 ICommand ,则可以在事件上绑定命令,并且不再需要在 ViewModel 中使用属性 SelectedItem .

    为此:

    1-添加对 System.Windows.Interactivity 的引用

    xmlns: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>
    
  • 13

    这可以通过仅使用绑定和GalaSoft MVVM Light库的EventToCommand以更好的方式完成 . 在您的VM中添加一个命令,该命令将在更改所选项目时调用,并初始化该命令以执行必要的操作 . 在这个例子中,我使用了一个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; }
    }
    

    然后在xaml中添加EventToCommand行为 . 使用混合物非常容易 .

    <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>
    
  • 19

    我遇到了这个寻找与原作者相同答案的页面,并且证明总是有不止一种方法可以做到这一点,对我来说解决方案比到目前为止提供的答案更容易,所以我想我不妨添加到了一堆 .

    绑定的动机是保持它和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}" />
    

    当然,这非常适合阅读当前所选项目,但不能设置它,这就是我所需要的 .

  • 0

    您也可以使用TreeViewItem.IsSelected属性

  • 2

    还有一种方法可以在不使用Interaction.Behaviors的情况下创建XAML可绑定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}">
    
  • 1

    我试过这个问题的所有解决方案 . 没有人完全解决我的问题 . 所以我认为最好将这样的继承类与重新定义的属性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;
        }
    }
    
  • 1

    我建议增加Steve Greatrex提供的行为 . 他的行为并不反映来自源的更改,因为它可能不是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采集移动到Behavior的OnAttached方法,从而节省每次绑定时使用反射的开销更新 .

  • 8

    WPF MVVM TreeView SelectedItem

    ...是一个更好的答案,但没有提到在ViewModel中获取/设置SelectedItem的方法 .

    • 将IsSelected布尔属性添加到ItemViewModel,并在TreeViewItem的样式设置器中绑定它 .

    • 将SelectedItem属性添加到用作TreeView的DataContext的ViewModel . 这是上述解决方案中缺失的部分 .

    ' 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>
    
  • 0

    我的要求是基于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;
        }
    }
    

    这使得能够迭代所有元素而不管级别 .

  • 209

    在研究了互联网一天后,我在 normal WPF / C#环境中创建 normal 树视图后找到了自己选择项目的解决方案

    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 ;
                }
            }
    
  • 34

    它也可以使用TreeView项的IsSelected属性来完成 . 这是我管理它的方式,

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

    然后在包含TreeView绑定的数据的ViewModel中,只需订阅TreeViewItem类中的事件 .

    TreeViewItem.OnItemSelected += TreeViewItemSelected;
    

    最后,在同一个ViewModel中实现这个处理程序,

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

    而绑定当然,

    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
    
  • 2

    我为您带来了我的解决方案,它提供以下功能:

    • 支持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}" />
    
  • 18

    (让我们都同意TreeView在这个问题上明显被破坏 . 绑定到SelectedItem会很明显 . 叹气)

    我需要解决方案与TreeViewItem的IsSelected属性正确交互,所以这是我如何做到的:

    // 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>
    
  • 43

    如果XAML有

    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    

    在列表中找到该项目有什么问题?我也用

    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
    

    为了确保我在VM中设置IsSelected = true时父项被扩展 .

相关问题