在之前的一篇文章中提到了,在MVC中我需要动态生成表单,而这个表单是根据自定义的表单设计器设计出来的。详见:ASP.NET MVC 2生成动态表单的一种最简单的思路。这篇文章,我将演示一下我开发的一个简易的WPF表单设计器,以及这样设计的思路。
这样设计的思路很简单:由WPF设计器产生一个XML文件,然后将这个XML文件保存起来,然后在Asp.net MVC项目根据条件查找出XML表单,随后将它解析成HTML。
这种方式的优点是:由于有一个WPF的客户端设计器,用户在制作表单的时候就会比较方便。不会存在浏览器上设计表单的诸多问题。
这种方式的缺点是:设计出来的表单是XML的,还需要转换成HTML,转换和定位都非常麻烦。
看下那篇文章的XML结构:
<?xml version="1.0" encoding="utf-8" ?> <form name="form1"> <field type="text" name ="firstname" class ="textbox" left="300" top="200">朱</field> <field type="text" name ="lastname" class ="textbox" left="700" top="200">祁林</field> <field type="text" name ="sex" class ="textbox" left="300" top="240">男</field> <field type="text" name ="age" class ="textbox" left="700" top="240">24</field> </form>
这个文件中的div使用了绝对布局,这样的方式固然问题多多,但是在我还没想到更好的办法之前,我还是使用这种方式去生产表单,以便能继续下去。其实使用table就可以用不着div的绝对布局了,只是我还没有找到在WPF设计器上画Table的方法。
下面看下这种思路的一个简单的Demo:在WPF设计器中设计一个登陆界面表单,然后在MVC中解析这个表单。
我实现的这个WPF表单设计器的界面如下图,有点类似VS。上边是菜单,左边是工具栏,中间是设计区,右边是属性框。
通过拖拽设计一个登陆界面:
使用下面代码,将界面上的元素转化成XML。
public XElement SerializeDesignerItems(IEnumerable<DesignerItem> designerItems) { string contentXaml; XElement serializedItem = null; XElement serializedItems = new XElement("Form"); foreach (DesignerItem item in designerItems) { contentXaml = XamlWriter.Save(((DesignerItem)item).Content); XElement XElementName = null; XElement XElementType = null; XElement XElementValue = null; object obj = ((DesignerItem)item).Content; switch (obj.GetType().Name) { case "Button": XElementName = new XElement("Name",(obj as Button).Name); XElementType = new XElement("Type", "button"); XElementValue = new XElement("Value", (obj as Button).Content); break; case "ComboBox": XElementName = new XElement("Name",(obj as ComboBox).Name); XElementType = new XElement("Type", "select"); string value = ""; foreach (ComboBoxItem cb in (obj as ComboBox).Items) { value = value+":"+cb.Content; } value = value.Substring(1, value.Length - 1); XElementValue = new XElement("Value", value); break; case "Label": XElementName = new XElement("Name",(obj as Label).Name); XElementType = new XElement("Type", "label"); XElementValue = new XElement("Value", (obj as Label).Content); break; case "TextBox": XElementName = new XElement("Name",(obj as TextBox).Name); XElementType = new XElement("Type", "text"); XElementValue = new XElement("Value", (obj as TextBox).Text); break; case "RadioButton": XElementName =new XElement("Name",(obj as RadioButton).Name); XElementType = new XElement("Type", "radio"); XElementValue = new XElement("Value", (obj as RadioButton).Content); break; case "CheckBox": XElementName =new XElement("Name",(obj as CheckBox).Name); XElementType = new XElement("Type", "checkbox"); XElementValue = new XElement("Value", (obj as CheckBox).Content); break; } XElement XElementLeft = new XElement("Left", Canvas.GetLeft(item)+100); XElement XElementTop = new XElement("Top", Canvas.GetTop(item)+100); XElement XElementWidth = new XElement("Width", item.Width); XElement XElementHeight = new XElement("Height", item.Height); XElement XElementContent = new XElement("Content", contentXaml); serializedItem = new XElement("field", XElementName,XElementType,XElementValue, XElementLeft, XElementTop, XElementWidth, XElementHeight, XElementContent); serializedItems.Add(serializedItem); } return serializedItems; }
在Asp.net MVC项目中使用下面代码将XML转换成HTML:
<% XElement xml = (XElement)ViewData["xml"]; %> <%Html.BeginForm(); %> <% StringBuilder sb = new StringBuilder(); string label = " <div style=\"left: {0}px; position: absolute; top: {1}px\">{2}</div>"; string input = "<input name=\"{0}\" type=\"{1}\" style=\"left: {2}px; position: absolute; top: {3}px;width:{4}px; height:{5}px\" value=\"{6}\"></input>"; string checkbox = "<input name=\"{0}\" type=\"{1}\" style=\"left: {2}px; position: absolute; top: {3}px;width:{4}px; height:{5}px\" ><div style=\"left: {7}px; position: absolute; top: {8}px;\">{6}</div></input>"; sb.Append("<div style=\"height:200px\"> "); foreach(XElement f in xml.Elements()) { if (f.Element("Type").Value == "label") { sb.Append(string.Format(label, Double.Parse(f.Element("Left").Value) - 60, f.Element("Top").Value, f.Element("Value").Value)); } else if (f.Element("Type").Value == "checkbox") { sb.Append(string.Format(checkbox, f.Element("Name").Value, f.Element("Type").Value, f.Element("Left").Value, f.Element("Top").Value, f.Element("Width").Value, f.Element("Height").Value, f.Element("Value").Value, Double.Parse(f.Element("Left").Value) + 50, Double.Parse(f.Element("Top").Value) +8)); } else { sb.Append(string.Format(input, f.Element("Name").Value, f.Element("Type").Value, f.Element("Left").Value, f.Element("Top").Value, f.Element("Width").Value, f.Element("Height").Value, f.Element("Value").Value)); } } sb.Append("</div > "); Response.Write(sb.ToString()); %> <%Html.EndForm(); %> 在Asp.net MVC中显示的登陆界面:
总结:本文主要是讲述了实现一个表单设计器的简单思路。如果有什么疑问,或者更好的办法(最好能有Demo),欢迎能一起讨论。
本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2010/08/20/wpf-form-designer.html,如需转载请自行联系原作者