如何为闭源类型的所有属性注入自定义UITypeEditor?

9
我希望避免在我为其编写了自定义UITypeEditor的某种类型的每个实例上放置EditorAttribute。
我无法在类型上放置EditorAttribute,因为我无法修改源代码。
我有一个引用指向唯一将被使用的PropertyGrid实例。
我能否告诉PropertyGrid实例(或所有实例)在遇到特定类型时使用自定义UITypeEditor?
这里是一个MSDN文章(链接),提供了在.NET 2.0或更高版本中如何执行此操作的起点。
3个回答

24

您通常可以通过TypeDescriptor.AddAttributes在运行时关联编辑器等。例如(Bar属性应显示为带有“…”的形式,并显示“Editing!”):

using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;

class Foo
{
    public Foo() { Bar = new Bar(); }
    public Bar Bar { get; set; }
}
class Bar
{
    public string Value { get; set; }
}

class BarEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        MessageBox.Show("Editing!");
        return base.EditValue(context, provider, value);
    }
}
static class Program
{
    [STAThread]
    static void Main()
    {
        TypeDescriptor.AddAttributes(typeof(Bar),
            new EditorAttribute(typeof(BarEditor), typeof(UITypeEditor)));
        Application.EnableVisualStyles();
        Application.Run(new Form { Controls = { new PropertyGrid { SelectedObject = new Foo() } } });
    }
}

Marc,做得好。我本来会建议创建自定义类型描述符并发布提供程序,但你的方法是在幕后注册提供程序和注入编辑器的好捷径!学到了东西。 - Nicolas Cadilhac
哇,实际操作中非常简单。谢谢! - Cat Zimmermann
太好了!我正在开发一个绘图库,并希望为对象提供PropertyGrid编辑器支持,而不需要从对象库中依赖Windows Forms来装饰属性。这个解决方案允许我在核心库之外创建编辑器,并在运行时添加它们。 - Michael A. McCloskey

3

马克的解决方案直接将EditorAttribute全局应用于Bar类型。如果您有娇气的性格,可能更喜欢注释特定实例的属性。不幸的是,这不可能使用TypeDescriptor.AddAttributes。

我的解决方案是编写一个包装器ViewModel<T>,它从T复制属性,并用额外的属性进行注释。假设我们有一个Report类型的变量“datum”,我们将像这样使用它

        var pretty = ViewModel<Report>.DressUp(datum);
        pretty.PropertyAttributeReplacements[typeof(Smiley)] = new List<Attribute>() { new EditorAttribute(typeof(SmileyEditor),typeof(UITypeEditor))};
        propertyGrid1.SelectedObject = pretty;

ViewModel<T> 的定义位置:

public class ViewModel<T> : CustomTypeDescriptor
{
    private T _instance;
    private ICustomTypeDescriptor _originalDescriptor;
    public ViewModel(T instance, ICustomTypeDescriptor originalDescriptor) : base(originalDescriptor)
    {
        _instance = instance;
        _originalDescriptor = originalDescriptor;
        PropertyAttributeReplacements = new Dictionary<Type,IList<Attribute>>();
    }

    public static ViewModel<T> DressUp(T instance)
    {
        return new ViewModel<T>(instance, TypeDescriptor.GetProvider(instance).GetTypeDescriptor(instance));
    }

    /// <summary>
    /// Most useful for changing EditorAttribute and TypeConvertorAttribute
    /// </summary>
    public IDictionary<Type,IList<Attribute>> PropertyAttributeReplacements {get; set; } 

    public override PropertyDescriptorCollection GetProperties (Attribute[] attributes)
    {
        var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>();

        var bettered = properties.Select(pd =>
            {
                if (PropertyAttributeReplacements.ContainsKey(pd.PropertyType))
                {
                    return TypeDescriptor.CreateProperty(typeof(T), pd, PropertyAttributeReplacements[pd.PropertyType].ToArray());
                }
                else
                {
                    return pd;
                }
            });
        return new PropertyDescriptorCollection(bettered.ToArray());
    }

    public override PropertyDescriptorCollection GetProperties()
    {
        return GetProperties(null);
    }
}

如上所定义,这将替换特定类型的属性,但如果您需要更高的分辨率,则可以按名称替换属性。


既然决定这太麻烦了,而采用更简单的全局解决方案。 - Colonel Panic
回到使用这个,但是警告一下,它可能与实现ICustomTypeDescriptor接口的对象不兼容。 - Colonel Panic

0

2
很好的想法,但我忘了提到我无法在类型上放置EditorAttribute,因为我无法修改源代码。 - Cat Zimmermann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接