【问题标题】:Master/detail view using TreeView使用 TreeView 的主/详细视图
【发布时间】:2013-03-04 16:50:59
【问题描述】:

我正在使用 TreeView 和自定义详细信息视图控件在我的应用程序中实现主/详细信息视图。我也在尝试坚持 MVVM 模式。

现在 TreeView 绑定到包含所有详细信息的视图模型对象的集合,并且详细信息视图绑定到 TreeView 的选定项。

这很好用...直到其中一个 TreeView 节点有 5,000 个子节点并且应用程序突然占用了 500MB 的 RAM。

主窗口视图模型:

public class MainWindowViewModel
{
    private readonly List<ItemViewModel> rootItems;

    public List<ItemViewModel> RootItems { get { return rootItems; } } // TreeView is bound to this property.

    public MainWindowViewModel()
    {
        rootItems = GetRootItems();
    }

    // ...
}

项目视图模型:

public ItemViewModel
{
    private readonly ModelItem item; // Has a TON of properties
    private readonly List<ItemViewModel> children;

    public List<ItemViewModel> Children { get { return children; } }

    // ...
}

这是我绑定详细信息视图的方式:

<View:ItemDetails DataContext="{Binding SelectedItem, ElementName=ItemTreeView}" />

我对 WPF 和 MVVM 模式还很陌生,但我想将 TreeView 绑定到一个更小、更简化的对象的集合,该对象仅具有显示项目所需的属性(如名称和ID),然后一旦选择它就会加载所有详细信息。我将如何去做这样的事情?

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    概述

    应该只是将 TreeView 的 selected item 属性绑定到您的源中的某个东西的简单问题。但是,由于 TreeView 控件的构建方式,您必须使用开箱即用的 WPF 编写更多代码来获得对 MVVM 友好的解决方案。

    如果您使用的是普通 WPF(我假设您是),那么我建议您使用附加行为。附加的行为将绑定到主视图模型上的一个操作,该操作将在 TreeView 的选择更改时调用。您也可以调用命令而不是操作,但我将向您展示如何使用操作。

    基本上,总体思路是使用细节视图模型的一个实例,该实例将作为主视图模型的属性提供。然后,您可以使用轻量级对象,而不是拥有数百个视图模型实例的 RootItems 集合,这些对象仅具有节点的显示名称,并且可能在它们后面具有某种 id 字段。当 TreeView 上的选择发生变化时,您希望通过调用方法或设置属性来通知您的详细信息视图模型。在下面的演示代码中,我在 DetailsViewModel 上设置了一个名为 Selection 的属性。

    代码演练

    这是附加行为的代码:

    public static class TreeViewBehavior
    {
        public static readonly DependencyProperty SelectionChangedActionProperty =
            DependencyProperty.RegisterAttached("SelectionChangedAction", typeof (Action<object>), typeof (TreeViewBehavior), new PropertyMetadata(default(Action), OnSelectionChangedActionChanged));
    
        private static void OnSelectionChangedActionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var treeView = sender as TreeView;
            if (treeView == null) return;
    
            var action = GetSelectionChangedAction(treeView);
    
            if (action != null)
            {
                // Remove the next line if you don't want to invoke immediately.
                InvokeSelectionChangedAction(treeView);
                treeView.SelectedItemChanged += TreeViewOnSelectedItemChanged;
            }
            else
            {
                treeView.SelectedItemChanged -= TreeViewOnSelectedItemChanged;
            }
        }
    
        private static void TreeViewOnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            var treeView = sender as TreeView;
            if (treeView == null) return;
    
            InvokeSelectionChangedAction(treeView);
    
        }
    
        private static void InvokeSelectionChangedAction(TreeView treeView)
        {
            var action = GetSelectionChangedAction(treeView);
            if (action == null) return;
    
            var selectedItem = treeView.GetValue(TreeView.SelectedItemProperty);
    
            action(selectedItem);
        }
    
        public static void SetSelectionChangedAction(TreeView treeView, Action<object> value)
        {
            treeView.SetValue(SelectionChangedActionProperty, value);
        }
    
        public static Action<object> GetSelectionChangedAction(TreeView treeView)
        {
            return (Action<object>) treeView.GetValue(SelectionChangedActionProperty);
        }
    }
    

    然后,在 TreeView 元素的 XAML 中,应用以下内容:local:TreeViewBehavior.SelectionChangedAction="{Binding Path=SelectionChangedAction}"。请注意,您必须用 local 替换 TreeViewBehavior 类的命名空间。

    现在,将以下属性添加到您的 MainWindowViewModel:

    public Action<object> SelectionChangedAction { get; private set; } 
    public DetailsViewModel DetailsViewModel { get; private set; }
    

    在 MainWindowViewModel 的构造函数中,您需要将 SelectionChangedAction 属性设置为某个值。如果您的 DetailsViewModel 上有一个 Selection 属性,您可以使用 SelectionChangedAction = item =&gt; DetailsViewModel.Selection = item;。这完全取决于您。

    最后,在您的 XAML 中,将详细信息视图连接到其视图模型,如下所示:

    <View:ItemDetails DataContext="{Binding Path=DetailsViewModel}" />
    

    这是使用直接 WPF 的 MVVM 友好解决方案的基本架构。现在,话虽如此,如果您使用像 Caliburn.Micro 或 PRISM 这样的框架,您的方法可能与我在这里提供的不同。请记住这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-14
      • 1970-01-01
      • 2012-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多