ワークプレースとモジュールのクラスの使用例
LoadModuleメソッドでは、ワークプレースのコンテナにモジュールをロードする処理と、適切なイベントを対応付ける処理を行い、ロードする側とされる側のモジュール間での制御の受け渡しを実現します。
public static IWTModule LoadModule(IWTWorkplace hostPanel,
IWTModule module, IWTModule parentModule, bool loadVisible){
Control control = module as Control;
if (control == null)
module = null;
else {
Control hostBody = hostPanel.BodyPanel;
// assign a unique name to the module we add,
// based on existing controls already loaded
int currentControlCnt = hostBody.Controls.Count + 1;
control.Name = hostBody.Name + "_ctrl" + currentControlCnt;
control.Location = new System.Drawing.Point(1, 1);
control.Dock = DockStyle.Fill;
hostBody.Controls.Add(control);
// if we don't have to show the control right now
// then just don't do anything
if (!loadVisible)
control.Visible = false;
else
ShowModule(hostPanel, module);
// set reference to caller module
module.CallerModule = parentModule;
// finally, set reference to workplace panel
module.HostWorkplace = hostPanel;
// bind close event to the controller, so it can get notified
// when a module needs to close, and perform required actions
module.Close +=new WTCancelGenericEventHandler(Module_Close);
}
return module;
}
Closeイベントのハンドラでは、モジュールをワークプレースから削除します。さらにオプションで、呼び出し元のモジュールに制御を戻し、終了するモジュールからの引数を渡します。
static void Module_Close(object sender, WTCancelGenericEventArgs e) {
IWTModule module = sender as IWTModule;
if (module != null)
{
// ret reference to caller module and the workplace
IWTModule parentModule = module.CallerModule;
IWTWorkplace workplace = module.HostWorkplace;
// and removes the module from workplace to unload it
workplace.RemoveModule(module);
if (parentModule != null)
{
// if I have reference to parent module
// check if the module was not canceled, and if so
// call Refresh method of the caller module passing
// it received arguments, so it can update itself
if (!e.Cancel)
parentModule.Refresh(e);
// get reference to workplace the parent is loaded into
workplace = parentModule.HostWorkplace
// and activate the parent
workplace.ShowModule(parentModule);
}
}
}
上のコードで要注目なのが、WTCancelGenericEventHandlerとWTCancelGenericEventArgsの2つです。WTCancelGenericEventHandlerは、WTCancelGenericEventArgsと組み合わせて使うデリゲートです。キャンセル可能なジェネリックイベントベースの仕組みを実現します。この仕組みを使うことにより、モジュール間であらゆるデータをEventArgsとして受け渡すことができます。これについては後で解説します。
インターフェースとWTWorkplaceControllerの準備ができたら、次は実際のコンポーネントの作成です。ワークプレースもモジュールもUserControlを継承し、さらにワークプレースはIWTWorkplaceを、モジュールはIWTModuleを、それぞれ実装します。WTWorkplaceコンポーネントのコードは次のとおりです。
public partial class WTWorkplace : UserControl, IWTWorkplace {
protected Control m_container;
public WTWorkplace(){
InitializeComponent();
m_container = (Control)this;
}
public string Caption {
get { return this.Text; }
set {
m_container.Text = value;
}
}
public virtual Control BodyPanel {
get {
if (m_container == null)
m_container = this;
return m_container;
}
}
public IWTModule LoadModule(IWTModule module,
IWTModule parentModule) {
return WTWorkplaceController.LoadModule(
this, module, parentModule, true);
}
public IWTModule LoadModule(IWTModule module,
IWTModule parentModule, bool loadVisible) {
return WTWorkplaceController.LoadModule(
this, module, parentModule, loadVisible);
}
public void ShowModule(IWTModule module) {
WTWorkplaceController.ShowModule(this, module);
}
public void RemoveModule(IWTModule module) {
WTWorkplaceController.RemoveModule(this, module);
}
public void HideAllModules() {
WTWorkplaceController.HideAllModules(this);
}
public bool CheckModuleExists(IWTModule module) {
return WTWorkplaceController.CheckModuleExists(this, module);
}
}
コードは簡潔です。いずれのメソッドも、WTWorkplaceControllerクラスで定義された同様のメソッドを呼び出すだけのラッパーです。
本稿のダウンロードサンプルにも、WTHeaderedWorkplaceクラスのコードが含まれています。
Visual WebGUIは開発のペースが速く、現在のバージョンは6.3.8aです。本稿のダウンロードサンプルもこのバージョンで動作を確認しています。ダウンロードしたサンプルソリューションをビルドして実行するためには、Visual WebGUIのProfessional版をダウンロードしてインストールし、デモ/試用として登録する必要があります。無料のExpress版でもテストは可能ですが、その場合はコードを構成し直すことが必要です。Visual Studio ExpressではWebプロジェクトを利用できないため、コードをWebサイトとして構成する必要があるからです。
WTHeaderedWorkplaceクラスは、HeaderedPanelとして実装したワークプレースコンポーネントです。実装の中身は次のように簡単なものです。
- WTWorkplaceを継承した新しいコンポーネントをWTHeaderedWorkplaceという名前で作成する
- そのコンポーネントにHeaderedPanelを追加し、コンストラクタに次のコードを使用する
public partial class WTHeaderedWorkplace : WTWorkplace {
public WTHeaderedWorkplace() {
InitializeComponent();
headeredPanel.Dock = Gizmox.WebGUI.Forms.DockStyle.Fill;
m_container = headeredPanel;
}
}
WTModuleは、UserControlを継承し、IWTModuleを実装しています。こちらのコードも、各メンバの中身は極めて単純です。ワークプレースモジュールと親モジュールへの参照を保持する2つのprivateフィールドのgetter/setterにすぎません。
// ...
private IWTWorkplace m_hostPanel;
private IWTModule m_callerModule;
public IWTWorkplace HostWorkplace {
get { return m_hostPanel; }
set { m_hostPanel = value; }
}
public IWTModule CallerModule {
get { return m_callerModule; }
set { m_callerModule = value; }
}
Refreshメソッドは仮想メソッドとして実装しており、アプリケーションのモジュールでオーバーライドする必要があります。呼び出された側のモジュールの終了時に保存すべきデータがある場合に、呼び出し側のモジュールを更新する処理を行います。
public virtual void Refresh(WTCancelGenericEventArgs e){ }
WTModuleには、重要なプロテクトメソッドであるCloseModuleもあります。同じ名前のメソッドが3種類あり、それぞれパラメータが異なります。
// CloseModule should be called when the module needs to be closed
// it fires Close event, which is intercepted by WTWorkplaceController
protected void CloseModule() {
CloseModule(false);
}
protected void CloseModule(bool cancel) {
CloseModule(new WTCancelGenericEventArgs(cancel));
}
protected void CloseModule(WTCancelGenericEventArgs e){
if (Close != null)
Close(this, e);
}
モジュールが閉じるときには、この3つのいずれか1つを呼び出す必要があります。1つ目の引数なしのメソッドを呼び出すのは、モジュールが不要となって閉じる場合です。2つ目のCloseModule(bool cancel)を呼び出すのは、次のいずれかの場合です。
- キャンセルボタンのクリックによりモジュールを閉じる場合。このときはパラメータにtrueを渡す
- モジュールを閉じるが、保存すべきデータはない場合。このときはパラメータにfalseを渡す
- モジュールを閉じ、データを保存するが、呼び出し側のモジュールにはデータを渡す必要がない場合
3つ目のメソッドを呼び出すのは、モジュールを閉じるときに呼び出し元のモジュールにデータを渡す必要がある場合です。この処理の例は、本稿のサンプルアプリケーションのModuleNameモジュールとDashboardモジュールにあります。
// in called module, in the event handler for Save button
private void buttonSave_Click(object sender, EventArgs e) {
WTCancelGenericEventArgs evt = new WTCancelGenericEventArgs();
evt.AddEventData("name", textName.Text);
CloseModule(evt);
}
// . . .
// in caller module, the code for Refresh method
public override void Refresh(WTCancelGenericEventArgs e){
if (e.Cancel)
statusBar1.Text = "Name was canceled";
else {
if (e.ExistsEventData("name"))
statusBar1.Text =
string.Format("Welcome, {0}",
e["name"].ToString());
}
}
必要なコードは以上です。次は、スタートアップフォームの登録と設定です。
