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.