如何在 PropertyGrid 自定义 collection 编辑器中自定义 "Add" 按钮下拉列表中的名称

How to customize names in "Add" button dropdown in the PropertyGrid custom collection editor

我正在使用 PropertyGrid 编辑 collection。带有 collection 的 object 定义如下:

class ObjWithCollection
{
    [Editor(typeof(MyCustomCollectionEditor),typeof(UITypeEditor))]
    public List<ItemBase> collection { get; set; } = new List<ItemBase>();//ItemBase is abstract
}

collection包含两种类型的object,派生自ItemBaseclass:Item1 项目 2。这些 class 定义如下:

public abstract class ItemBase
{
    public string Name { get; set; }
    public ItemBase() { }
    public ItemBase(string name) { Name = name; }
}

public class Item1:ItemBase
{
    public Item1():base("Item 1 name"){}
}

[DisplayName("item2 test display name")]
public class Item2:ItemBase
{
    public Item2() : base("item 2 name") { }
}

为了能够通过编辑器向 collection 添加新项目,我还定义了自定义 collection 编辑器 class 并覆盖 CreateNewItemTypes 列出适合 collection 的所有类型:

class MyCustomCollectionEditor : CollectionEditor
{
    public MyCustomCollectionEditor(Type type) : base(type){}
    protected override Type[] CreateNewItemTypes()
    {
        return new Type[] { typeof(Item1), typeof(Item2) };
    }
}

然后我将自定义编辑器绑定到 ObjWithCollection.collection 属性 和 Editor 属性(参见 ObjWithCollection.collection 定义).

这很好用,我可以编辑我的 collection,包括添加新项目。添加按钮有一个下拉菜单,允许用户 select 要添加的元素类型。 editor window http://i.share.pho.to/31d50d09_o.png

但是在“添加”按钮下拉列表中,名为“Item1”和“Item2”的项目我无法更改这些名称。我尝试了 DisplayName 属性,ToString 覆盖,但没有成功。

所以,问题是如何输出 Add 按钮菜单元素的自定义名称。

我认为这不可能直接从 属性 网格的代码中实现。但是,您可以使用 TypeDelegator 来欺骗系统并强制它使用例如您的 DisplayName 属性来代替它默认使用的类型名称 属性。

1) 创建自定义 TypeDelegator,如下所示:

class MyTypeDelegator : TypeDelegator
{
    public MyTypeDelegator(Type delegatingType)
        : base(delegatingType)
    {
    }

    public override string Name
    {
        get
        {
            var dna = (DisplayNameAttribute)typeImpl.GetCustomAttribute(typeof(DisplayNameAttribute));
            return dna != null ? dna.DisplayName : typeImpl.Name;
        }
    }
}

2) 像这样修改 CreateNewItemTypes():

    protected override Type[] CreateNewItemTypes()
    {
        return new Type[] { new MyTypeDelegator(typeof(Item1)), new MyTypeDelegator(typeof(Item2)) };
    }

现在,您应该在菜单中看到显示名称而不是名称。

您还可以通过连接到 CollectionForm 的控件来更改文本(丑陋,不推荐,请勿在生产中使用,请注意它可能随时更改和过时)

 public sealed class OutputItemEditor : CollectionEditor // need a reference to System.Design.dll
{
    public OutputItemEditor(Type type)
        : base(type)
    {
    }

    protected override Type[] CreateNewItemTypes()
    {
        return new[] { typeof(StaticOutputItem), typeof(VariableOutputItem),typeof(ExpressionOutputItem) };
    }

    protected override CollectionForm CreateCollectionForm()
    {
        CollectionForm collectionForm = base.CreateCollectionForm();            
        collectionForm.Text = "Output Collection";

        //Modify the Add Item Button Text
        try
        {
            //You can use "collectionForm.Controls.Find("addButton",true)" here also
            foreach (ToolStripItem item in collectionForm.Controls[0].Controls[1].Controls[0].ContextMenuStrip.Items)
            {
                //Since Item Names are the Type Names
                switch (item.Text)
                {
                    case "StaticOutputItem":
                        item.Text = "Static Output Item";
                        break;
                    case "VariableOutputItem":
                        item.Text = "Variable Output Item";
                        break;
                    case "ExpressionOutputItem":
                        item.Text = "Expression Output Item";
                        break;
                    default:
                        break;
                }
            }
        }
        catch (Exception)
        {
        }

        return collectionForm;
    }
}