254

如何检索在 WPF 树视图中选择的项目?我想在 XAML 中执行此操作,因为我想绑定它。

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

这就是我想要做的:

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

我想将 绑定SelectedItem到我的模型上的属性。

但这给了我错误:

“SelectedItem”属性是只读的,不能从标记中设置。

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

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

在我的 xaml 的代码隐藏文件中:

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

20 回答 20

266

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

希望它会帮助别人!

于 2011-02-25T14:29:47.633 回答
46

此属性存在:TreeView.SelectedItem

但它是只读的,所以你不能通过绑定来分配它,只能检索它

于 2009-06-16T08:04:28.467 回答
39

好吧,我找到了解决方案。它移动了混乱,使 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>
于 2010-08-20T21:43:03.857 回答
30

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

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

为此:

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>
于 2016-03-09T09:57:22.360 回答
20

这可以通过仅使用绑定和 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 行为。使用 blend 真的很容易。

<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>
于 2011-02-04T14:15:19.370 回答
12

一切都变得复杂......使用 Caliburn Micro (http://caliburnmicro.codeplex.com/)

看法:

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

视图模型:

public void SetSelectedItem(YourNodeViewModel item) {}; 
于 2011-07-20T20:27:34.377 回答
9

我在此页面上寻找与原作者相同的答案,并证明总是有不止一种方法可以做到这一点,对我来说,解决方案比迄今为止提供的答案更容易,所以我想我不妨添加到堆。

绑定的动机是让它保持良好和 MVVM。ViewModel 的可能用法是拥有一个带有名称的属性,例如“CurrentThingy”,而在其他地方,其他事物上的 DataContext 绑定到“CurrentThingy”。

而不是通过所需的额外步骤(例如:自定义行为,第 3 方控制)来支持从 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}" />

当然,这对于读取当前选择的项目非常有用,但不是设置它,这就是我所需要的。

于 2011-06-13T04:48:04.487 回答
6

您也许还可以使用 TreeViewItem.IsSelected 属性

于 2010-08-10T09:36:43.280 回答
3

还有一种方法可以在不使用 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}">
于 2013-05-30T15:05:21.640 回答
3

我尝试了这个问题的所有解决方案。没有人完全解决我的问题。所以我认为最好使用带有重新定义属性 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;
    }
} 
于 2013-08-16T03:59:06.357 回答
3

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

这使得无论级别如何都可以遍历所有元素。

于 2015-10-20T09:39:29.733 回答
2

我建议对 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 方法,从而节省每次绑定更新时使用反射的开销。

于 2011-07-12T01:38:48.643 回答
2

WPF MVVM TreeView SelectedItem

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

  1. 将 IsSelected 布尔属性添加到您的 ItemViewModel,并在 TreeViewItem 的样式设置器中绑定到它。
  2. 将 SelectedItem 属性添加到用作 TreeView 的 DataContext 的 ViewModel。这是上述解决方案中缺少的部分。
    ' 项目虚拟机...
    公共属性被选为布尔值
        得到
            返回 _func.SelectedNode 是我
        结束获取
        设置(值作为布尔值)
            If IsSelected 值 Then
                _func.SelectedNode = If(value, Me, Nothing)
            万一
            提高属性更改()
        结束集
    结束属性
    ' 树虚拟机...
    公共属性 SelectedItem 作为 ItemVM
        得到
            返回 _selectedItem
        结束获取
        设置(值作为 ItemVM)
            如果 _selectedItem 是值 那么
                返回
            万一
            暗淡上一个 = _selectedItem
            _selectedItem = 值
            If prev IsNot Nothing Then
                上一页.IsSelected = False
            万一
            If _selectedItem IsNot Nothing Then
                _selectedItem.IsSelected = True
            万一
        结束集
    结束属性
<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>
于 2015-05-26T14:34:08.410 回答
1

在网上研究了一天后,我找到了自己在普通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 ;
            }
        }
于 2013-04-19T14:38:22.023 回答
1

也可以使用 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}" />    
于 2015-02-13T16:10:42.823 回答
1

我知道这个线程已有 10 年历史,但问题仍然存在......

最初的问题是“检索”所选项目。我还需要在我的视图模型中“获取”选定的项目(而不是设置它)。在此线程中的所有答案中,“Wes”的答案是唯一一个以不同方式解决问题的答案:如果您可以将“选定项目”用作数据绑定的目标,请将其用作数据绑定的源。Wes 对另一个视图属性执行此操作,我将对视图模型属性执行此操作:

我们需要两件事:

  • 在视图模型中创建一个依赖属性(在我的“MyObject”类型的情况下,因为我的树视图绑定到“MyObject”类型的对象)
  • 在 View 的构造函数中从 Treeview.SelectedItem 绑定到此属性(是的,这是后面的代码,但您很可能也会在那里初始化数据上下文)

视图模型:

public static readonly DependencyProperty SelectedTreeViewItemProperty = DependencyProperty.Register("SelectedTreeViewItem", typeof(MyObject), typeof(MyViewModel), new PropertyMetadata(OnSelectedTreeViewItemChanged));

    private static void OnSelectedTreeViewItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as MyViewModel).OnSelectedTreeViewItemChanged(e);
    }

    private void OnSelectedTreeViewItemChanged(DependencyPropertyChangedEventArgs e)
    {
        //do your stuff here
    }

    public MyObject SelectedWorkOrderTreeViewItem
    {
        get { return (MyObject)GetValue(SelectedTreeViewItemProperty); }
        set { SetValue(SelectedTreeViewItemProperty, value); }
    }

查看构造函数:

Binding binding = new Binding("SelectedItem")
        {
            Source = treeView, //name of tree view in xaml
            Mode = BindingMode.OneWay
        };

        BindingOperations.SetBinding(DataContext, MyViewModel.SelectedTreeViewItemProperty, binding);
于 2019-07-05T11:30:59.507 回答
1

我提出了这个解决方案(我认为这是最简单且无内存泄漏的),它非常适合从 View 的选定项更新 ViewModel 的选定项。

请注意,从 ViewModel 更改所选项目不会更新视图的所选项目。

public class TreeViewEx : TreeView
{
    public static readonly DependencyProperty SelectedItemExProperty = DependencyProperty.Register("SelectedItemEx", typeof(object), typeof(TreeViewEx), new FrameworkPropertyMetadata(default(object))
    {
        BindsTwoWayByDefault = true // Required in order to avoid setting the "BindingMode" from the XAML
    });

    public object SelectedItemEx
    {
        get => GetValue(SelectedItemExProperty);
        set => SetValue(SelectedItemExProperty, value);
    }

    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
    {
        SelectedItemEx = e.NewValue;
    }
}

XAML 使用

<l:TreeViewEx ItemsSource="{Binding Path=Items}" SelectedItemEx="{Binding Path=SelectedItem}" >
于 2020-01-04T01:07:19.773 回答
0

(让我们都同意 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>
于 2018-02-15T02:36:41.400 回答
0

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

  • 支持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}" />  
于 2018-05-24T14:27:11.537 回答
0

我意识到自发布以来已经有一段时间了,但是 FWIW 我正在使用 Telerik 的 RadTreeView,并且 SelectedItem 似乎工作正常 - 在此期间问题已经解决,或者 Telerik 已经为我们解决了这个问题。

于 2021-11-09T15:43:24.697 回答