【问题标题】:Need To Hide A Designer-Only Property From PropertyGrid For A .NET Winforms Control需要从 .NET Winforms 控件的 PropertyGrid 中隐藏仅设计器的属性
【发布时间】:2020-01-29 01:56:52
【问题描述】:

我深入研究在我的 C#/.NET 解决方案中使用 Winforms 设计器(System.ComponentModel.Design 命名空间),以便我的用户可以在我正在运行的应用程序中访问表单设计器。其中大部分运行良好,但我遇到了一个非常具体的问题:我在 Microsoft 控件上遇到了一个仅在设计时出现的属性——即,对于控件的设计时实例。我想禁止该属性,以便用户在将该控件的实例放置在我的程序的 Winform 设计图面实现上时无法修改它。

详细信息:当用户将控件从工具箱拖放到设计器界面时,我确保选择了新添加的控件设计器实例(以便它显示调整大小句柄,因此属性网格会显示控件的设计时属性)。通过使用选择服务的 GetSelectedComponents() 方法并将属性网格的 SelectedObjects 属性分配给结果,我将设计器表面上的选定对象绑定到属性网格。

我的工具箱中的许多控件都是 .NET 控件。其中之一是 .NET Winforms TableLayoutPanel 控件。当您将该控件的实例放置在设计器图面上时,您将在绑定的 PropertyGrid 中看到一个 Columns 属性。我想禁止显示该属性,使其不会出现在 PropertyGrid 中。

问题是 TableLayoutPanel 类型的属性列表中似乎不存在此 Columns 属性 - 例如,使用 selectedComponents[0].GetType().GetProperties() 时不包含 属性。此外,我无法为现有的 Columns 属性创建新的或覆盖,因为它在设计时不会显示为 TableLayoutPanel 控件的公开属性 - 因此我不能用Browsable(false) 属性装饰它。

我似乎无法利用 PreFilterPropertiesPostFilterProperties,因为我无法交互和自定义 TableLayoutPanel 的设计器。

如何抑制 TableLayoutPanelColumns 设计器属性,使其不会出现在 PropertyGrid 中,而无需编写我的自己的设计师?

【问题讨论】:

  • 如果有帮助,属性名称是ColumnStylesColumns 是它的显示名称。
  • 哇,我没有想到这一点,我很惊讶微软决定将 ColumnStyles 公开为 ColumnStyles 以外的属性名称(我想知道他们为什么这样做?)这是我唯一的方法发现这将是浏览 .NET 源代码并查找 ColumnStyles 属性的显示名称属性?
  • PropertyGrid 始终显示由DisplayName 属性为属性设置的显示名称。它仅用于在 PropertyGrid、DataGridView 或使用该属性的任何其他组件/框架中显示。当您按 F12 或转到成员的定义时,VS 也会显示其属性。

标签: c# .net winforms windows-forms-designer design-surface


【解决方案1】:

如果您想避免自己写TableLayoutPanelDesigner,那么我可以建议一个解决方法。

ITypeDescriptorFilterService是负责调用设计器PreFilterProperties方法的接口。 DesignSurface 类有一个在其ServiceContainer 中注册的接口实现的实例。因此,您可以删除现有的注册实例并注册您自己的ITypeDescriptorFilterService 实现的新实例。

在新的实现中,覆盖FilterProperties并做任何你认为合适的事情,例如你可以检查组件的类型是否为TableLayoutPanel,然后不要调用它的设计者PreFilterProperties

然后要总结解决方案,您需要通过从 DesignSurface 类派生并删除已注册的 ITypeDescriptorFilterService 并注册您创建的所需实例来创建自己的设计图面。

示例

仅供参考,您要查找的属性名称为ColumnStyles,默认具有Browsable(false) 属性。但是TableLayoutPanel 的默认设计器将此属性替换为可浏览的版本。

我在这个例子中所做的是阻止设计者使这些属性可见。

