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

WinForms自定义UserControl集合属性编辑后设计器UI不更新求助

WinForms自定义控件:集合编辑后设计器即时刷新UI方案

问题场景

我正在开发名为SidebarControl的WinForms自定义UserControl,它包含List<SidebarMenuItem>类型的MenuItems属性,通过自定义SidebarMenuItemCollectionEditor在Visual Studio设计器中编辑。

属性定义:

[Editor(typeof(SidebarMenuItemCollectionEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<SidebarMenuItem> MenuItems { get; set; }

在集合编辑器中重写SetItems方法,尝试修改集合后重建菜单UI:

protected override object SetItems(object editValue, object[] value)
{
    var updated = base.SetItems(editValue, value);

    if (Context?.Instance is SidebarControl sidebar)
    {
        sidebar.RebuildMenuFromItems(); // 基于MenuItems向面板添加按钮
    }

    return updated;
}

// SidebarControl中的UI重建方法
public void RebuildMenuFromItems()
{
    if (pnlMenubar == null)
        return;

    pnlMenubar.Controls.Clear();

    foreach (var item in MenuItems)
    {
        AddMenuItemControl(item);
    }

    this.Refresh();
    this.Invalidate();
}

当前问题:修改集合后,必须关闭并重新打开主窗体才能看到UI变化,需要实现设计器中即时刷新。

解决方案

1. 通知设计器属性已变更

在SidebarControl的RebuildMenuFromItems方法末尾添加设计模式下的变更通知,通过IDesignerHost告知设计器MenuItems属性已修改:

public void RebuildMenuFromItems()
{
    if (pnlMenubar == null)
        return;

    pnlMenubar.Controls.Clear();

    foreach (var item in MenuItems)
    {
        AddMenuItemControl(item);
    }

    this.Refresh();
    this.Invalidate();

    // 设计模式下触发属性变更通知
    if (DesignMode)
    {
        var designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));
        if (designerHost != null)
        {
            var menuItemsProp = TypeDescriptor.GetProperties(this)["MenuItems"];
            designerHost.RaiseComponentChanged(this, menuItemsProp, null, MenuItems);
        }
    }
}

2. 在集合编辑器中触发组件变更通知

在SetItems方法中,通过IComponentChangeService直接通知设计器组件已发生变化:

protected override object SetItems(object editValue, object[] value)
{
    var updated = base.SetItems(editValue, value);

    if (Context?.Instance is SidebarControl sidebar)
    {
        sidebar.RebuildMenuFromItems();
        
        // 通知设计器控件已修改
        if (Context is ITypeDescriptorContext typeContext)
        {
            var changeService = (IComponentChangeService)typeContext.GetService(typeof(IComponentChangeService));
            changeService?.OnComponentChanged(sidebar, null, null, null);
        }
    }

    return updated;
}

3. 优化设计模式下的重绘逻辑

在RebuildMenuFromItems中增强重绘操作,确保控件和子容器都能即时更新:

public void RebuildMenuFromItems()
{
    if (pnlMenubar == null)
        return;

    pnlMenubar.SuspendLayout();
    pnlMenubar.Controls.Clear();

    foreach (var item in MenuItems)
    {
        AddMenuItemControl(item);
    }

    pnlMenubar.ResumeLayout();
    pnlMenubar.PerformLayout();
    
    this.Invalidate();
    this.Update();
    
    if (DesignMode)
    {
        this.PerformLayout();
        // 若父容器存在也可触发刷新
        Parent?.Invalidate();
    }
}

4. 改用BindingList实现自动变更通知(推荐)

将List<SidebarMenuItem>替换为BindingList<SidebarMenuItem>,它内置集合变更事件,设计器能自动感知集合的增删改操作,无需手动触发通知:

[Editor(typeof(SidebarMenuItemCollectionEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public BindingList<SidebarMenuItem> MenuItems { get; set; } = new BindingList<SidebarMenuItem>();

结合RebuildMenuFromItems方法,集合修改后UI会即时在设计器中更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 13:43:19