简体   繁体   中英

How to create a custom control factory method

I have a skinnable Control library that was loads control settings/properties from external xml files. The Xml classes are in a seperate project as these will be used in a skin editor application, now the question, The controls accept an xml object in the constructor to build the Control but I need to find a nice way to create each control.

Xml class example:

[Serializable]
[XmlInclude(typeof(XmlButton))]
[XmlInclude(typeof(XmlGroup))]
[XmlType(TypeName="Control")]
public class XmlControl
{
    [DefaultValue(0)]
    public int Width { get; set; }

    [DefaultValue(0)]
    public int Height { get; set; }
 ...

and derived types per control type

[Serializable]
[XmlType(TypeName = "Button")]
public class XmlButton : XmlControl
{
    public string Label { get; set; }
}

Control classes

public class GUIControl : GUI3DBase
{
    public GUIControl(XmlControl skinXml)
    {
        SkinXml = skinXml;
...

public class GUIButton : GUIControl, IActionControl
{
    public GUIButton(XmlControl skinXml) : base(skinXml)
    {
    }
...

Now this is where I need help, at the moment I have a Method to create controls based on the xml object passed in.

    public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
    {
        if (skinXml is XmlButton)
        {
            return new GUIButton(skinXml);
        }
        else if  (skinXml is XmlGroup)
        {
            return new GUIGroup(skinXml);
        }
        ....

I have about 30 controls and the "if ladder" is growing fast and I feel like I am missing a simple way to create thes controls withou needing to check the xml object type then create the corresponding control type.

I can't add a Type property in the Xml object as that would create circular dependency.

Any help on a good factory method or new stucture layout would be awesome

Maybe IDictionary<Type, Func<XmlControl, GUIControl>> would help. Something like this:

private static Dictionary<Type, Func<XmlControl, GUIControl>> _dictionary = new Dictionary<Type, Func<XmlControl, GUIControl>>()
                                                                                    {
                                                                                        {typeof (XmlControlImpl), x => new GUIControl(x)},
                                                                                        {typeof (XmlGroup), x => new GUIGroup(x)},
                                                                                    }; 

public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
    Func<XmlControl, GUIControl> builder;
    if (!_dictionary.TryGetValue(typeof(T), out builder))
        throw new KeyNotFoundException("");

    return builder(skinXml);
}                            

Ok, I have found a way to do this with all your ideas and a little reflection, not sure if its the best way but it works nicly and adding a new skinnable control only requires a new xml object and an attribute on the control class.

Attribute Class

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class XmlControlTypeAttribute : Attribute
{
    protected Type xmlType;

    public XmlControlTypeAttribute(Type xmlType)
    {
        this.xmlType = xmlType;
    }

    public Type XmlType
    {
        get { return this.xmlType; }
    }
}

Control:

[XmlControlType(typeof(XmlButton))]
public class GUIButton : GUIControl, IActionControl
{
    public GUIButton(XmlControl skinXml) : base(skinXml)
    {
    }
    ....
}

Factory method:

public static GUIControl CreateControl2<T>(T skinXml) where T : XmlControl
{
    var controlType = Assembly.GetExecutingAssembly().DefinedTypes
        .Where(t => t.BaseType == typeof(GUIControl) && t.GetCustomAttribute<XmlControlTypeAttribute>().XmlType.Equals(typeof(T)))
        .FirstOrDefault();

    return (GUIControl)Activator.CreateInstance(controlType, new[] { skinXml }, null);
}

Thanks for all the ideas the helped heaps, I will leave this question open a bit longer incase somome has a better solution than this.

I would be tempted to add an abstract method to XmlControl :

public abstract class XmlControl
{
    [DefaultValue(0)]
    public int Width { get; set; }

    [DefaultValue(0)]
    public int Height { get; set; }

    public abstract Type ControlType();

override it in each implementation eg:

public class XmlButton : XmlControl
{
    public string Label { get; set; } 

    public override Type ControlType(){ return typeof(GUIButton); }
}

And then use reflection in the Factory method to construct the right class:

public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
    return (GUIControl)Activator.CreateInstance(skinXml.ControlType(),
                                     new[]{skinXml},null);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM