我有一个大的switch语句,我根据XElement的输入值创建UIElements:
public static UIElement CreateElement(XElement element) {
var name = element.Attribute("Name").Value;
var text = element.Attribute("Value").Value;
var width = Convert.ToDouble(element.Attribute("Width").Value);
var height = Convert.ToDouble(element.Attribute("Height").Value);
//...
switch (element.Attribute("Type").Value) {
case "System.Windows.Forms.Label":
return new System.Windows.Controls.Label() {
Name = name,
Content = text,
Width = width,
Height = height
};
case "System.Windows.Forms.Button":
return new System.Windows.Controls.Button() {
Name = name,
Content = text,
Width = width,
Height = height
};
//...
default:
return null;
}
}
我正在创建这样的很多控件,正如您所看到的,重复过多。
有没有办法避免这种重复?提前感谢您的想法。
答案 0 :(得分:6)
您可以创建执行create:
的通用函数private static Create<T>(string name, string text, double width, double height) where T: Control, new()
{
return new T { Name = name, Content = text, Width = width, Height = height }
}
您的开关变为:
switch (element.Attribute("Type").Value) {
case "System.Windows.Forms.Label" : return Create<System.Windows.Forms.Label>(name, text, width, height);
etc.
}
您也可以根据自己的喜好调整此项以传递XElement。
如果Type属性始终是您想要的System.Type的名称,那么您可以执行
Control ctrl = (Control) Activator.CreateInstance(Type.GetType(element.Attribute("Type").Value));
ctrl.Name = name;
etc.
如果属性值与所需类型之间存在一对一映射,则可以使用映射声明只读静态字段:
private static readonly uiTypeMapping = new Dictionary<string,Type> {
{ "System.Windows.Forms.Label", typeof(System.Windows.Controls.Label) },
{ "System.Windows.Forms.Button", typeof(System.Windows.Controls.Button) },
{ etc. }
};
并使用
UIElement elem = (UIElement) Activator.CreateInstance(uiTypeMapping[element.Attribute("Type").Value]);
etc.
答案 1 :(得分:6)
像这样的东西可以起作用...... :)。
var controlCreators = new Dictionary<string, Func<ContentControl>>
{
{"System.Windows.Forms.Label", () => new Label()},
{"System.Windows.Forms.Button", () => new Button()}
};
Func<ContentControl> createControl;
if (!controlCreators.TryGetValue(element.Attribute("Type").Value, out createControl))
{
return null;
}
var control = createControl();
control.Name = name;
control.Content = text;
control.Width = width;
control.Height = height;
return control;
答案 2 :(得分:1)
那些不同的控件有继承树。例如,在FrameworkElement上定义了Width,Height,Name。所以你可以做类似以下的事情:
object createdObject = null;
switch (element.Attribute("Type").Value)
{
case "System.Windows.Forms.Label":
createdObject = new System.Windows.Controls.Label();
break;
case "System.Windows.Forms.Button":
createdObject = new System.Windows.Controls.Button();
break;
}
var fe = createdObject as FrameworkElement;
if (fe != null)
{
fe.Name = element.Attribute("Name").Value;
fe.Width = Convert.ToDouble(element.Attribute("Width").Value);
fe.Height = Convert.ToDouble(element.Attribute("Height").Value);
}
var ce = createdObject as ContentElement;
if (ce != null)
{
ce.Content = element.Attribute("Value").Value;
}
return createdObject;
请注意,通过使用这种方法,与Flynn的答案相比,您还可以轻松添加代码,例如“当控件是ItemsControl时,执行此操作”,即代码不适用于所有类型,但仅适用于其中一些。
答案 3 :(得分:1)
你可以用反射+表达式来完成。
[TestClass]
public class UnitTest1
{
public class Creator
{
private static Dictionary<string,Func<XElement, Control>> _map = new Dictionary<string, Func<XElement,Control>>();
public static Control Create(XElement element)
{
var create = GetCreator(element.Attribute("Type").Value);
return create(element);
}
private static Expression<Func<XElement, string>> CreateXmlAttributeAccessor(string elementName)
{
return (xl => xl.Attributes(elementName).Select(el => el.Value).FirstOrDefault() ?? "_" + elementName);
}
private static Func<XElement, Control> GetCreator(string typeName)
{
Func<XElement, Control> existing;
if (_map.TryGetValue(typeName, out existing))
return existing;
// mapping for whatever property names you wish
var propMapping = new[]
{
new{ Name = "Name", Getter = CreateXmlAttributeAccessor("Name") },
new{ Name = "Content", Getter = CreateXmlAttributeAccessor("Value") },
};
var t = Assembly.GetAssembly(typeof (Control)).GetType("System.Windows.Controls." + typeName);
var elementParameter = Expression.Parameter(typeof (XElement), "element");
var p = from propItem in propMapping
let member = t.GetMember(propItem.Name)
where member.Length != 0
select (MemberBinding)Expression.Bind(member[0], Expression.Invoke(propItem.Getter, elementParameter));
var expression = Expression.Lambda<Func<XElement, Control>>(
Expression.MemberInit(Expression.New(t),p), elementParameter);
existing = expression.Compile();
_map[typeName] = existing;
return existing;
}
}
[TestMethod]
public void TestMethod1()
{
var xel = new XElement("control",
new XAttribute("Type", "Button"),
new XAttribute("Name", "Foo"),
new XAttribute("Value", "Bar"),
new XElement("NonExistent", "foobar")); // To check stability
var button = (Button) Creator.Create(xel);
Assert.AreEqual("Foo", button.Name);
Assert.AreEqual("Bar", button.Content);
}
}
要使其与其他类型的字符串一起使用,您可以使用Expression.Convert。留下来作为练习。
答案 4 :(得分:0)
您可以使用反射代替,或者您可以创建字符串字典(您现在要切换的内容)和创建控件的Func(或操作)。
对于您发布的特定代码,您可以在switch语句后指定高度和宽度,因为它们直接在Control
上存在。