1. 程式人生 > 實用技巧 >介面程式碼分離的軟體設定介面設計

介面程式碼分離的軟體設定介面設計

現在介面設計越來越注重與程式碼的分離,把表現介面的元素寫在XML檔案中,程式載入時再通過反射等機制載入到程式裡。以前我寫的小程式,也有些設定功能,往往把介面直接在程式碼裡寫死。如果選項不多還好,如果選項一多,就使介面混亂不堪了。所以我也採用了XML配置檔案的方式來編寫設定功能。

但是既然是小程式,就要保持原來短小精悍的風格,速度也不能太慢,最重要的是程式碼編寫得方便,所以不能太那些框架那樣搞。我先分析,設定介面大概需要這些東西:1,數值選擇框、2,複選框、3,單選框,4,其它(可以有TextBox之類的)。我只需要前三項,且第3項單選框我用“下拉選擇框”來代替,可以節省空間(其實主要是方便編碼啦)。

先來規定下XML的格式,下面是一個示例。

  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <preference>
  3. <pagename="標籤1">
  4. <itemtype="int"name="set1">
  5. <text>設定1</text>
  6. <valuemax="100"min="0">5</value>
  7. </item>
  8. <itemtype="int"
    name="set6">
  9. <text>設定1</text>
  10. <valuemax="100"min="0">100</value>
  11. </item>
  12. <itemtype="checkbox"name="set2">
  13. <text>設定2</text>
  14. <value>True</value>
  15. </item>
  16. <itemtype="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. <pagename="標籤2">
  30. <itemtype="int"name="set1">
  31. <text>設定1</text>
  32. <valuemax="100"min="0">100</value>
  33. </item>
  34. <pagename="標籤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. publicclassPreferenceTreeAdapter
  5. {
  6. protectedstringxmlDoc;
  7. publicPreferenceTreeAdapter(StringfilePath)
  8. {
  9. xmlDoc=File.ReadAllText(filePath);
  10. }
  11. publicvoidFill(TreeViewtreeView)
  12. {
  13. XmlDocumentdoc=newXmlDocument();
  14. doc.LoadXml(xmlDoc);
  15. XmlNodeListnodeList=doc.SelectSingleNode("preference").SelectNodes("page");
  16. FillChildren(nodeList,treeView.Nodes);
  17. }
  18. protectedvoidFillChildren(XmlNodeListnodeList,TreeNodeCollectiontreeNodeCollection)
  19. {
  20. if(nodeList.Count==0)
  21. {
  22. return;
  23. }
  24. for(inti=0;i<nodeList.Count;i++)
  25. {
  26. XmlNodenode=nodeList[i];
  27. TreeNodecurTreeNode=treeNodeCollection.Add(node.Attributes.GetNamedItem("name").Value);
  28. XmlNodeListnewNodeList=node.SelectNodes("page");
  29. if(newNodeList!=null)
  30. {
  31. FillChildren(newNodeList,curTreeNode.Nodes);
  32. }
  33. }
  34. }
  35. }

