WPFをうかうとViewとModelを綺麗に分離したアプリケーションが割りと簡単に作れるように出来ていると思う。
後は、この特徴を余すことなく利用したフレームワークが出てくれば!?と思ってたら、出てました。Composite Application Guidance for WPFっていうのが。
余すことなく使っているか?といわれると、まだ使いこなせてないから微妙だけど、HelloWorldしてみた限りだといい感じなんじゃないかと思う。EntLib4.0のUnityを使ってるからクラス間を祖結合に出来たりとかしそうだ。夢は広がる。
夢を見てるだけじゃいけないので、早速HelloWorld的なアプリケーションを作ってみようと思う。完成系は、Hello Worldと書かれたテキストが画面上に表示されるだけという面白くもなんとも無いものになります。でも何事も基本はそういうもんだよね。
dllを作ろう
とりあえず、CodePlexのComposite Application Guidance for WPFのサイトからソースをダウンロードできるのでダウンロードする。ダウンロードしたファイル内にあるOpen Composite Application Library Solution.batをダブルクリックするとVisual Studioが立ち上がるのでReleaseモードでコンパイルして出来上がるdllを適当な場所に避難しておく。出来上がるDLLは下の5種類(一部EntLibのものもあるけど)
- Microsoft.Practices.Composite.dll
- Microsoft.Practices.Composite.UnityExtensions.dll
- Microsoft.Practices.Composite.Wpf.dll
- Microsoft.Practices.ObjectBuilder2.dll
- Microsoft.Practices.Unity.dll
ソースコードのボリュームも思ったほどない感じなので、その気になれば読むのにちょうどいいかも。とりあえず今は使い方から入って気になった部分を読む程度にしようと思う。
プロジェクトの作成
CompositeHelloWorldという名前でプロジェクトを作る。そこにさっき作ったDLLもコピーして参照設定を追加する。もう1つCompositeHelloWorldModuleという名前でクラスライブラリプロジェクトを作る。こっちにも、先ほど作ったDLLを参照に追加する。
クラスライブラリプロジェクトには、WPF関連の参照も定義されていないので、WindowsBase, PresentationCore, PresentationFrameworkの3つも参照に追加する。
Shellの作成
いきなり、よくわからない言葉が出てきたけど、Shellというのは要はレイアウトだけを定義したウィンドウのことらしい。
Window1という名前で定義されているので、リファクタリングを駆使してShellという名前にする。
<Window x:Class="CompositeHelloWorld.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hello World Shell" Height="300" Width="300">
<Grid>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CompositeHelloWorld
{
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
}
}
}
そして、App.xamlにあうrStartupUriの記述も消しておく。普通のWPFのアプリケーションとは違い別の場所でメインウィンドウを指定する形になっているようだ。
<Application x:Class="CompositeHelloWorld.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
これでやっと最初の準備が終わった。ShellのXAMLをいじっていく。とりあえず、ItemsControlに対してcal:RegionManager.RegionName="MainRegion"をつければいいだけみたい。ふむ。
<Window x:Class="CompositeHelloWorld.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:cal="http://www.codeplex.com/CompositeWPF"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hello World Shell" Height="300" Width="300">
<!-- ItemsControlにMainRegionという名前をつける! -->
<ItemsControl cal:RegionManager.RegionName="MainRegion" />
</Window>
この状態でアプリを起動してもメインウィンドウすらないものになってしまう。(App.xamlかrStartupUri消したからね)
Composite Application Guidance for WPFでは、エントリポイント的な役割を持つものとしてBootstrapperというものがいるようだ。
ということで、Bootstrapperを作っていく。Bootstrapperは、UnityBootstrapperを継承して作成する。継承すると、CreateShellメソッドが抽象メソッドらしく実装を強要される。
ここでは、名前の通りShellのインスタンスを作って表示するのが仕事のようだ。さっき作ったShellクラスのインスタンスを作ってShowするコードを書く。
そして、GetModuleENumeratorというメソッドもオーバーライドする。これは、多分ほかのモジュールをどういう風に探して列挙するかという仕事を担うIModuleEnumeratorインターフェースの実装クラスw作るところみたいだ。
軽く見たところ、ソース内にモジュールを定義する方法と、構成ファイルに定義する方法と、特定のフォルダにあるdllからモジュールを見つける方法とかがあるみたいだ。HelloWOrldでは、ソースコード内に定義するようになってるので、それようのクラスのStaticModuleEnumeratorクラスのインスタンスを作って返すようにする。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Composite.UnityExtensions;
using Microsoft.Practices.Composite.Modularity;
namespace CompositeHelloWorld
{
public class HelloWorldBootstrapper : UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
// どうもメインウィンドウを作って表示するらしい。
var shell = new Shell();
shell.Show();
return shell;
}
protected override Microsoft.Practices.Composite.Modularity.IModuleEnumerator GetModuleEnumerator()
{
return new StaticModuleEnumerator();
}
}
}
そして、BootstrapperをAppクラスのコンストラクタあたりでインスタンス化してRunを呼び出す。
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace CompositeHelloWorld
{
public partial class App : Application
{
public App()
{
var bootstrapper = new HelloWorldBootstrapper();
bootstrapper.Run();
}
}
}
個人的には、Startupイベントあtりに書きたい処理だけどHelloWorldではコンストラクタを使っている。ちょっと悲しい。この状態で実行すると、やっと真っ白なウィンドウが表示される。
モジュール作成!
次に、モジュール作成に取り掛かる。モジュールは、IModuleというインターフェースを実装して作るようだ。IModuleインターフェースはInitializeメソッドを持つだけのシンプル構造。さくっと作ってみよう。ちなみに作るプロジェクトはCompositeHelloWorldModuleプロジェクトのほうになるので注意。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Composite.Modularity;
namespace CompositeHelloWorldModule
{
public class HelloWorldModule : IModule
{
public void Initialize()
{
// 処理はまだない
}
}
}
このモジュールを、メインのプロジェクト側に認識させないといけない。StaticModuleEnumeratorはソースコード内にモジュールについて記述するので、HelloWOrldBootstrapperのGetModuleEnumeratorにちょっと手を加える。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Composite.UnityExtensions;
using Microsoft.Practices.Composite.Modularity;
using CompositeHelloWorldModule;
namespace CompositeHelloWorld
{
public class HelloWorldBootstrapper : UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
// どうもメインウィンドウを作って表示するらしい。
var shell = new Shell();
shell.Show();
return shell;
}
protected override Microsoft.Practices.Composite.Modularity.IModuleEnumerator GetModuleEnumerator()
{
// HelloWOrldModuleを追加する
return new StaticModuleEnumerator().AddModule(typeof(HelloWorldModule));
}
}
}
もちろんプロジェクトの参照を追加してCompositeHelloWorldプロジェクトからCompositeHelloWorldModuleプロジェクトの中のクラスを使えるようにしておく必要もあります。
ついに画面を作る!
やっとHelloWorldの文字をうつときがきた。HelloWorldCompositeModuleプロジェクトにWPFのユーザコントロールを追加してTextBlockを1つ置いてHelloWorldをTextプロパティに設定する。ユーザコントロールの名前は、HelloWorldViewにしておいた。このUserControlがShellのほうに表示されるのだろう。
<UserControl x:Class="CompositeHelloWorldModule.HelloWorldView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Text="Hello world" />
</UserControl>
最後に、ここで作ったUserControlをShellに表示する処理を追加する。処理を追加するのはHelloWorldModuleになる。
HelloWorldModuleにコンストラクタを追加して、IRegionManagerを受け取るようにする。多分UnityContainerがいい感じにインスタンスを渡してくれるのだろう。
渡されたインスタンスはフィールドに保持しておく。
そして、InitializeメソッドでMainRegionという名前のRegionを取得してHelloWorldViewを追加する。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
namespace CompositeHelloWorldModule
{
public class HelloWorldModule : IModule
{
private IRegionManager regionManager;
public HelloWorldModule(IRegionManager regionManager)
{
// 多分Unityからいい感じのインスタンスが渡されてくる
this.regionManager = regionManager;
}
public void Initialize()
{
// Shellに定義したMainRegionを取得
var region = regionManager.Regions["MainRegion"];
// そこにHelloWorldViewを追加する
region.Add(new HelloWorldView());
}
}
}
ついに完成
長かったけど、これで完成になる。実行すると、Hello worldが表示される。
Hello worldを終えての感想
Composite Application Guidance for WPFでHello worldをしてみたけど、結構手順が多くて大変だと感じた。
めんどくささの割りに、Hello worldではメリットは特に感じれなかった。
ただ、サンプルとかを見ている感じだとModuleを入れ替えるだけで画面の表示が変わったりModule間を割りと祖結合に出来たり、DIコンテナであるUnityContainerベースなので色々インジェクションしたり、EntLibのApplication Blockと連携出来そうだったりして、いい感じだとは思う。
tりあえず、もうちょっと使ってみようかな。イベントやコマンドとの連携とかを重点的に見てみるぞぉ。