Smart.Windows.Mvc補足

ソースだけ公開してもしょうがないので、簡単な使い方について説明してみます(´д`;)

出来ること

まず、Smart.Windows.Mvcの機能概要はこんなんです(・∀・)

  • メインForm内で複数の画面(UserControl)を切り替えて表示する、画面遷移型のアプリケーションの構築をサポートします*1
  • 各画面は、画面IDと画面クラスをセットとしてControllerに登録し、画面IDを指定してControllerに遷移を依頼します
  • 画面IDと画面クラスの登録は、ViewAttributeを使った自動登録が可能です
  • 同じ画面のクラスに対して、異なるIDを登録可能です(同じ画面クラスを新規モードと編集モードで使うとか)
  • なお、同じ画面のクラスに対して異なるIDを割り当てた場合には、画面の初期化イベントでそのIDを引数ViewForwardEventArgsから取得できるので、その値により画面の表示を切り替える事が可能です
  • 画面遷移の種類としては、前画面を閉じて次画面を表示する通常の遷移(Forward)の他に、一時的に別画面を表示して後で元の画面に戻ってくるスタック型の遷移(Push/Pop)をサポートします
  • Controllerに対して終了処理(Exit)を呼び出すと、画面遷移を終了します
  • 画面遷移時と終了時のイベントは、ControllerのForwardingイベントとExitedイベントにより画面外でも認識可能です
  • 各画面では、Controllerによる画面オープン時(OnViewOpen/OnViewActived)、画面クローズ時(OnViewClose)の処理を実装可能です
  • なお、スタック型遷移でPushを行う場合には、元の画面ではOnViewClose()は呼び出されません
  • また、スタック型遷移でPopにより戻ってきた場合には、元の画面では再度OnViewOpen()/OnViewActived()が発生しますが、引数ViewForwardEventArgsのIsPopBackプロパティにより、初回の表示なのかPopによる再表示なのかを判定することが可能です
  • 画面間でパラメータを引き継がせたい場合、ViewParameterAttributeを指定することにより自動引き継ぎが行われます
  • また、特定の対話コンテキスト間のみ存在するデータのライフサイクル管理を行う仕組みとして、ViewContextAttributeを用意しています
  • 画面外から、コントローラを通じて現在表示している画面への通知(Notify)が可能です

ってな感じで、各画面クラス間に依存関係を持たせずに、画面間でデータをやりとりしながら画面遷移していくアプリケーションが作成可能です。

はじめかた

Smart.Windows.Mvcを使ったアプリケーションの作成手順についてです(・∀・)
まず、Visual StudioWindows フォーム アプリケーションを作成して、Smart.Windows.Mvcを参照設定に追加してください。

用意するもの

次に、アプリケーションの作成者が用意するクラスについて示します。
アプリケーションの作成者は、下記のものをプロジェクト内に用意してください。

  • 画面IDの定義
  • 枠となるForm
  • 各画面のUserControl
  • 対話コンテキストのデータを保存するデータクラス(必要に応じて)

ここでは、対話コンテキストは無しで、メニュー画面と、メニュー画面から遷移する2つの画面のみのアプリケーションを作成してみます。


画面IDの定義

画面IDの定義は、次のようなenumを用意すればOKです(・∀・)

public enum Views
{
    Menu,
    Foo,
    Bar,
}

画面IDは、Controller内で管理するDictionaryから画面のTypeを見つけるためのKeyにすぎないなので、キーに使えれば任意のオブジェクトで構いません(・3・)

枠となるForm

枠となるFormは、基本的には何もControlを配置しないFormで構いません(´ω`)
もし、全画面共通のヘッダ、フッタ等を用意したい場合には、このFormにそのコントロールを配置します。*2
ここで作るサンプルでは、共通のヘッダ部のみを用意したいと思います。
また、クライアント領域のヘッダ部以外には、各画面の表示範囲用にPanelを配置します。

次に、このFormのソースを示します。

public partial class MainForm : Form
{
    // 画面遷移コントローラ
    private readonly Controller controller;

    public MainForm()
    {
        InitializeComponent();

        // コントローラ初期化
        controller = new Controller( new ControlViewProvider( ViewPanel ) );
        controller.Forwarding += OnForwarding;
        controller.Exited += OnExited;

        // 画面自動登録
        controller.AutoRegister( Assembly.GetExecutingAssembly() );

        // 最初の画面へ遷移
        Show();
        controller.Forward( Views.Menu );
    }

    // 画面遷移イベント
    private void OnForwarding(object sender, ViewForwardEventArgs e)
    {
    }

    // コントローラー終了イベント
    private void OnExited(object sender, ViewExitEventArgs e)
    {
        Close();
    }
}

まず、画面遷移Controllerクラスをメンバ変数として用意しています。
コントローラの初期化では、ControlViewProviderのインスタンスを引数としてControllerを生成しています。
ControlViewProviderクラスはControl派生クラスを画面として扱うためのプロバイダで、その引数としては画面遷移の親となるControlを指定します。
ここで指定しているViewPanelは、表示範囲用に配置したPanelクラス名です。