2,OptionsAdapter 負責一個"page"中的選項和控制元件的對應關係,是一個雙向的介面卡。

  1. ///<summary>
  2. ///將XML中一個page的選項展示在窗體中
  3. ///</summary>
  4. publicclassOptionsAdapter
  5. {
  6. protectedDictionary<Control,XmlNode>map=newDictionary<Control,XmlNode>();
  7. protectedXmlDocumentdoc=newXmlDocument();
  8. publicOptionsAdapter(stringfilePath)
  9. {
  10. doc.LoadXml(File.ReadAllText(filePath));
  11. }
  12. ///<summary>
  13. ///當路徑對應的設定項填充到介面中
  14. ///</summary>
  15. ///<paramname="layout">要填充到的物件</param>
  16. ///<paramname="fullPath">路徑</param>
  17. publicvoidFill(TableLayoutPanellayout,stringfullPath)
  18. {
  19. layout.Controls.Clear();
  20. XmlNodenode=XmlLocate(fullPath);
  21. XmlNodeListoptionsList=node.SelectNodes("item");
  22. for(inti=0;i<optionsList.Count;i++)
  23. {
  24. Panelpanel=newPanel();
  25. panel.AutoSize=true;
  26. Controlcontrol=FillPanel(optionsList[i],panel);
  27. if(control!=null)
  28. {
  29. map.Add(control,optionsList[i]);//新增控制元件對節點的對映關係
  30. layout.Controls.Add(panel,0,i);
  31. }
  32. }
  33. }
  34. protectedXmlNodeXmlLocate(stringfullPath)
  35. {
  36. string[]pathList=fullPath.Split('\\');
  37. XmlNodenode=doc.SelectSingleNode("preference");
  38. intpathLevelCount=pathList.Count();
  39. for(inti=0;i<pathLevelCount;i++)
  40. {
  41. boolflag=false;
  42. foreach(XmlNodecurNodeinnode.SelectNodes("page"))
  43. {
  44. if(curNode.Attributes.GetNamedItem("name").Value==pathList[i])
  45. {
  46. node=curNode;
  47. flag=true;
  48. break;
  49. }
  50. }
  51. if(!flag)
  52. {
  53. thrownewException("nosuchpath:"+pathList[i]);
  54. }
  55. }
  56. returnnode;
  57. }
  58. protectedControlFillPanel(XmlNodenode,Panelpanel)
  59. {
  60. stringtype=node.Attributes.GetNamedItem("type").Value;
  61. switch(type)
  62. {
  63. case"checkbox":
  64. {
  65. CheckBoxcheckBox=newCheckBox();
  66. checkBox.Text=node.SelectSingleNode("text").InnerText;
  67. checkBox.Checked=bool.Parse(
  68. node.SelectSingleNode("value").InnerText);
  69. panel.Controls.Add(checkBox);
  70. returncheckBox;
  71. }
  72. case"combo":
  73. {
  74. Labellabel=newLabel();
  75. label.Text=node.SelectSingleNode("text").InnerText;
  76. label.Location=newSystem.Drawing.Point(0,0);
  77. XmlNodeListlist=node.SelectNodes("combo");
  78. ComboBoxcomboBox=newComboBox();
  79. comboBox.DisplayMember="Key";
  80. comboBox.ValueMember="Value";
  81. comboBox.Location=newSystem.Drawing.Point(
  82. 5,label.Height+label.Top+2);
  83. stringselectedValue=node.SelectSingleNode("value").InnerText;
  84. for(inti=0;i<list.Count;i++)
  85. {
  86. XmlNodecurNode=list[i];
  87. stringkey=curNode.SelectSingleNode("text").InnerText;
  88. stringvalue=curNode.SelectSingleNode("value").InnerText;
  89. comboBox.Items.Add(newKeyValuePair<string,string>(key,value));
  90. if(selectedValue==value)
  91. {
  92. comboBox.SelectedIndex=i;
  93. }
  94. }
  95. panel.Controls.Add(label);
  96. panel.Controls.Add(comboBox);
  97. returncomboBox;
  98. }
  99. case"int":
  100. {
  101. Labellabel=newLabel();
  102. label.Location=newSystem.Drawing.Point(0,0);
  103. label.Text=node.SelectSingleNode("text").InnerText;
  104. NumericUpDownnumericUpDown=newNumericUpDown();
  105. XmlNodevalueNode=node.SelectSingleNode("value");
  106. if(valueNode.Attributes.GetNamedItem("max")!=null)
  107. {
  108. numericUpDown.Maximum=decimal.Parse(
  109. valueNode.Attributes.GetNamedItem("max").Value);
  110. }
  111. if(valueNode.Attributes.GetNamedItem("min")!=null)
  112. {
  113. numericUpDown.Minimum=decimal.Parse(
  114. valueNode.Attributes.GetNamedItem("min").Value);
  115. }
  116. numericUpDown.Value=decimal.Parse(valueNode.InnerText);
  117. numericUpDown.Location=newSystem.Drawing.Point(
  118. 5,label.Height+label.Top+2);
  119. panel.Controls.Add(label);
  120. panel.Controls.Add(numericUpDown);
  121. returnnumericUpDown;
  122. }
  123. }
  124. returnnull;
  125. }
  126. ///<summary>
  127. ///把窗體上的控制元件的值儲存到XML中
  128. ///</summary>
  129. ///<paramname="layout"></param>
  130. publicvoidUpdate(TableLayoutPanellayout)
  131. {
  132. foreach(Controlpanelinlayout.Controls)
  133. {
  134. if(panelisPanel)
  135. {
  136. foreach(Controlcontrolinpanel.Controls)
  137. {
  138. if(map.ContainsKey(control))
  139. {
  140. map[control].SelectSingleNode("value").InnerText
  141. =getControlValue(control);
  142. }
  143. }
  144. }
  145. }
  146. doc.Save(Resource.ResourceManager.GetString("preference"));
  147. }
  148. protectedstringgetControlValue(Controlcontrol)
  149. {
  150. switch(control.GetType().Name.ToString().ToLower())
  151. {
  152. case"checkbox":
  153. {
  154. return((CheckBox)control).Checked.ToString();
  155. }
  156. case"combobox":
  157. {
  158. ComboBoxcomboBox=(ComboBox)control;
  159. return((KeyValuePair<string,string>)comboBox.SelectedItem).Value;
  160. }
  161. case"numericupdown":
  162. {
  163. return((NumericUpDown)control).Value.ToString();
  164. }
  165. }
  166. thrownewException("未知的型別");
  167. }
  168. }

