在实际开发中,我发现很多程序员花很多时间在界面处理上.并且界面之间的关系和控制逻辑可能工作量并不小.所以好些老手到后面就跑去做后台服务,做通讯去了.
界面布局和交互设计本身有很多的学问,或者有很多艺术,但是本文这里先不讨论这个问题.
在QPG团队实践中,我们把界面划分成很多的UI_PART,这和ASP.NET2.0的WebPart可能有相同的地方.我们用这些简单的PART进行接受输入或者展示结果.这样就可以比较方便的测试了.通常这些PART就是一些用户控件,我们提供了IMainForm接口,使得UI的容器可以动态调入这些部件,哪怕程序已经运行,您也可以编写一个PART,您只要把它放到bin\plugins目录即可.
有时部件可能要和容器通讯,看看下面的代码您可能就会明白:

IMainForm 成员#region IMainForm 成员


public string CurUserID
{

get
{
// TODO: 添加 Form1.CurUserID getter 实现
return "admin";
}
}


public object getCookie(string name)
{
// TODO: 添加 Form1.getCookie 实现
return name;
}


public void addCookie(string name, object obj)
{
// TODO: 添加 Form1.addCookie 实现
}


public QPG.UIP.Privilege getPrivilegeByUser(string uid, string cmdName)
{
// TODO: 添加 Form1.getPrivilegeByUser 实现
return QPG.UIP.Privilege.Full;
}


public void showStatus(string msg)
{
MessageBox.Show(msg);
}


public void showHelp(string help)
{
// TODO: 添加 Form1.showHelp 实现
}


public void showNotify(string title, string msg)
{
MessageBox.Show(msg);
}

#endregion要使用插件服务.您的代码Form容器大致如下:
public class Form1 : System.Windows.Forms.Form,IMainForm

{
private System.ComponentModel.IContainer components;
private System.Windows.Forms.Panel panel1;
public static PluginService Plugins=new PluginService();
private QPG.UIP.Actions.ActionList actionList1;
private QPG.UIP.Actions.Action action1;
private QPG.UIP.Actions.Action action2;
//

protected IPlugin selectedPlugin;

public Form1()

{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
Plugins.MainForm=this;
action1.Tag="Demo.UIC.Plugin1";
action2.Tag="Demo.UIC.Plugin2";
action1.Execute+=new EventHandler(runAction);
action2.Execute+=new EventHandler(runAction);

// Tester t=new Tester();
// t.testParametersConfig();
}

private void runAction(object sender, System.EventArgs e)
{
QPG.UIP.Actions.Action a=sender as QPG.UIP.Actions.Action;
if(getPrivilegeByUser("admin", a.Tag.ToString())==Privilege.None) return;
selectedPlugin=Plugins.FindPlugin(a.Tag.ToString() );
panel1.Controls.Clear();
panel1.Controls.Add(selectedPlugin.MainInterface);
selectedPlugin.MainInterface.Dock=DockStyle.Fill;

}插件的编写就很简单了,大致如下:
using System;
using QPG.UIP;

namespace Demo.UIC


{

/**//// <summary>
/// Plugin2
/// </summary>
public class Plugin1 : BasePlugin

{

public Plugin1():base()

{
}

protected override void init()

{
base.init();

this.myRequimentNo = "uca01";
this.myDescription="统计图";
this.myAuthor="alex";
}
public override void Initialize()

{
this.MainInterface=new PlugUI1();
}
public override void Dispose()

{
//Put any cleanup code in here for when the program is stopped
}

}
}
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using QPG.UIP;


namespace Demo.UIC


{

/**//// <summary>
/// Summary description for ctlMain.
/// </summary>
public class PlugUI1 : PluginBaseUI

{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.TextBox txtFeedback;
private System.Windows.Forms.Button button1;

/**//// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public PlugUI1()

{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();

// TODO: Add any initialization after the InitializeComponent call

}


/**//// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )

{
if( disposing )

{
if(components != null)

{
components.Dispose();
}
}
base.Dispose( disposing );
}

//



private void button1_Click(object sender, System.EventArgs e)
{
this.PluginHost.showStatus(txtFeedback.Text);
}


}
}

具体代码可以参阅:下载开发
演示文件