前一篇,我们定义并实现了配置文件的读写功能,这次我们来实现配置文件的界面展示。之前我们设计的时候,就要求界面能够动态展现,比如在界面定义文件中新增一个CheckBox定义,那么要求界面能够反映出来。
本次界面还原技术,我们采用SharpDev的方式,利用xml文件对界面进行描述,然后利用反射将界面展示出来。(PS:如果你用过Delphi,你同样会发现Delphi的frm文件也是采用这样的思路),这里有个问题需要暂时搁置一下,对于界面中用到的图像资源的处理,目前暂时不用管。
好了,我们先来看一下设计思路
总界面管理类定义
代码
/// 界面管理类
/// </summary>
public interface IConfigUiManager
{
/// <summary>
/// 默认界面文件路径
/// </summary>
string DefaultFloder
{
get;
set;
}
/// <summary>
/// 界面描述文件的后缀
/// </summary>
string DefaultExt
{
get;
set;
}
/// <summary>
/// 扫描默认目录下的界面文件并加载
/// </summary>
void Load();
/// <summary>
/// 定位某个界面
/// </summary>
/// <param name="pName"></param>
/// <returns></returns>
IConfigSectionUi FindUi(string pName);
/// <summary>
/// 展示总配置界面
/// </summary>
void ShowUi();
}
当个配置程序节的界面定义
代码
/// 单个配置程序节的界面
/// </summary>
public interface IConfigSectionUi
{
/// <summary>
/// 是否集成到总界面中
/// </summary>
bool LazyLoad
{
get;
set;
}
/// <summary>
/// 界面名称
/// </summary>
string Name
{
get;
set;
}
/// <summary>
///从配置文件中加载选项
/// </summary>
void LoadFromConfig();
/// <summary>
/// 将设置保存到配置文件中
/// </summary>
void SaveToConfig();
/// <summary>
/// 从xml文件中提取的界面
/// </summary>
System.Windows.Forms.Control Ui
{
get;
set;
}
/// <summary>
/// 展示当前的配置界面
/// </summary>
void ShowUi();
void LoadFromFile(string pFileName);
string Caption
{
get;
set;
}
Control FindControl(string pCtlName);
}
在这里,我们需要声明一下,对于界面定义文件,我们支持两种方式,一种是Form形式定义,这样的方式允许程序在运行期弹出独立界面跟用户交互;其二,是UserControl方式,这样的方式运行两种模式运行,第一集成到配置总界面中集中管理,第二,单独的弹出界面跟用户交互;基于这样的考虑,所以,我在接口IConfigSectionUi中定义了标识LazyLoad。
基本类的定义就是上面两个,但是辅助类还没有定义,这里,我不妨直接借用SharpDev的代码来实现
这里用到了其中核心的一个类XmlLoader(具体实现可以参考其源代码)
好了,这样一来,界面的还原功能就实现了。但还是没有达到我们之前制定的目标。好吧,我们来实现IConfigSectionUi.LoadFromFile方法
代码
void IConfigSectionUi.LoadFromFile(string pFileName)
{
using (FileStream pStream = new FileStream(pFileName, FileMode.Open))
{
AppConfig.Ui.FakeForm FakeForm = new AppConfig.Ui.FakeForm();
object FakeObj = null;
if (pStream == null)
{
throw new System.ArgumentNullException("stream");
}
FakeForm.SuspendLayout();
xmlLoader = new XmlLoader();
SetupXmlLoader();
if (pStream != null)
{
FakeObj = xmlLoader.LoadObjectFromStream(FakeForm, pStream);
}
if (FakeForm.Controls.Count > 0)
{
this._Ui = FakeForm;
this._Name = FakeForm.Name;
this._LazyLoad = true;
this._Caption = FakeForm.Caption;
FakeForm.ResumeLayout(false);
}
else
{
this._Ui = (Control)FakeObj;
this._Name = _Ui.Name;
this._Caption = ((Ui.FakeUserControl)_Ui).Caption;
}
pStream.Close();
}
}
嗯,我们按照两种方式,将界面Form和UserControl区分开了,那么在展现的做一下手脚就可以了。
看一下总界面的展现方法实现
代码
{
Ui.FormConfigMain FormConfigMain = new AppConfig.Ui.FormConfigMain();
TabControl pTab = FormConfigMain.pcCtl;
foreach (KeyValuePair<string, IConfigSectionUi> pUi in _List)
{
if (pUi.Value.LazyLoad)
{
// pUi.Value.ShowUi();
}
else//加入总界面
{
TabPage pPage = new TabPage();
pPage.Text = pUi.Value.Caption;
pPage.Controls.Add(pUi.Value.Ui);
pUi.Value.Ui.Dock = DockStyle.Fill;
pTab.TabPages.Add(pPage);
}
}
FormConfigMain.ShowDialog();
}
如果发现此界面为单独界面即Form类型,则不加载到总界面中;
嗯,那么单个界面展示如何展示呢?看一下吧
代码
{
if (this._Ui == null) return;
if (_LazyLoad)
this._Ui.Show();
else
{
Ui.FormConfigDynamic frm = new AppConfig.Ui.FormConfigDynamic();
frm.pnlCtl.Controls.Add(this._Ui);
frm.Show();
}
}
如果此界面为单独界面,直接Show出来,如果是个UserControl,则用内置的ormConfigDynamic界面包装一下然后Show出来。
好了,到此为止,基本实现了我们之前定义的需求。
展示一下几个测试界面
好了,到此,界面的动态展示基本完成。下次,我们将解决界面内选项的动态展示,也就是涉及到脚本技术跟C#的互动。
(未完待续...)
上次,我们分析了“动态”配置文件的基本需求,也基本定下了设计思路。我们今天就来实现第一要素:读写配置文件
按照我们上次的分析要求,我设计了以下几个类
ConfigManager,【配置文件管理类】
Config,【单个配置文件】
ConfigSection,【配置文件中的单个程序节】
三个类依次包含,为了方便程序调用,为此,设计了三个接口,分别由三个类来实现。这样一来,外部调用的程序,只需要知道接口,而不需要引用这个类;
部分定义代码
IConfigManager定义
IConfigManager
2 /// 配置文件管理接口
3 /// </summary>
4 public interface IConfigManager
5 {
6 /// <summary>
7 /// 增加配置文件
8 /// </summary>
9 /// <param name="pCfg">配置文件</param>
10 void AddConfig(IConfig pCfg);
11 /// <summary>
12 /// 删除配置文件
13 /// </summary>
14 /// <param name="pCfg">配置文件</param>
15 void DeleteConfig(IConfig pCfg);
16 /// <summary>
17 /// 定位配置文件
18 /// </summary>
19 /// <param name="pName"></param>
20 /// <returns></returns>
21 IConfig FindConfig(string pName);
22 /// <summary>
23 /// 配置文件是否存在
24 /// </summary>
25 /// <param name="pName"></param>
26 /// <returns></returns>
27 bool HasExist(string pName);
28 /// <summary>
29 /// 默认配置文件后缀
30 /// </summary>
31 string DefaultEx
32 {
33 get;
34 set;
35 }
36 /// <summary>
37 /// 默认配置文件存放位置
38 /// </summary>
39 string DefaultFolder
40 {
41 get;
42 set;
43 }
44 /// <summary>
45 /// 加载目录下的所有配置文件
46 /// </summary>
47 void Load();
48 /// <summary>
49 /// 当前的配置文件
50 /// </summary>
51 IConfig CurrentConfig
52 {
53 get;
54 set;
55 }
56 }
IConfig定义
代码
2 /// 单个配置文件
3 /// </summary>
4 public interface IConfig
5 {
6 /// <summary>
7 /// 配置文件名称
8 /// </summary>
9 string Name
10 {
11 get;
12 set;
13 }
14 /// <summary>
15 /// 增加程序节
16 /// </summary>
17 /// <param name="pCfgSec"></param>
18 void AddSection(IConfigSection pCfgSec);
19 /// <summary>
20 /// 删除程序节
21 /// </summary>
22 /// <param name="pCfgSec"></param>
23 void DeleteSection(IConfigSection pCfgSec);
24 /// <summary>
25 /// 定位程序节
26 /// </summary>
27 /// <param name="pName"></param>
28 /// <returns></returns>
29 IConfigSection FindSection(string pName);
30 /// <summary>
31 /// 查询程序节是否存在
32 /// </summary>
33 /// <param name="pName"></param>
34 /// <returns></returns>
35 bool HasExist(string pName);
36 /// <summary>
37 /// 直接读取某个程序节下的设置
38 /// </summary>
39 /// <param name="pSectionName"></param>
40 /// <param name="pKeyName"></param>
41 /// <param name="pDefaultValue"></param>
42 /// <returns></returns>
43 object ReadKeyValue(string pSectionName, string pKeyName, object pDefaultValue);
44 /// <summary>
45 /// 写入某个程序节下,如果此程序节不存在,将创建
46 /// </summary>
47 /// <param name="pSectionName"></param>
48 /// <param name="pKeyName"></param>
49 /// <param name="pValue"></param>
50 void WriteKeyValue(string pSectionName, string pKeyName, object pValue);
51 /// <summary>
52 /// 加载文件,读取全部配置
53 /// </summary>
54 /// <param name="pFileName"></param>
55 void LoadfromFile(string pFileName);
56 /// <summary>
57 /// 设置保存到文件
58 /// </summary>
59 void SaveToFile();
60 /// <summary>
61 /// 自动保存
62 /// </summary>
63 bool AutoSave
64 {
65 get;
66 set;
67 }
68 /// <summary>
69 /// 是否开启文件监测
70 /// </summary>
71 bool Watched
72 {
73 get;
74 set;
75 }
76 /// <summary>
77 /// 此配置文件的文件路径
78 /// </summary>
79 string FileName
80 {
81 get;
82 set;
83 }
84 }
IConfigSection定义
代码
/// 配置分类
/// </summary>
public interface IConfigSection
{
string SectionName
{
get;
set;
}
void Add(string pKeyName, object pKeyValue);
void Delete(string pKeyName);
object ReadKeyValue(string pKeyName, object pDefaultValue);
void WriteKeyValue(string pKeyName, object pValue);
void SaveToFile();
}
由于本次采用ini文件作为配置文件的载体,所以,在读写上就省略了很多方法。甚至在IConfig接口上提供了读写方法,但为了XML文件之类的读写,还是提供了方法定义。
(未完待续...)

