UpdateControls.NET がすごい件

ここんとこWPFにプチハマり中(いい意味で)なんですが、ふとしたきっかけで*1 INotifyPropertyChanged Is Obsolete というセンセーショナルなタイトルの記事に釣られ、そこで Update Controls .NET というオープンソースライブラリのことを知りました。

WPF では Model-View-ViewModel パターンが常套手段とされているわけですが、 Model を View にバインディングするためには ViewModel で INotifyPropertyChanged インターフェースとか DependencyObject を継承して実装する*2という、なかなか面倒な手順が必要。しかし UpdateControls を使うと、その手間をかなり軽減できます。

ライセンスは LGPL 、現時点の最新版は 2.0.4.3 (2009/06/28) 、2008/05/16に最初のLGPL版がリリースされたようですがその前は未詳。

理由はよくわからないけど*3、日本語で紹介されている例が(ぐぐった限り)見つからなかった*4ので、簡単に紹介したいと思います。Perry さんの記事には部分的なコードの紹介にとどまってるのと、公式サイトのイントロやデモ(ダウンロードページから落とせる)も最新版を使って動かすには調整が必要みたいなので、現時点でそのまま動く最低限のサンプルを。


ここでは WPF を例にしますが、Formsでも使えるみたい(公式のデモプログラムはFormを使ってる)。

まず公式サイトのダウンロードページ*5 "For Visual Studio 2008" というリンクをクリック、 UpdateControls.2.0.4.3.msi をダウンロードして、普通にインストール。デフォルトでは %Program Files%\Mallard Software Designs\Update Controls に入ります(XP Proで確認)。

で、VS 2008を起動して「WPF アプリケーション」プロジェクトを作成。まずは参照に UpdateControls のコンポーネントを追加します。VS の .NET コンポーネントに以下の6つが追加されてるはずですが、今回使うのは UpdateControls と UpdateControls.XAML の2つ。

  • UpdateControls ← これと
  • UpdateControls.Forms
  • UpdateControls.Installer
  • UpdateControls.Themes
  • UpdateControls.VSAddIn
  • UpdateControls.XAML ← これ。

で、まずは Model にあたる簡単なクラスを作ります。

Name.cs

using UpdateControls; // Independent クラスを使うためだけ

namespace TestUpdateControl
{
    class Name
    {
        #region private fields

        private string _first;
        private string _last;

        #endregion // private firlds

        public Name()
        {
            this.First = "Barack"; // すぐ後で作るプロパティ
            this.Last = "Obama";   // 同上
        }
    }
}

で、同じファイルに UpdateControls が利用するプロパティを作ります。Visual Studio 2008 Standard 以上だと、"Generate Independent Properties" というコマンドが Ctrl-D, G というショートカットキーにバインドされて自動生成できるらしいんですが、ここでは Express Edition を使ったのでサンプルを真似て手で打ちました。まぁそれでも OK ということで。

    class Name
    {
        ...
        #region Independent properties

        private Independent _indFirst = new Independent();
        private Independent _indLast = new Independent();

        public string First
        {
            get { _indFirst.OnGet(); return _first; }
            set { _indFirst.OnSet(); _first = value; }
        }
        public string Last
        {
            get { _indLast.OnGet(); return _last; }
            set { _indLast.OnSet(); _last = value; }
        }

        #endregion // Independent properties
        ...
    }

これで Name.cs は完成。残るはプレゼンテーション部分(ラップクラス)とビュー部分(XAML)なんですが、どっちから作るかは考えどころで、好みの問題かも。説明しやすいのでラップクラスから示します。

NamePresentation.cs

namespace TestUpdateControl
{
    class NamePresentation
    {
        private Name _name;
        public Name TheName
        {
            get { return _name; }
        }
        public NamePresentation(Name name)
        {
            _name = name;
        }
        public string FirstLast
        {
            get { return _name.First + " " + _name.Last; }
        }
        public string LastFirst
        {
            get { return _name.Last + " " + _name.First; }
        }
    }
}

これだけ! あとは XAML を作って、DataContext を作ってやるのみです。

Window1.xaml

<Window x:Class="TestUpdateControl.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:u="clr-namespace:UpdateControls.XAML;assembly=UpdateControls.XAML"
    Title="Window1" Height="180" Width="200">
    <StackPanel Name="panel">
        <TextBlock Text="First Name"/>
        <TextBox Text="{u:Update TheName.First}"/>
        <TextBlock Text="Last Name"/>
        <TextBox Text="{u:Update TheName.Last}"/>
        <TextBlock Text="First-Last"/>
        <TextBox Text="{u:Update FirstLast}"/>
        <TextBlock Text="Last-First"/>
        <TextBox Text="{u:Update LastFirst}"/>
    </StackPanel>
</Window>

UpdateControls.XAML アセンブリ内の同名前空間をインポートしてることに注意。その上で u:Update というキーワードに続けてバインドされた Presentation クラスのプロパティを指定しています。Presentation クラスとの結びつきは Window1.xaml.cs で指定していて、以下の通り。

Window1.xaml.cs

using System.Windows;

using UpdateControls;
using UpdateControls.XAML;

namespace TestUpdateControl
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        //private Name _person = null;
        
        public Window1()
        {
            InitializeComponent();

            panel.DataContext = new NamePresentation(new Name());
            // Window1 に直接バインドしても表面の挙動は同じ
            //DataContext = new NamePresentation(new Name());
        }
    }
}

公式サイトの例では UpdateControls.XAML.ForView.Wrap という静的クラスを使ってたりするんですが、この単純な(すぎる?)例では必要ないみたい。

で、ビルドして走らせると以下のようなウィンドウが出ます。テーマを切ってるせいで味気ないキャプチャですが、Vista とか Vienna なら多少色気も出るでしょう、多分。

上の2つのテキストボックス(First Name と Last Name)をいじった上でそのボックスからフォーカスを外すと、下の2つのテキストボックス(First-Last と Last-First)が変化します。

なんかもう ViewModel ちょう面倒とか思ってた僕には渡りに船のライブラリなんですが、いかがでしょうか。これを使うことによる制約やデメリットなどはまだわからない段階ですが*6、かなり面白そうだと見込んでいます。WPF + MVVM を使う気が増すというか。

追記: name付き見出し記法がさっぱりわかんなくなっててキーワードページに変なポストが……ごめんなさい。目の前の液晶に角張った固いものを押し当てて綺麗なグラデーションが出るようにしてしまいたい。

*1:INotifyPropertyChanged を軸にいろいろぐぐってただけ。

*2:実例は「[http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx:title=WPF のための MODEL-VIEW-VIEWMODEL (MVVM) デザイン パターン]」(MSDNマガジン Feb. 2009)や「[http://blogs.wankuma.com/kazuki/archive/2009/02/23/168586.aspx:title=Model View ViewModelパターンでハローワールド ]」(かずきのBlog)などを参照。

*3:他にもう有名な何かが存在するとか?

*4:[http://cs.gogo-asp.net/blogs/naoki/archive/2007/02/09/ASP.NET-AJAX-_6E30C930AD30E530E130F330C8304C30C030A630F330ED30FC30C930EF53FD80673059300230_.aspx:title=ASP.NET のコントロール] とか [http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/win.102/B25023-01/dcmethods.htm:title=Oracle のメソッド] とかは出る。

*5:もちろん CodePlex でもOK。

*6:正直、もっとすごい方々に期待してます。