Selecting static fields via dropdown at Designtime

2019-09-10 11:05发布

问题:

Is it possible to modify a ComponentConverter, Typeconverter,... so I can select "Components" that are in a static class outside the form?

When I create a custom control and add a property of type Component or Control I get a dropdown menu in designer where I can select one ot the existing ones.

The reference to the selected object is automatically added in the designer code.

Problem is, that the dropdown only lets me select components that were declared in this form or a form that this form inherits, but no other form or a static class.

But I want to have one specific file in my solution where I can declare all these components once, an then use them in each form (I have several in my project) at designtime.

When I add the reference to the static objects myself in designer code, everything works fine, but this is not comfortable, because I have to alter the code manually.

Would it be possible to modify a Converter or UITypeEditor, so it can select static objects and then pass them to the designer?

I tried to override a ComponentConverter, but in the designer code it only writes "null" instead of the object:

class MyConverter : ComponentConverter
{
    public MyConverter(Type type) : base(type)
    {
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        return new StandardValuesCollection(new List<BOOL>() { Variablen.M0_2, Variablen.M0_3, Variablen.M0_4 });
    }
}

回答1:

After some work, I got this working, but stability is not the best. Sometimes after compiling I get errors, because context or service is null in the TypeConverter

Base-class:

[TypeConverter(typeof(MyReferenceTypeonverter))]
[DesignerSerializer(typeof(MyObjectSerializer), typeof(CodeDomSerializer))]
public abstract class MyReferencableObject
{
    internal FieldInfo FieldInfo { get; set; }

    //Could also use: FieldInfo.DeclaringType.FullName + "." + FieldInfo.Name;
    public string FullName { get { return FieldInfo.DeclaringType.Name + "." + FieldInfo.Name; } }
}

Serializer:

public class MyObjectSerializer : CodeDomSerializer
{
    public override object Serialize(IDesignerSerializationManager manager, object value)
    {
        return new CodeVariableReferenceExpression((value as MyReferencableObject).FullName);
    }
}`

Container

 /// <summary>
/// Prototype for class that whrer MyReferencableObject are declared
/// </summary>
public abstract class Deklaration
{

}

TypeConverter

public class MyReferenceTypeonverter : TypeConverter
{
    private Type ContainerType = typeof(Deklaration);

    private const string empty = "(none)";


    Dictionary<string, MyReferencableObject> values;

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        string FullName = (string)value;

        if (FullName == empty)
            return null;

        InitDict(context);

        if (values.ContainsKey(FullName))
            return values[FullName];

        return value;
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (value == null)
            return empty;

        InitDict(context);

        foreach (string key in values.Keys)
            if (key != empty)
                if (values[key].FullName == ((MyReferencableObject)value).FullName)
                    return key;

        //sometimes happens...
        return "ERROR!!!";
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        InitDict(context);
        return new StandardValuesCollection(values.Values);
    }

    void InitDict(ITypeDescriptorContext context)
    {
        //sometimes happens...
        if (context == null)
            return;

        Dictionary<string, MyReferencableObject> tvalues = new Dictionary<string, MyReferencableObject>();
        tvalues.Add(empty, null);

        ITypeDiscoveryService Service = (ITypeDiscoveryService)context.GetService(typeof(ITypeDiscoveryService));

        //sometimes happens...
        if (Service == null)
            return;

        foreach (Type declaration in Service.GetTypes(ContainerType, false))
            foreach (FieldInfo fieldInfo in declaration.GetFields())
                if (context.PropertyDescriptor.PropertyType.IsAssignableFrom(fieldInfo.FieldType))
                {
                    MyReferencableObject var = (MyReferencableObject)fieldInfo.GetValue(null);
                    var.FieldInfo = fieldInfo;
                    tvalues.Add(var.FullName, var);
                }

        //in a perfect world the if should not be necessary....
        if (tvalues.Count > 1 || values == null)
            values = tvalues;
    }

    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        return TypeDescriptor.GetProperties(value, attributes);
    }
}