You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Caliburn Micro在WPF MVVM中创建带动态嵌套数据的TreeView

WPF MVVM TreeView with Caliburn Micro (Dynamic/Nested Data)

Let's walk through the complete implementation to get your dynamic nested TreeView up and running with Caliburn Micro.


1. Complete the ViewModel Layer

First, let's finish the ShellViewModel (including the missing BuildTree logic) and add support for item selection. We'll also include a sample data structure to mimic your server response.

using Caliburn.Micro;
using System.Collections.ObjectModel;
using System.Linq;

namespace YourNamespace
{
    public class ShellViewModel : Screen // Inherit from Screen for Caliburn's lifecycle support
    {
        // Root collection bound to the TreeView via Caliburn convention
        public ObservableCollection<TreeItem> Root { get; } = new ObservableCollection<TreeItem>();

        // Track selected TreeView item
        private TreeItem _selectedTreeItem;
        public TreeItem SelectedTreeItem
        {
            get => _selectedTreeItem;
            set
            {
                _selectedTreeItem = value;
                NotifyOfPropertyChange(() => SelectedTreeItem);
                // Add custom logic here when selection changes (e.g., load details)
            }
        }

        public ShellViewModel()
        {
            // Replace this with your actual server.GetData() call
            var serverData = GetSampleServerData();
            BuildTree(serverData);
        }

        // Build nested TreeItem hierarchy from server data
        private void BuildTree(System.Collections.Generic.List<SampleServerData> serverData)
        {
            // Get top-level items (no parent)
            var rootLevelItems = serverData.Where(d => d.ParentId == null).ToList();
            
            foreach (var rootData in rootLevelItems)
            {
                var treeRoot = new TreeItem { Name = rootData.Name };
                // Recursively add child items
                AddChildItems(treeRoot, serverData);
                Root.Add(treeRoot);
            }
        }

        // Recursive method to populate child items
        private void AddChildItems(TreeItem parentItem, System.Collections.Generic.List<SampleServerData> serverData)
        {
            // Get all children for the current parent (adjust filter to match your data's parent-child relationship)
            var childData = serverData.Where(d => d.ParentId == parentItem.Name).ToList();
            
            foreach (var childDataItem in childData)
            {
                var childTreeItem = new TreeItem { Name = childDataItem.Name };
                // Recurse to add grandchildren
                AddChildItems(childTreeItem, serverData);
                parentItem.Children.Add(childTreeItem);
            }
        }

        // Optional: Handle double-click on TreeView items
        public void OnTreeItemDoubleClick(TreeItem clickedItem)
        {
            // Add your custom action here (e.g., open a detail view)
            System.Windows.MessageBox.Show($"You double-clicked: {clickedItem.Name}");
        }

        // Sample server data structure (replace with your actual server model)
        private System.Collections.Generic.List<SampleServerData> GetSampleServerData()
        {
            return new System.Collections.Generic.List<SampleServerData>
            {
                new SampleServerData { Name = "Project A", ParentId = null },
                new SampleServerData { Name = "Module 1", ParentId = "Project A" },
                new SampleServerData { Name = "Submodule 1.1", ParentId = "Module 1" },
                new SampleServerData { Name = "Module 2", ParentId = "Project A" },
                new SampleServerData { Name = "Project B", ParentId = null },
                new SampleServerData { Name = "Component X", ParentId = "Project B" }
            };
        }
    }

    // Your existing TreeItem model (already correctly implemented!)
    public class TreeItem : PropertyChangedBase
    {
        private string name;
        public string Name
        {
            get => name;
            set { name = value; NotifyOfPropertyChange(() => Name); }
        }
        public ObservableCollection<TreeItem> Children { get; } = new ObservableCollection<TreeItem>();
    }

    // Sample server data model (adjust to match your actual server response)
    public class SampleServerData
    {
        public string Name { get; set; }
        public string ParentId { get; set; }
    }
}

2. Create the View (ShellView.xaml)

Next, define the TreeView in your view with a HierarchicalDataTemplate to handle nested items. We'll use Caliburn Micro's convention-based binding and add support for double-click actions.

<UserControl x:Class="YourNamespace.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org"
             xmlns:local="clr-namespace:YourNamespace">

    <Grid Margin="10">
        <!-- TreeView binds to the "Root" collection automatically via Caliburn's naming convention -->
        <TreeView x:Name="Root"
                  SelectedItem="{Binding SelectedTreeItem, Mode=TwoWay}"
                  BorderThickness="1"
                  BorderBrush="#DDD">

            <!-- Hierarchical template to render TreeItem and its nested children -->
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type local:TreeItem}"
                                          ItemsSource="{Binding Children}">
                    <!-- TextBlock with double-click action bound to ViewModel method -->
                    <TextBlock Text="{Binding Name}"
                               Padding="2"
                               cal:Message.Attach="[Event MouseDoubleClick] = [Action OnTreeItemDoubleClick($dataContext)]"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</UserControl>

Key Notes & Features

  • Dynamic Updates: Since TreeItem.Children is an ObservableCollection, any runtime changes (adding/removing items) will automatically reflect in the TreeView.
  • Caliburn Conventions: The TreeView's name Root matches the ViewModel's Root property, so Caliburn handles the binding automatically—no need for explicit ItemsSource="{Binding Root}".
  • Selection Handling: The SelectedTreeItem property in the ViewModel stays in sync with the TreeView's selected item via two-way binding.
  • Recursive Hierarchy: The BuildTree and AddChildItems methods recursively construct the nested tree from your server data (adjust the parent-child filtering logic to match your actual data structure).

内容的提问来源于stack exchange,提问作者MagB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:01:31