动态【配置文件】之实现 (二)
前一篇,我们定义并实现了配置文件的读写功能,这次我们来实现配置文件的界面展示。之前我们设计的时候,就要求界面能够动态展现,比如在界面定义文件中新增一个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#的互动。
(未完待续...)