当前位置: 首页 > news >正文

界面代码分离的软件设置界面设计

   现在界面设计越来越注重与代码的分离,把表现界面的元素写在XML文件中,程序加载时再通过反射等机制加载到程序里。以前我写的小程序,也有些设置功能,往往把界面直接在代码里写死。如果选项不多还好,如果选项一多,就使界面混乱不堪了。所以我也采用了XML配置文件的方式来编写设置功能。

   但是既然是小程序,就要保持原来短小精悍的风格,速度也不能太慢,最重要的是代码编写得方便,所以不能太那些框架那样搞。我先分析,设置界面大概需要这些东西:1,数值选择框、2,复选框、3,单选框,4,其它(可以有TextBox之类的)。我只需要前三项,且第3项单选框我用“下拉选择框”来代替,可以节省空间(其实主要是方便编码啦)。

   先来规定下XML的格式,下面是一个示例。

 
 
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <preference> 
  3.   <page name="标签1"> 
  4.     <item type="int" name="set1"> 
  5.       <text>设置1</text> 
  6.       <value max="100" min="0">5</value> 
  7.     </item> 
  8.     <item type="int" name="set6"> 
  9.       <text>设置1</text> 
  10.       <value max="100" min="0">100</value> 
  11.     </item> 
  12.     <item type="checkbox" name="set2"> 
  13.       <text>设置2</text> 
  14.       <value>True</value> 
  15.     </item> 
  16.     <item type="combo" name="set3"> 
  17.       <combo> 
  18.         <text>item0</text> 
  19.         <value>0</value> 
  20.       </combo> 
  21.       <combo> 
  22.         <text>item1</text> 
  23.         <value>1</value> 
  24.       </combo> 
  25.       <text>选择XX选项:</text> 
  26.       <value>1</value> 
  27.     </item> 
  28.   </page> 
  29.   <page name="标签2"> 
  30.     <item type="int" name="set1"> 
  31.       <text>设置1</text> 
  32.       <value max="100" min="0">100</value> 
  33.     </item> 
  34.     <page name="标签2_1"> 
  35.     </page> 
  36.   </page> 
  37. </preference> 

用“preference”做根节点,根节点下面是“page”,一个“page”代表一个选项卡,一个“page”里可以有子“page”,就像Eclipse的设置一样,也可以包含有“Item”,一个“Item”代表一个选项。“Item”可以有3种类型,代表“数值选择框”、“复选框”和“下拉框”。路径由"name"属性来唯一标识,如:“name1\name1_1”,同一个page下相同的"name"将会导致解析的错误(未定义行为)。上面的例子中有“标签1”,“标签2”两页,“标签2”有一个“标签2_1”的子页。

为了解析这个XML,我设计了三个类。

