IExtenderProvider 根据对象类型仅添加一些属性 [英] IExtenderProvider add just some properties depending on object type

查看:39
本文介绍了IExtenderProvider 根据对象类型仅添加一些属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个问题,我不知道这是否确实可行(如果有一种hacky"方式,我全力以赴,但我还没有找到).

I'm having an issue and I don't know if this is indeed doable (if there's a "hacky" way, I'm all up for it, but I haven't found one).

我有一个 IExtenderProvider 组件,我用它来拥有自己的 UITypeEditor 第三方控件上的某些属性(我无法更改,因为很明显原因).

I have an IExtenderProvider component that I'm using to have my own UITypeEditor for some properties on third-party controls (which I can't change, for obvious reasons).

这些控件不一定从同一个基类继承(如果它们继承了,基类不一定具有我想要扩展的属性,而这些属性是在同一个类中定义的).

These controls don't necessarily inherit from the same base (and if they do, the base doesn't necessarily have the properties I want to extend, and those are defined in the same class).

因此,假设例如我想为属性 ImageGlyphLargeGlyphSmallGlyph<创建一个替代属性/code> 在他们身上.

So, imagine for example I want to make an alternative property for the properties Image, Glyph, LargeGlyph, SmallGlyph on them.

所以我有类似的东西:

[ProvideProperty("LargeGlyphCustom", typeof (object))]
[ProvideProperty("GlyphCustom", typeof(object))]
[ProvideProperty("SmallImageCustom", typeof(object))]
[ProvideProperty("LargeImageCustom", typeof(object))]
[ProvideProperty("ImageCustom", typeof(object))]
public class MyImageExtender : Component, IExtenderProvider
{
  private readonly Type[] _extendedTypes =
  {
    typeof (OtherControl),
    typeof (SomeOtherControl),
    typeof (AControl),
    typeof (AButton)
  };

  bool IExtenderProvider.CanExtend(object o)
  {
    if (!DesignMode) return false;
    return _extendedTypes.Any(t => t.IsInstanceOfType(o));
  } 

  // Implement the property setter and getter methods
}

到目前为止,一切都很好.我可以在我期望的类型的控件上看到我的属性.

So far, so good. I can see my properties on the controls of the types I'm expecting.

但是,这些是控件中属性的替换(只是为了更改UITypeEditor).

However, these are replacements (just to change the UITypeEditor) of properties in the control.

我的方法的问题是我在所有扩展类型中看到了所有扩展属性.

The problem with my approach is that I see all of the extended properties in all of the extended types.

说,如果AButton只有Image,我只想看到ImageCustom而不是SmallImageCustomLargeImageCustom

Say, if AButton only has Image, I only want to see ImageCustom and not SmallImageCustom, LargeImageCustom, etc.

所以我的方法是这样做:

So my approach was to do this:

[ProvideProperty("LargeGlyphCustom", typeof (OtherControl))]
// other properties
[ProvideProperty("ImageCustom", typeof(AButton))]
public class MyImageExtender : Component, IExtenderProvider
// ...

这似乎工作正常,现在我只看到 AButton 上的 ImageCustomOtherControl 上的 LargeGlyphCustom.

This seemed to work fine, and now I only see ImageCustom on AButton, and LargeGlyphCustom on OtherControl.

现在的问题是,如果我想同时在 AButtonOtherControl 中显示 ImageCustom,我曾想过这样做:

Now the problem is, if I want to show ImageCustom in both AButton and OtherControl, I had thought of doing this:

[ProvideProperty("ImageCustom", typeof(AButton))]
[ProvideProperty("ImageCustom", typeof(OtherControl))]
public class MyImageExtender : Component, IExtenderProvider

但这不起作用,我只能在 AButton 上看到 ImageCustom,而在 OtherControl 上看不到.

This doesn't work though, I only get to see ImageCustom on AButton, but not on OtherControl.

反编译ProvidePropertyAttribute 的源代码,发生这种情况的原因可以说"很清楚.它在内部创建了一个 TypeId,我怀疑是 WinForms 设计者使用的,如下所示:

Decompiling the sources for ProvidePropertyAttribute, the reason this happens is "arguably" clear. It internally creates a TypeId, which I suspect is what the WinForms designer is using like this:

public override object TypeId
{
  get
  {
    return (object) (this.GetType().FullName + this.propertyName);
  }
}

这使得TypeId为"ProvidePropertyAttributeImageCustom",因此无法区分不同的接收器类型.