首先提供ITypeDescriptorFilterService 的自定义实现。下面的实际上是System.Design 程序集中的现有实现,我已经更改了它的FilterProperties 方法并检查了组件是否为TableLayoutPanel,我要求什么都不做。

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class TypeDescriptorFilterService : ITypeDescriptorFilterService
{
    internal TypeDescriptorFilterService()
    {
    }

    private IDesigner GetDesigner(IComponent component)
    {
        ISite site = component.Site;
        if (site != null)
        {
            IDesignerHost service = site.GetService(typeof(IDesignerHost)) as IDesignerHost;
            if (service != null)
                return service.GetDesigner(component);
        }
        return (IDesigner)null;
    }

    bool ITypeDescriptorFilterService.FilterAttributes(IComponent component, IDictionary attributes)
    {
        if (component == null)
            throw new ArgumentNullException("component");
        if (attributes == null)
            throw new ArgumentNullException("attributes");
        IDesigner designer = this.GetDesigner(component);
        if (designer is IDesignerFilter)
        {
            ((IDesignerFilter)designer).PreFilterAttributes(attributes);
            ((IDesignerFilter)designer).PostFilterAttributes(attributes);
        }
        return designer != null;
    }

    bool ITypeDescriptorFilterService.FilterEvents(IComponent component, IDictionary events)
    {
        if (component == null)
            throw new ArgumentNullException("component");
        if (events == null)
            throw new ArgumentNullException("events");
        IDesigner designer = this.GetDesigner(component);
        if (designer is IDesignerFilter)
        {
            ((IDesignerFilter)designer).PreFilterEvents(events);
            ((IDesignerFilter)designer).PostFilterEvents(events);
        }
        return designer != null;
    }

    bool ITypeDescriptorFilterService.FilterProperties(IComponent component, IDictionary properties)
    {
        if (component == null)
            throw new ArgumentNullException("component");
        if (properties == null)
            throw new ArgumentNullException("properties");
        if (typeof(TableLayoutPanel).IsAssignableFrom(component.GetType()))
            return true;
        IDesigner designer = this.GetDesigner(component);
        if (designer is IDesignerFilter)
        {
            ((IDesignerFilter)designer).PreFilterProperties(properties);
            ((IDesignerFilter)designer).PostFilterProperties(properties);
        }
        return designer != null;
    }
}

然后通过从DesignSurface 派生来创建设计图面:

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class MyDesignSurface : DesignSurface
{
    public MyDesignSurface() : base()
    {
        this.ServiceContainer.RemoveService(typeof(ITypeDescriptorFilterService));
        this.ServiceContainer.AddService(typeof(ITypeDescriptorFilterService), new TypeDescriptorFilterService());
    }
}

然后例如以这种方式初始化设计表面:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        var surface = new MyDesignSurface();
        var host = (IDesignerHost)surface.GetService(typeof(IDesignerHost));
        var selectionService = (ISelectionService)surface.GetService(typeof(ISelectionService));
        surface.BeginLoad(typeof(Form));
        var root = (Form)host.RootComponent;
        var tableLayoutPanel1 = (Control)host.CreateComponent(typeof(TableLayoutPanel), "tableLayoutPanel1");
        root.Controls.Add(tableLayoutPanel1);
        var view = (Control)surface.View;
        view.Dock = DockStyle.Fill;
        this.Controls.Add(view);
        selectionService.SetSelectedComponents(new[] { tableLayoutPanel1 });
        var propertyGrid1 = new PropertyGrid() { Dock = DockStyle.Right, Width = 200 };
        this.Controls.Add(propertyGrid1);
        propertyGrid1.SelectedObjects = selectionService.GetSelectedComponents().Cast<object>().ToArray();
    }
}

然后运行您的设计器应用程序,您将看到 ColumnsRows 属性按预期隐藏。

您需要隐藏ColumnCountRowCount 属性以及分配给编辑/添加/删除列和行的动词。

【讨论】:

  • Reza,我有时间来解决这个问题(为延迟道歉):我已经正确连接了 FilterProperties,但我在上面的示例中没有看到 TableLayoutPanel Columns 和 Rows 属性是如何隐。这是在哪里做的?
  • 完全没问题。看FilterProperties中的这段代码:if (typeof(TableLayoutPanel).IsAssignableFrom(component.GetType())) return true;表示不运行PreFilterPropertiesPostFilterPropertiesIDesigner。但是我们为什么要这样做呢?因为您要查找的属性是ColumnStyles,默认情况下它具有Browsable(false) 属性。但是TableLayoutPanel 的默认设计器在TableLayoutDesignerPreFilterProperties 方法中将此属性替换为可浏览版本。
  • 谢谢你——我将很快发布一个相关的后续问题。
  • 我现在看到了这个逻辑(哇——在你进一步解释之前这并不明显)。再次感谢你。我现在发布了一个后续问题:stackoverflow.com/questions/58841974/…
猜你喜欢
  • 1970-01-01
  • 2011-12-20
  • 2013-09-26
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多