如何创建自定义控件工厂方法

时间:2013-01-09 10:34:30

标签: c# factory

我有一个可换肤的控件库,它从外部xml文件加载控件设置/属性。 Xml类在一个单独的项目中,因为它们将在皮肤编辑器应用程序中使用,现在问题是,控件接受构造函数中的xml对象来构建Control,但我需要找到一个创建每个控件的好方法。

Xml类示例:

[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; }
 ...

和每个控件类型的派生类型

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

控制类

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

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

现在这是我需要帮助的地方,目前我有一个方法来根据传入的xml对象创建控件。

    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);
        }
        ....

我有大约30个控件,“if ladder”正在快速增长,我觉得我缺少一个简单的方法来创建控件,而不需要检查xml对象类型,然后创建相应的控件类型。

我无法在Xml对象中添加Type属性,因为这会创建循环依赖。

对良好工厂方法或新结构布局的任何帮助都很棒

3 个答案:

答案 0 :(得分:2)

也许IDictionary<Type, Func<XmlControl, GUIControl>>会有所帮助。像这样:

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);
}                            

答案 1 :(得分:2)

好的,我已经找到了一种方法来完成所有的想法和一些反思,不确定它是否是最好的方式,但它工作得很好并且添加一个新的skinnable控件只需要一个新的xml对象和一个属性控制班。

属性类

[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; }
    }
}

控制:

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

工厂方法:

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);
}

感谢帮助堆积的所有想法,我会稍微延长这个问题,因为有更好的解决方案比这更好。

答案 2 :(得分:1)

我很想将一个抽象方法添加到XmlControl

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

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

    public abstract Type ControlType();

在每个实现中覆盖它,例如:

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

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

然后在Factory方法中使用反射来构造正确的类:

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