Which makes the TypeId be "ProvidePropertyAttributeImageCustom", so it can't differentiate between the different receiver types.

我将测试派生 ProvidePropertyAttribute 并创建一个不同的 TypeId,因为它似乎可以覆盖,但我希望 winforms 设计人员希望特定的 ProvidePropertyAttribute 类型而不是派生的类型(winforms 设计人员对这些东西很挑剔).

I'm going to test deriving ProvidePropertyAttribute and create a different TypeId since it seems overridable, but I expect the winforms designer expect the specific ProvidePropertyAttribute type and not a derived one (the winforms designer is picky with these things).

哎呀,ProvidePropertyAttributesealed 所以我无法派生并制作我的自定义 TypeId,这似乎(不是我寄予厚望)这会起作用)

Ouch, ProvidePropertyAttribute is sealed so I can't derive and make my custom TypeId, it seems (not that I had high hopes that this would work at all)

与此同时,有没有人做过这样的事情并且知道我可以使用的东西?

In the meantime, anyone has ever done something like this and know something I could use?

推荐答案

我知道这是一个快速的答案,但这已经让我发疯了好几天,所以我走了一条似乎有效的不同路线很好.

I know this is a quick answer, but this has been driving me nuts for a few days, so I've gone a different route which seems to work just fine.

由于目标目标(正如我在我的问题中所解释的)是更改某些属性上的 UITypeEditor,因此我制作了一个覆盖属性的非可视组件(使用自定义 TypeDescriptor) 在这些属性上,并在那里分配我的自定义 UITypeEditor.

Since the target goal (as I explained on my question) was to change the UITypeEditor on some properties, I've made a non-visual component that overrides the attributes (using a custom TypeDescriptor) on those properties, and assign my custom UITypeEditor there.

我使用这个答案作为实现覆盖属性的TypeDescriptor的基础.

I used this answer as a base for implementing the property-overriding TypeDescriptor.

作为记录,链接答案中提供的解决方案有效,但是存在一个问题,即 TypeDescriptionProvider 会被派生类选取,但是返回的 TypeDescriptor只会返回基础对象的属性(您在父 TypeDescriptor 中传递的属性),从而导致诸如 winforms 设计器之类的东西的破坏.

For the record, the solution provided in the linked answer worked, however it had a problem where the TypeDescriptionProvider would get picked up for derived classes, however the returned TypeDescriptor would only return the properties for the base object (the one for which you passed in the parent TypeDescriptor), causing havok in things like the winforms designer.

我制作了一个通用的属性覆盖TypeDescriptionProvider.到目前为止,它运行得很好.这是实现.请参阅链接答案以了解其来源:

I made an all purpose property-overrider TypeDescriptionProvider. So far, it has worked just fine. Here's the implementation. See the linked answer for an explanation of where did this come from:

  1. 提供者:

  1. The provider:

internal class PropertyOverridingTypeDescriptionProvider : TypeDescriptionProvider
{
    private readonly Dictionary<Type, ICustomTypeDescriptor> _descriptorCache = new Dictionary<Type, ICustomTypeDescriptor>();
    private readonly Func<PropertyDescriptor, bool> _condition;
    private readonly Func<PropertyDescriptor, Type, PropertyDescriptor> _propertyCreator;

    public PropertyOverridingTypeDescriptionProvider(TypeDescriptionProvider parentProvider, Func<PropertyDescriptor, bool> condition, Func<PropertyDescriptor, Type, PropertyDescriptor> propertyCreator) : base(parentProvider)
    {
        _condition = condition;
        _propertyCreator = propertyCreator;
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {
        lock (_descriptorCache)
        {
            ICustomTypeDescriptor returnDescriptor;
            if (!_descriptorCache.TryGetValue(objectType, out returnDescriptor))
            {
                returnDescriptor = CreateTypeDescriptor(objectType);
            }
            return returnDescriptor;
        }
    }

    private ICustomTypeDescriptor CreateTypeDescriptor(Type targetType)
    {
        var descriptor = base.GetTypeDescriptor(targetType, null);
        _descriptorCache.Add(targetType, descriptor);
        var ctd = new PropertyOverridingTypeDescriptor(descriptor, targetType, _condition, _propertyCreator);
        _descriptorCache[targetType] = ctd;
        return ctd;
    }
}

  • 这是实际的TypeDescriptor:

