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

如何使用递归构建自定义控件?基于ViewModel的复杂控件开发问询

递归构建自定义控件的ViewModel实现方案

看起来你想要用递归逻辑在ViewModel里驱动复杂自定义控件的层级构建,刚好我之前做过类似的场景,给你一套完整的实现方案参考:

首先明确核心场景:基于Publisher(包含子Publisher集合和子Developer集合)和Developer两个类,通过ViewModel的递归逻辑,让UI自动渲染出多层级的控件结构。


第一步:完善支持递归的Model/ViewModel类

先把Publisher和Developer类补全,确保它们支持属性变更通知,并且Publisher的子集合能触发UI更新:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Publisher : INotifyPropertyChanged
{
    private ObservableCollection<Publisher> _subPublishers;
    private ObservableCollection<Developer> _childDevelopers;
    private string _name;

    public Publisher()
    {
        _subPublishers = new ObservableCollection<Publisher>();
        _childDevelopers = new ObservableCollection<Developer>();
        // 绑定集合变更事件,确保子集合变化时UI能感知
        _subPublishers.CollectionChanged += OnCollectionChanged;
        _childDevelopers.CollectionChanged += OnCollectionChanged;
    }

    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public ObservableCollection<Publisher> SubPublishers
    {
        get => _subPublishers;
        set
        {
            if (_subPublishers != value)
            {
                _subPublishers.CollectionChanged -= OnCollectionChanged;
                _subPublishers = value;
                _subPublishers.CollectionChanged += OnCollectionChanged;
                OnPropertyChanged();
            }
        }
    }

    public ObservableCollection<Developer> ChildDevelopers
    {
        get => _childDevelopers;
        set
        {
            if (_childDevelopers != value)
            {
                _childDevelopers.CollectionChanged -= OnCollectionChanged;
                _childDevelopers = value;
                _childDevelopers.CollectionChanged += OnCollectionChanged;
                OnPropertyChanged();
            }
        }
    }

    private void OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        // 集合变化时通知UI刷新相关属性
        OnPropertyChanged(nameof(SubPublishers));
        OnPropertyChanged(nameof(ChildDevelopers));
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Developer : INotifyPropertyChanged
{
    private string _name;
    private string _skill;

    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public string Skill
    {
        get => _skill;
        set
        {
            _skill = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

第二步:用递归逻辑构建层级数据

在主ViewModel里,编写递归方法生成多层级的Publisher结构,驱动UI的递归渲染:

public class MainViewModel : INotifyPropertyChanged
{
    private Publisher _rootPublisher;

    public Publisher RootPublisher
    {
        get => _rootPublisher;
        set
        {
            _rootPublisher = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        // 初始化时自动构建递归层级数据
        RootPublisher = BuildRecursivePublisherHierarchy();
    }

    // 递归构建Publisher层级的核心方法,注意设置终止条件避免无限递归
    private Publisher BuildRecursivePublisherHierarchy(int currentDepth = 0, int maxDepth = 2)
    {
        var publisher = new Publisher { Name = $"Publisher Level {currentDepth}" };

        // 给当前Publisher添加子开发者
        for (int i = 0; i < 2; i++)
        {
            publisher.ChildDevelopers.Add(new Developer 
            { 
                Name = $"Dev {i} under Publisher {currentDepth}", 
                Skill = currentDepth == 0 ? "Senior Engineer" : "Junior Engineer" 
            });
        }

        // 递归添加子Publisher,直到达到最大深度
        if (currentDepth < maxDepth)
        {
            for (int i = 0; i < 1; i++)
            {
                publisher.SubPublishers.Add(BuildRecursivePublisherHierarchy(currentDepth + 1, maxDepth));
            }
        }

        return publisher;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

第三步:自定义控件的递归UI实现

在XAML里用HierarchicalDataTemplate实现递归渲染,不需要手动写嵌套逻辑,模板会自动处理层级:

<UserControl x:Class="YourNamespace.RecursivePublisherControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:YourNamespace">
    <UserControl.Resources>
        <!-- 开发者项的渲染模板 -->
        <DataTemplate DataType="{x:Type local:Developer}">
            <StackPanel Orientation="Horizontal" Margin="4">
                <TextBlock Text="👨💻 " />
                <StackPanel>
                    <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
                    <TextBlock Text="{Binding Skill}" FontStyle="Italic" Foreground="#666"/>
                </StackPanel>
            </StackPanel>
        </DataTemplate>

        <!-- Publisher的递归模板,自动渲染子Publisher -->
        <HierarchicalDataTemplate DataType="{x:Type local:Publisher}"
                                  ItemsSource="{Binding SubPublishers}">
            <Expander Header="{Binding Name}" Margin="2" BorderThickness="1" BorderBrush="#EEE">
                <StackPanel>
                    <!-- 渲染当前Publisher下的所有开发者 -->
                    <ItemsControl ItemsSource="{Binding ChildDevelopers}" Margin="8,0"/>
                    <!-- 递归渲染子Publisher,HierarchicalDataTemplate会自动复用模板 -->
                    <ItemsControl ItemsSource="{Binding SubPublishers}" Margin="8,0"/>
                </StackPanel>
            </Expander>
        </HierarchicalDataTemplate>
    </UserControl.Resources>

    <!-- 控件主内容,绑定根Publisher的子集合 -->
    <ItemsControl ItemsSource="{Binding RootPublisher.SubPublishers}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</UserControl>

几个关键注意点

  • 递归终止条件:一定要在递归方法里设置明确的终止条件(比如示例里的maxDepth),不然会触发无限递归导致栈溢出。
  • 性能优化:如果层级很深或者数据量很大,记得把ItemsPanel换成VirtualizingStackPanel,开启UI虚拟化提升渲染性能。
  • 变更通知:所有属性和集合都要正确实现INotifyPropertyChanged和ObservableCollection,不然UI不会实时响应数据变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:23:10