1,PreferenceTreeAdapter,读取XML的目录结构,显示到TreeView控件中。

 

 
 
  1. /// <summary> 
  2.  /// 从XML文件中读取首选项的目录 
  3.  /// </summary> 
  4.  public class PreferenceTreeAdapter 
  5.  { 
  6.      protected string xmlDoc; 
  7.  
  8.      public PreferenceTreeAdapter(String filePath) 
  9.      { 
  10.          xmlDoc = File.ReadAllText(filePath); 
  11.      } 
  12.  
  13.      public void Fill(TreeView treeView) 
  14.      { 
  15.          XmlDocument doc = new XmlDocument(); 
  16.          doc.LoadXml(xmlDoc); 
  17.  
  18.          XmlNodeList nodeList = doc.SelectSingleNode("preference").SelectNodes("page"); 
  19.          FillChildren(nodeList, treeView.Nodes);      
  20.      } 
  21.  
  22.      protected void FillChildren(XmlNodeList nodeList, TreeNodeCollection treeNodeCollection) 
  23.      { 
  24.          if (nodeList.Count == 0) 
  25.          { 
  26.              return
  27.          } 
  28.  
  29.          for (int i = 0; i < nodeList.Count; i++) 
  30.          { 
  31.              XmlNode node = nodeList[i]; 
  32.              TreeNode curTreeNode = treeNodeCollection.Add(node.Attributes.GetNamedItem("name").Value); 
  33.              XmlNodeList newNodeList = node.SelectNodes("page"); 
  34.              if (newNodeList != null
  35.              { 
  36.                  FillChildren(newNodeList, curTreeNode.Nodes); 
  37.              } 
  38.          } 
  39.      } 
  40.  } 

2,OptionsAdapter 负责一个"page"中的选项和控件的对应关系,是一个双向的适配器。

 
 
  1. /// <summary> 
  2.     /// 将XML中一个page的选项展示在窗体中 
  3.     /// </summary> 
  4.     public class OptionsAdapter 
  5.     { 
  6.         protected Dictionary<Control, XmlNode> map = new Dictionary<Control, XmlNode>(); 
  7.         protected XmlDocument doc = new XmlDocument(); 
  8.  
  9.         public OptionsAdapter(string filePath) 
  10.         { 
  11.             doc.LoadXml(File.ReadAllText(filePath)); 
  12.         } 
  13.  
  14.         /// <summary> 
  15.         /// 当路径对应的设置项填充到界面中 
  16.         /// </summary> 
  17.         /// <param name="layout">要填充到的对象</param> 
  18.         /// <param name="fullPath">路径</param> 
  19.         public void Fill(TableLayoutPanel layout, string fullPath) 
  20.         { 
  21.             layout.Controls.Clear(); 
  22.  
  23.             XmlNode node = XmlLocate(fullPath); 
  24.             XmlNodeList optionsList = node.SelectNodes("item"); 
  25.             for (int i = 0; i < optionsList.Count; i++ ) 
  26.             { 
  27.                 Panel panel = new Panel(); 
  28.                 panel.AutoSize = true
  29.  
  30.                 Control control =  FillPanel(optionsList[i], panel); 
  31.                 if (control != null
  32.                 { 
  33.                     map.Add(control, optionsList[i]);//添加控件对节点的映射关系 
  34.                     layout.Controls.Add(panel, 0, i); 
  35.                 } 
  36.             } 
  37.  
  38.         } 
  39.  
  40.         protected XmlNode XmlLocate(string fullPath) 
  41.         { 
  42.             string[] pathList = fullPath.Split('\\'); 
  43.  
  44.             XmlNode node = doc.SelectSingleNode("preference"); 
  45.  
  46.             int pathLevelCount = pathList.Count(); 
  47.             for (int i = 0; i < pathLevelCount; i++) 
  48.             { 
  49.                 bool flag = false
  50.                 foreach (XmlNode curNode in node.SelectNodes("page")) 
  51.                 { 
  52.                     if (curNode.Attributes.GetNamedItem("name").Value == pathList[i]) 
  53.                     { 
  54.                         node = curNode; 
  55.                         flag = true
  56.                         break
  57.                     } 
  58.                 } 
  59.                 if (!flag) 
  60.                 { 
  61.                     throw new Exception("no such path:" + pathList[i]); 
  62.                 } 
  63.             } 
  64.  
  65.             return node; 
  66.         } 
  67.  
  68.         protected Control FillPanel(XmlNode node, Panel panel) 
  69.         { 
  70.             string type = node.Attributes.GetNamedItem("type").Value; 
  71.   
  72.             switch (type) 
  73.             { 
  74.                 case "checkbox"
  75.                     { 
  76.                         CheckBox checkBox = new CheckBox(); 
  77.                         checkBox.Text = node.SelectSingleNode("text").InnerText; 
  78.                         checkBox.Checked = bool.Parse( 
  79.                             node.SelectSingleNode("value").InnerText); 
  80.                         panel.Controls.Add(checkBox); 
  81.                         return checkBox; 
  82.                     } 
  83.                 case "combo"
  84.                     { 
  85.                         Label label = new Label(); 
  86.                         label.Text = node.SelectSingleNode("text").InnerText; 
  87.                         label.Location = new System.Drawing.Point(0, 0); 
  88.  
  89.                         XmlNodeList list = node.SelectNodes("combo"); 
  90.                         ComboBox comboBox = new ComboBox(); 
  91.                         comboBox.DisplayMember = "Key"
  92.                         comboBox.ValueMember = "Value"
  93.                         comboBox.Location = new System.Drawing.Point( 
  94.                             5, label.Height + label.Top + 2); 
  95.  
  96.                         string selectedValue = node.SelectSingleNode("value").InnerText; 
  97.                         for (int i = 0; i < list.Count; i++) 
  98.                         { 
  99.                             XmlNode curNode = list[i]; 
  100.                             string key = curNode.SelectSingleNode("text").InnerText; 
  101.                             string value = curNode.SelectSingleNode("value").InnerText; 
  102.                             comboBox.Items.Add(new KeyValuePair<stringstring>(key, value)); 
  103.                             if(selectedValue == value) 
  104.                             { 
  105.                                 comboBox.SelectedIndex = i; 
  106.                             } 
  107.                         } 
  108.  
  109.                         panel.Controls.Add(label); 
  110.                         panel.Controls.Add(comboBox); 
  111.                         return comboBox; 
  112.                     } 
  113.                 case "int"
  114.                     { 
  115.                         Label label = new Label(); 
  116.                         label.Location = new System.Drawing.Point(0, 0); 
  117.                         label.Text = node.SelectSingleNode("text").InnerText; 
  118.  
  119.                         NumericUpDown numericUpDown = new NumericUpDown(); 
  120.                         XmlNode valueNode = node.SelectSingleNode("value"); 
  121.                         if (valueNode.Attributes.GetNamedItem("max") != null
  122.                         { 
  123.                             numericUpDown.Maximum = decimal.Parse( 
  124.                                 valueNode.Attributes.GetNamedItem("max").Value); 
  125.                         } 
  126.                         if (valueNode.Attributes.GetNamedItem("min") != null
  127.                         { 
  128.                             numericUpDown.Minimum = decimal.Parse( 
  129.                                 valueNode.Attributes.GetNamedItem("min").Value); 
  130.                         } 
  131.                         numericUpDown.Value = decimal.Parse(valueNode.InnerText); 
  132.                         numericUpDown.Location = new System.Drawing.Point( 
  133.                             5, label.Height + label.Top + 2); 
  134.  
  135.                         panel.Controls.Add(label); 
  136.                         panel.Controls.Add(numericUpDown); 
  137.                          
  138.                         return numericUpDown; 
  139.                     } 
  140.             } 
  141.  
  142.             return null
  143.         } 
  144.  
  145.         /// <summary> 
  146.         /// 把窗体上的控件的值保存到XML中 
  147.         /// </summary> 
  148.         /// <param name="layout"></param> 
  149.         public void Update(TableLayoutPanel layout) 
  150.         { 
  151.             foreach(Control panel in layout.Controls) 
  152.             { 
  153.                 if (panel is Panel) 
  154.                 { 
  155.                     foreach(Control control in panel.Controls) 
  156.                     { 
  157.                         if (map.ContainsKey(control)) 
  158.                         { 
  159.                             map[control].SelectSingleNode("value").InnerText 
  160.                                 = getControlValue(control); 
  161.                         } 
  162.                     } 
  163.                 } 
  164.             } 
  165.             doc.Save(Resource.ResourceManager.GetString("preference")); 
  166.         } 
  167.  
  168.         protected string getControlValue(Control control) 
  169.         { 
  170.             switch (control.GetType().Name.ToString().ToLower()) 
  171.             { 
  172.                 case "checkbox"
  173.                     { 
  174.                         return ((CheckBox)control).Checked.ToString(); 
  175.                     } 
  176.                 case "combobox"
  177.                     { 
  178.                         ComboBox comboBox = (ComboBox)control; 
  179.  
  180.                         return ((KeyValuePair<stringstring>)comboBox.SelectedItem).Value; 
  181.                     } 
  182.                 case "numericupdown"
  183.                     { 
  184.                         return ((NumericUpDown)control).Value.ToString(); 
  185.                     } 
  186.             } 
  187.             throw new Exception("未知的类型"); 
  188.         } 
  189.     } 

3、PreferenceReader 读取一个选项的值

 
 
  1. /// <summary> 
  2.    /// 读取某个选项的值 
  3.    /// </summary> 
  4.    class PreferenceReader 
  5.    { 
  6.        protected XmlDocument doc = new XmlDocument(); 
  7.  
  8.        public PreferenceReader(string filePath) 
  9.        { 
  10.            doc.LoadXml(File.ReadAllText(filePath)); 
  11.        } 
  12.  
  13.        public bool ReadCheckbox(string fullPath) 
  14.        { 
  15.            XmlNode node = XmlLocate(fullPath); 
  16.            if(node.Attributes.GetNamedItem("type").Value != "checkbox"
  17.            { 
  18.                throw new Exception("路径与类型不符"); 
  19.            } 
  20.  
  21.            return bool.Parse(node.SelectSingleNode("value").InnerText); 
  22.        } 
  23.  
  24.        public string ReadCombo(string fullPath) 
  25.        { 
  26.            XmlNode node = XmlLocate(fullPath); 
  27.            if (node.Attributes.GetNamedItem("type").Value != "combo"
  28.            { 
  29.                throw new Exception("路径与类型不符"); 
  30.            } 
  31.  
  32.            return node.SelectSingleNode("value").InnerText; 
  33.        } 
  34.  
  35.        public int ReadInt(string fullPath) 
  36.        { 
  37.            XmlNode node = XmlLocate(fullPath); 
  38.            if (node.Attributes.GetNamedItem("type").Value != "int"
  39.            { 
  40.                throw new Exception("路径与类型不符"); 
  41.            } 
  42.  
  43.            return int.Parse(node.SelectSingleNode("value").InnerText); 
  44.        } 
  45.  
  46.        protected XmlNode XmlLocate(string fullPath) 
  47.        { 
  48.            string[] pathList = fullPath.Split('\\'); 
  49.  
  50.            XmlNode node = doc.SelectSingleNode("preference"); 
  51.  
  52.            int pathLevelCount = pathList.Count(); 
  53.            for (int i = 0; i < pathLevelCount; i++) 
  54.            { 
  55.                bool flag = false
  56.                foreach (XmlNode curNode in node.ChildNodes) 
  57.                { 
  58.                    if (curNode.Attributes.GetNamedItem("name") != null &&  
  59.                        curNode.Attributes.GetNamedItem("name").Value == pathList[i]) 
  60.                    { 
  61.                        node = curNode; 
  62.                        flag = true
  63.                        break
  64.                    } 
  65.                } 
  66.                if (!flag) 
  67.                { 
  68.                    throw new Exception("no such path:" + pathList[i]); 
  69.                } 
  70.            } 
  71.  
  72.            return node; 
  73.        } 
  74.    } 

Demo:

 
 
  1. public partial class FrmPreference : Form 
  2.     { 
  3.         OptionsAdapter optionsAdapter = new OptionsAdapter( 
  4.                 Resource.ResourceManager.GetString("preference")); 
  5.  
  6.         public FrmPreference() 
  7.         { 
  8.             InitializeComponent(); 
  9.         } 
  10.  
  11.         private void FrmPreference_Load(object sender, EventArgs e) 
  12.         { 
  13.             PreferenceTreeAdapter preferenceTreeAdapter = new PreferenceTreeAdapter("preference.xml"); 
  14.             preferenceTreeAdapter.Fill(optionsView); 
  15.             if(optionsView.Nodes.Count > 0) 
  16.             { 
  17.                 optionsView.SelectedNode = optionsView.Nodes[0]; 
  18.             } 
  19.              
  20.             tableLayoutPanel.AutoSize = true;             
  21.         } 
  22.  
  23.         private void btnSave_Click(object sender, EventArgs e) 
  24.         { 
  25.             optionsAdapter.Update(tableLayoutPanel); 
  26.         } 
  27.  
  28.         private void button1_Click(object sender, EventArgs e) 
  29.         { 

  30.             PreferenceReader preferenceReader = new PreferenceReader( 
  31.                 Resource.ResourceManager.GetString("preference")); 
  32.  
  33.             MessageBox.Show(preferenceReader.ReadInt("标签1\\set1").ToString()); 
  34.             MessageBox.Show(preferenceReader.ReadCheckbox("标签1\\set2").ToString()); 
  35.             MessageBox.Show(preferenceReader.ReadCombo("标签1\\set3").ToString()); 
  36.         } 
  37.  
  38.         private void optionsView_AfterSelect(object sender, TreeViewEventArgs e) 
  39.         { 
  40.             optionsAdapter.Fill(tableLayoutPanel, optionsView.SelectedNode.FullPath); 
  41.         } 

Demo效果如下
 

 

最近一直在实习,晚上才有一点点时间自己写代码,断断续续写了N天,写的也有点乱了,初步就这么一个效果,有空再完善下界面。这种方式写的设置界面有个好处,只需要编码一次,以后再用就只要修改下XML文件就行了,界面的显示就交给软件自动去完成。而且代码还算精简,没有用到反射,但也有一定的扩展性了。

 

 


本文转自 dogegg250 51CTO博客,原文链接:http://blog.51cto.com/jianshusoft/637077,如需转载请自行联系原作者

相关文章:

  • (4) PIVOT 和 UPIVOT 的使用
  • sql中更新数据库用到declare @a in
  • Kubernetes集群部署1
  • 工作之余使用python玩转微信跳一跳(超详细的教程)
  • javascript测试输入以空格隔开的字符串中是否有重复的字符串,并且输出
  • nginx+tomcat
  • C#实现抽奖程序(1)
  • 修改mysql密码批处理
  • 设计模式——观察者模式(Observer Pattern)
  • python 自动化运维 Paramiko 模块
  • /etc/sudoer文件配置简析
  • 通过ipmitool监控机房内服务器温度
  • 使用vxsim(一)
  • linux系统学习第九天-工程师技术
  • Windows防火墙开启ping
  • ES6指北【2】—— 箭头函数
  • python3.6+scrapy+mysql 爬虫实战
  • Android单元测试 - 几个重要问题
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • js继承的实现方法
  • js中的正则表达式入门
  • Koa2 之文件上传下载
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • Promise初体验
  • Shell编程
  • Vue 2.3、2.4 知识点小结
  • 飞驰在Mesos的涡轮引擎上
  • 基于遗传算法的优化问题求解
  • 开源地图数据可视化库——mapnik
  • 普通函数和构造函数的区别
  • 以太坊客户端Geth命令参数详解
  • 译有关态射的一切
  • Hibernate主键生成策略及选择
  • Nginx实现动静分离
  • 交换综合实验一
  • #QT项目实战(天气预报)
  • $refs 、$nextTic、动态组件、name的使用
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (6)添加vue-cookie
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (solr系列:一)使用tomcat部署solr服务
  • (待修改)PyG安装步骤
  • (五)MySQL的备份及恢复
  • (原)本想说脏话,奈何已放下
  • (原創) 物件導向與老子思想 (OO)
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .Net各种迷惑命名解释
  • .NET基础篇——反射的奥妙
  • .NET委托:一个关于C#的睡前故事
  • @requestBody写与不写的情况
  • @基于大模型的旅游路线推荐方案
  • []AT 指令 收发短信和GPRS上网 SIM508/548
  • [AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗
  • [Android开源]EasySharedPreferences:优雅的进行SharedPreferences数据存储操作