3、PreferenceReader 讀取一個選項的值

  1. ///<summary>
  2. ///讀取某個選項的值
  3. ///</summary>
  4. classPreferenceReader
  5. {
  6. protectedXmlDocumentdoc=newXmlDocument();
  7. publicPreferenceReader(stringfilePath)
  8. {
  9. doc.LoadXml(File.ReadAllText(filePath));
  10. }
  11. publicboolReadCheckbox(stringfullPath)
  12. {
  13. XmlNodenode=XmlLocate(fullPath);
  14. if(node.Attributes.GetNamedItem("type").Value!="checkbox")
  15. {
  16. thrownewException("路徑與型別不符");
  17. }
  18. returnbool.Parse(node.SelectSingleNode("value").InnerText);
  19. }
  20. publicstringReadCombo(stringfullPath)
  21. {
  22. XmlNodenode=XmlLocate(fullPath);
  23. if(node.Attributes.GetNamedItem("type").Value!="combo")
  24. {
  25. thrownewException("路徑與型別不符");
  26. }
  27. returnnode.SelectSingleNode("value").InnerText;
  28. }
  29. publicintReadInt(stringfullPath)
  30. {
  31. XmlNodenode=XmlLocate(fullPath);
  32. if(node.Attributes.GetNamedItem("type").Value!="int")
  33. {
  34. thrownewException("路徑與型別不符");
  35. }
  36. returnint.Parse(node.SelectSingleNode("value").InnerText);
  37. }
  38. protectedXmlNodeXmlLocate(stringfullPath)
  39. {
  40. string[]pathList=fullPath.Split('\\');
  41. XmlNodenode=doc.SelectSingleNode("preference");
  42. intpathLevelCount=pathList.Count();
  43. for(inti=0;i<pathLevelCount;i++)
  44. {
  45. boolflag=false;
  46. foreach(XmlNodecurNodeinnode.ChildNodes)
  47. {
  48. if(curNode.Attributes.GetNamedItem("name")!=null&&
  49. curNode.Attributes.GetNamedItem("name").Value==pathList[i])
  50. {
  51. node=curNode;
  52. flag=true;
  53. break;
  54. }
  55. }
  56. if(!flag)
  57. {
  58. thrownewException("nosuchpath:"+pathList[i]);
  59. }
  60. }
  61. returnnode;
  62. }
  63. }

Demo:

  1. publicpartialclassFrmPreference:Form
  2. {
  3. OptionsAdapteroptionsAdapter=newOptionsAdapter(
  4. Resource.ResourceManager.GetString("preference"));
  5. publicFrmPreference()
  6. {
  7. InitializeComponent();
  8. }
  9. privatevoidFrmPreference_Load(objectsender,EventArgse)
  10. {
  11. PreferenceTreeAdapterpreferenceTreeAdapter=newPreferenceTreeAdapter("preference.xml");
  12. preferenceTreeAdapter.Fill(optionsView);
  13. if(optionsView.Nodes.Count>0)
  14. {
  15. optionsView.SelectedNode=optionsView.Nodes[0];
  16. }
  17. tableLayoutPanel.AutoSize=true;
  18. }
  19. privatevoidbtnSave_Click(objectsender,EventArgse)
  20. {
  21. optionsAdapter.Update(tableLayoutPanel);
  22. }
  23. privatevoidbutton1_Click(objectsender,EventArgse)
  24. {

  25. PreferenceReaderpreferenceReader=newPreferenceReader(
  26. Resource.ResourceManager.GetString("preference"));
  27. MessageBox.Show(preferenceReader.ReadInt("標籤1\\set1").ToString());
  28. MessageBox.Show(preferenceReader.ReadCheckbox("標籤1\\set2").ToString());
  29. MessageBox.Show(preferenceReader.ReadCombo("標籤1\\set3").ToString());
  30. }
  31. privatevoidoptionsView_AfterSelect(objectsender,TreeViewEventArgse)
  32. {
  33. optionsAdapter.Fill(tableLayoutPanel,optionsView.SelectedNode.FullPath);
  34. }

Demo效果如下

最近一直在實習,晚上才有一點點時間自己寫程式碼,斷斷續續寫了N天,寫的也有點亂了,初步就這麼一個效果,有空再完善下介面。這種方式寫的設定介面有個好處,只需要編碼一次,以後再用就只要修改下XML檔案就行了,介面的顯示就交給軟體自動去完成。而且程式碼還算精簡,沒有用到反射,但也有一定的擴充套件性了。

轉載於:https://blog.51cto.com/jianshusoft/637077