次に画面IDと画面クラスの登録ですが、ここではViewAttributeを使った自動登録を行うことにするので、Controller.AutoRegister()に各画面クラスが存在するアセンブリを指定するだけで終了です。(ViewAttributeについては後で)


その後、Controller.Forward()により最初の画面へ遷移を行います。


また、ここではControllerのForwardingイベントとExitedイベントを設定しています。
Forwardingイベントは画面遷移の度に発生するイベントで、引数ViewForwardEventArgsにより、どの画面へ遷移するのかの情報が取得できます。
例えば、各画面外の項目(枠Formに配置した共通ヘッダ、フッタ領域等)を各画面毎に表示内容を変えたい場合等にForwardingイベントを使用します。
Exitedイベントは、Controller.Exit()により画面遷移を終了した後に呼び出されるイベントで、終了処理を記述します。
このサンプルでは、枠となるFormをClose()することにより、アプリケーションを終了しています。

各画面のUserControl

っで、最初に遷移するメニュー画面(MenuView)と、メニュー画面から遷移するFoo画面(FooView)について、イメージとソースを以下に示したいと思います。
なお、MenuViewについてはControllerからの通知ハンドラを何も実装しない形で、FooViewについては全ての通知ハンドラを実装した形でのソースにしてあります。


[View(Views.Menu)]
public partial class MenuView : ControlViewBase
{
    public MenuView()
    {
        InitializeComponent();
    }

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

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

    // 遷移終了
    private void btnMenu3_Click(object sender, EventArgs e)
    {
        Controller.Exit();
    }
}


[View(Views.Foo)]
public partial class FooView : ControlViewBase
{
    public FooView()
    {
        InitializeComponent();
    }

    // オープンハンドラ
    public override void OnViewOpen(ViewForwardEventArgs args)
    {
    }

    // 活性化ハンドラ
    public override void OnViewActived(ViewForwardEventArgs args)
    {
    }

    // クロースハンドラ
    public override void OnViewClose()
    {
    }

    // 通知ハンドラ
    public override void OnViewNotify(Controller sender, ViewNotifyEventArgs args)
    {
    }

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

どちらの画面でも、クラスに対してViewAttributeを付加しています。
Controller.AutoRegister()を呼び出したときには、アセンブリ内からViewAttributeが付加されたクラスを検出して、画面として自動登録する仕組みになっていますヽ(´ー`)ノ


また、どちらの画面もControlViewBaseの派生クラスとなっていますが、これは最初にUserControlとして新規ファイルを作成した後、ControlViewBase派生に書き換えています。
ControlViewBaseとはなんぞや(・ω・)?と言う話ですが、その実態は、UserControlにSmart.Windows.Mvcで定義するインタフェースを空実装したものにすぎません。


ControlViewBaseが実装しているのは下記のインタフェースです。

  • IControllerAware

このインタフェースを実装した画面は、ControllerからSetController()が呼び出されるので、Controllerのインスタンスを取得する事が可能になります。

  • IViewEventSupport

このインタフェースを実装した画面は、画面がオープン(OnViewOpen)、活性化(OnViewActived)、クローズ(OnViewClose)した際に、該当するメソッドが呼び出されるようになります。
ViewForwardEventArgsのメンバでは遷移する画面の画面IDや、遷移時に指定したパラメータの取得が可能です。


ちなみに、OnViewActived()はC++版の時の名残で、画面が可視状態になった後でないとうまく動かない処理があったため、OnViewOpen()とは別に定義されています。
ControlViewBaseに関して言えば、ControlのVisibleがtrueになる前の通知がOnViewActived、trueになった後の通知がOnViewActived()です。
なので、OnViewActived()はあまり使う必要は無いと思います(´Д`)

  • IViewNotifySupport

このインタフェースを実装した画面は、Controller.Notify()による通知を受け取ることが可能になります。


Controllerクラスは、各画面がこれらのインタフェースを実装していれば該当する処理を呼び出し、実装していなければなにもしない作りとなっています。


実際の所、各画面はControlViewBaseを派生せずに、UserControlとして作ってもかまいません(・ω・)
ただし、その場合は該当するメソッドが呼び出されなくなるので、必要に応じて個別にインタフェースを実装する必要があります。
ControlViewBaseとはつまり、その手間を省くために標準で用意したものに過ぎません。
ControlViewBase派生ではなく、独自のControl派生を基底クラスとして使いたい場合を考えて、このような作りになっています。


っで、とりあえず画面遷移に関する基礎はこんなカンジです(・∀・)
これ以上の機能については、Smart.Windows.Mvcのソースに入れてあるサンプルを使って説明した方が良いと思うので、そういう方向で。

*1:一応、Smart.Windows.Mvcの仕組み自体はControl以外も対象にできるようにしているつもりですが、標準でサポートを組み入れているのはControl対象の処理だけです(´д`;)

*2:Compact Frameworkのアプリケーションの場合、共通ヘッダに時計、バッテリ残量、無線LAN強度なんかを表示しています(・∀・)