Form単位での画面制御ならApplicationContextでやりますが、Form内の切り替えの方がちらつかないよね

ASP.NETなお仕事と同時進行で、久々にWindows Formsなお仕事もやることになりました(`・ω・´)
っというわけで、昔のコードから画面遷移フレームワークを持ってきてリファイン。
親Control内で任意の子Controlを切り替えて表示する、要するにWizardライクな機構です。
MasterPageなメインのFormを1つだけ用意しておいて、その中で各画面のUserControlなんかを遷移させるわけですね(・∀・)


このライブラリ、元々はWindows CEのアプリケーション用にC++で作っていたもので、最初の版は2002年くらいに作成。
当時流行始めていたStrutsだとか、WebのMVCフレームワークの考えなんかも取り入れて設計してました。
あと、templateでのメタプログラミング麻疹*1な時期だったり、デザパタを色々と使いたくて、手段の為には目的を選ばない時期だったりもしたので、そういう要素も色々と盛り込んでいました(・ω・)


そのライブラリもなんだかんだと使い続け、今では余計な機能は外出しして、シンプルに画面遷移とコンテキスト管理を実装する800行程度のソースになっています(・∀・)


ちなみに、基本的な遷移以外の機能はこんな感じ。
(新規)ってあるのは、今回のリファインで追加した機能。

  • 画面間パラメータの引き渡し

要するにFlashとかTempData(・ω・)
画面Aから画面Bに遷移する時に、画面Aで使っていたメンバ変数を画面Bに渡したい時にどうするか?
共通領域に保存するとライフサイクルが長くなるし、項目が多くなると問題になるので。
っというわけで、メンバ変数にViewParameter属性を付けておくと、Controllerが前画面の値を取得して、次画面のメンバ変数にインジェクションしてくれるという仕組み。
まあ、画面遷移時にobject[]な引数も指定できるんだけどね(´・ω・`)

  • 対話コンテキスト(新規)

要するにSeamのアレ(・ω・)
ある機能の間、持ち回りたいデータのライフサイクル管理をしてくれる仕組み。
メンバ変数にViewContext属性をつけておくと、そこに対話コンテキストで管理されるオブジェクトがインジェクションされるのです。
コンテキストオブジェクトにIContextSupportを実装しておくと、ライフサイクルの最初(Initialize)と終わり(Dispose)に処理を呼び出してくれたりとかも。

Controllerの処理ステージで拡張機能を呼び出す仕組み。
上記のパラメータの引き渡しや対話コンテキストもプラグインを使って実装。
昔の版では、この辺の処理はController内に組み込まれていたのでした(´・ω・`)

  • 画面の自動登録

Controllerへの画面登録は今まで手動だったんですが、View属性がついているものを自動登録する機能を追加しました(`・ω・´)

  • スタック型遷移

画面遷移の基本は、前画面を破棄して次が面を表示という処理になりますが。
前画面の状態はそのまま(一時的に非表示)に一時的に別画面を表示して、処理後に元の画面に戻ってきたい時なんかもあるので、それ用に遷移状態をスタックに積んで画面遷移する機能。
ちなみに、元画面に戻るPop遷移は多段階の指定も可能。

  • 各画面は非継承(新規)

昔の版では、遷移する小画面用にベースクラスを用意して、そこでテンプレート的に処理を実装していたんですが。
今回の版からは、小画面はControlならなんでも良いことにしますた。
その代わり、IControllerAware(画面へのControllerのインジェクション)、IViewEventSupport(画面表示時、Close時等のイベント)、IViewEventNotify(Controllerから画面への通知)とかのインターフェースを作って、それを実装している場合にのみ処理を呼び出すように変更。
でも、結局アプリケーション固有のベースクラスは作るんだけどね(´・ω・`)


っで、各画面のコードはこんな感じになるのです。

// 同じ画面を新規(EmployeeDetailNew)と編集(EmployeeDetailEdit)の2つのIDで登録
[View(Views.EmployeeDetailNew)]
[View(Views.EmployeeDetailEdit)]
public partial class EmployeeDetailView : UserControl,
                                          IControllerAware,
                                          IViewEventSupport,
                                          IViewNotifySupport
{
    // 対話コンテキスト
    [ViewContext]
    protected EmployeeContext context;

    // 前画面からインジェクションされる値
    [ViewParameter]
    protected string selectedUserId;

...
    public void OnViewShow(ViewForwardEventArgs args)
    {
        // 初期化処理
    }

...
    private void OnCloseButtonClick(object sender, EventArgs e)
    {
        // 画面遷移
        Controller.Forward( Views.EmployeeList );
    }
}

そしてワークフロー制御やDIなんかは外出しです。
本格的にDIなんかをしたかったら、ControllerのForwadingイベントのタイミングでインジェクションができますのダ(`・ω・´)


…あっ、明日はお休みです。

*1:っといっても、CE用のC++は色々制限があって、やりたいことが十分にできなかったりしたんですが(´・ω・`)