    internal class PropertyOverridingTypeDescriptor : CustomTypeDescriptor
    {
        private readonly ICustomTypeDescriptor _parent;
        private readonly PropertyDescriptorCollection _propertyCollection;
        private readonly Type _objectType;
        private readonly Func<PropertyDescriptor, bool> _condition;
        private readonly Func<PropertyDescriptor, Type, PropertyDescriptor> _propertyCreator;
    
        public PropertyOverridingTypeDescriptor(ICustomTypeDescriptor parent, Type objectType, Func<PropertyDescriptor, bool> condition, Func<PropertyDescriptor, Type, PropertyDescriptor> propertyCreator)
            : base(parent)
        {
            _parent = parent;
            _objectType = objectType;
            _condition = condition;
            _propertyCreator = propertyCreator;
            _propertyCollection = BuildPropertyCollection();
        }
    
        private PropertyDescriptorCollection BuildPropertyCollection()
        {
            var isChanged = false;
            var parentProperties = _parent.GetProperties();
    
            var pdl = new PropertyDescriptor[parentProperties.Count];
            var index = 0;
            foreach (var pd in parentProperties.OfType<PropertyDescriptor>())
            {
                var pdReplaced = pd;
                if (_condition(pd))
                {
                    pdReplaced = _propertyCreator(pd, _objectType);
                }
                if (!ReferenceEquals(pdReplaced, pd)) isChanged = true;
                pdl[index++] = pdReplaced;
            }
            return !isChanged ? parentProperties : new PropertyDescriptorCollection(pdl);
        }
    
        public override object GetPropertyOwner(PropertyDescriptor pd)
        {
            var o = base.GetPropertyOwner(pd);
            return o ?? this;
        }
    
        public override PropertyDescriptorCollection GetProperties()
        {
            return _propertyCollection;
        }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return _propertyCollection;
        }
    }
    

  • 这是你如何使用它.我已经评论过了:

    And here's how you use it. I've commented this:

    private void ChangeTypeProperties(Type modifiedType, params string[] propertyNames)
    {
        // Get the current TypeDescriptionProvider
        var curProvider = TypeDescriptor.GetProvider(modifiedType);
        // Create a replacement provider, pass in the parent, this is important
        var replaceProvider = new PropertyOverridingTypeDescriptionProvider(curProvider,
            // This the predicate that says wether a `PropertyDescriptor` should be changed
            // Here we are changing only the System.Drawing.Image properties,
            // either those whose name we pass in, or all if we pass none
            pd =>
                typeof (System.Drawing.Image).IsAssignableFrom(pd.PropertyType) &&
                (propertyNames.Length == 0 || propertyNames.Contains(pd.Name)),
    
            // This our "replacer" function. It'll get the source PropertyDescriptor and the object type.
            // You could use pd.ComponentType for the object type, but I've
            // found it to fail under some circumstances, so I just pass it
            // along
            (pd, t) =>
            {
                // Get original attributes except the ones we want to change
                var atts = pd.Attributes.OfType<Attribute>().Where(x => x.GetType() != typeof (EditorAttribute)).ToList();
                // Add our own attributes
                atts.Add(new EditorAttribute(typeof (MyOwnEditor), typeof (System.Drawing.Design.UITypeEditor)));
                // Create the new PropertyDescriptor
                return TypeDescriptor.CreateProperty(t, pd, atts.ToArray());
            }
        );
        // Finally we replace the TypeDescriptionProvider
        TypeDescriptor.AddProvider(replaceProvider, modifiedType);
    }
    

    现在,根据我的问题的要求,我创建了一个简单的插入式组件,将其放置在基本表单上,它的作用是:

    Now, for the requirements of my question, I've created a simple drop-in component which I drop on the base form, which does just this:

    public class ToolbarImageEditorExtender : Component
    {
        private static bool _alreadyInitialized;
        public ToolbarImageEditorExtender()
        {
            // no need to reinitialize if we drop more than one component
            if (_alreadyInitialized)
                return;
            _alreadyInitialized = true;
            // the ChangeTypeProperties function above. I just made a generic version
            ChangeTypeProperties<OtherControl>(nameof(OtherControl.Glyph), nameof(OtherControl.LargeGlyph));
            ChangeTypeProperties<AButton>(nameof(AButton.SmallImage), nameof(AButton.LargeImage));
            // etc.
        }
    }
    

    到目前为止,它已经产生了奇迹.

    So far, it has worked wonders.

    这篇关于IExtenderProvider 根据对象类型仅添加一些属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