Edited at
Kazoo04Day 6

むずかしくないWPF

More than 3 years have passed since last update.

kazoo04 Advent Calendar 6日目です(6日目とは言っていない)

みんな気合の入った記事を書いているので、頑張って書かないとなぁって書いていたらどんどん期限をオーバーしてしまって…ごめんなさい。

kazoo04氏の周りの人はみんなMacだったりLinuxだったりを使っていてあんまりWindowsプラットフォームに興味がないかもしれませんが、僕が書けそうな内容がこれしかなかったので、今回はWindowsのGUIプログラミング、WPFについてのおはなしです。

ちなみに: 今アツいかもしれないWindows Store AppはXAMLとC#を用いて書きますが、WPFじゃありません(重要)。ですが、概念は一緒です。(裏を返せば、概念以外は大きく違いますので気をつけてください……)

これ、XAML Advent Calendarに書くべきだったかなぁ。


WPFは、むずかしい?

WPF こと Windows Presentation Foundation は、これまで使われてきた GDI/GDI+(とそれをラップするWinForms) UIサブシステムを置き換える、新しいUIサブシステムです。とは言っても、登場は2006年、.NET Framework 3.0に含まれてリリースされたのが最初なので、もはや新しいとは言えませんね。

そんなWPFですが、未だに「よくわからん」だとか「むずかしい」だとか「WinFormsでいい」だとかそんなお話を聞きます。小さなアプリケーションならまだしも、ある程度規模のあるアプリケーションであればWPFを採用しない理由はもはや存在しません。もしもあなたのWinFormsアプリケーションがListBoxを一つでも使用しているのなら、WPFへの移行を考えるべきです!


WPFのむずかしさの正体

WinFormsは理解できても、WPFはよくわからないなにかとなっている、という人も多く居ることかと思います。XAMLだったりデータバインディングだったり、新しい知識が多くて敷居が高いですよね。そんな人に対しては悲報となりますが、この記事では、データバインディングの説明もXAMLの説明もしません。

なぜなら、WPFを「難しい何か」にしてしまっているのは、「XAML」でもなく「データバインディング」でもなく、考え方の違いだと考えているからです!考え方の違いさえ掴んでしまえば、後はデータバインディングもXAMLの書き方もおのずと理解できるはずです。


WinFormsからのパラダイム・シフト

さて、ここで前置きとしてデータバインディングの話をしますが、理解できなくても問題ありません。話の本筋からは逸れた話となります。

WPFアプリケーションを構築する際に、WinFormsやその他のUIツールキットでアプリケーションを構築していた人がまず陥りがちなのが、コントロールに値を設定する、という記述をしてしまうというトラップです。

たとえば、


example.cs

textBox1.Text = "Hello, World!"


というコードですら、WPFではもはや許されません!私達は、Bindingを使うべきなのです!


データバインディングのしくみ

データバインディングについては、@ITさんの"連載:WPF入門:第5回 WPFの「データ・バインディング」を理解する"が詳しいです。詳しくはそちらをお読み頂くとして、基本的なところだけおさらいしておきましょう。

以下のような Data クラスのインスタンスがデータコンテキストであるとき、


Data.cs

public class Data

{
public string Text
{
get { return "Hello, World!"; }
}
}

TextBoxにTextをバインドしてやることで


example.xaml

<TextBox Text="{Binding Path=Text}" />


Hello,World! となるわけですね!


XAML時代の考え方: 「描く」のではなく「組み立てる」

Bindingについて少し説明をしましたが、物事の考え方が変わっているのに気付いたでしょうか。WinFormsでは、データをプログラマがUI要素に投げ込んでいました。しかしWPFでは、プログラマはUI要素がどのデータを読むべきなのかを指定しただけです。実際にデータを取り込むのはWPFがやっています。

そう、これまでは

TextBlockのTextにData.Textを表示する

のに

Data.Textを取得し、textBlock1.Textにそれを代入する

という操作をする必要がありましたが、これからは

TextBlockのTextにData.Textを表示する

と書くだけでよいのです!そのまま!

実は、Bindingだけではなく、WPFのユーザインタフェースを作る上で、全てが同じように変わっています。

これまでのユーザインタフェースサブシステムの多くは、一連の手続きにしたがって描く、いわば「絵描き」のモデルを使っていました。UserControlで全てを自分で描画するコントロールを作った方や、オーナードローを使ったことがある方ならすぐに分かると思います。

たとえば、次のリストをWinFormsとWPFでそれぞれ描画する場合の考え方を比較してみましょう。

gochilist.png

(アイコンは yaplus氏のイラストを使用しています: 「ごちうさフラットアイコンまとめ」/「やぷらす」のイラスト [pixiv]

WinFormsでは、こうなるでしょう。


  1. 枠を描く

  2. 背景を描く

  3. 1つ目の要素の画像を描く

  4. 1つ目の要素のテキストを描く

  5. 2つ目の要素の画像を描く

  6. ...

対して WPFでは、こう組み立てます。



    • 複数のアイテムを表示するリスト


      • 画像

      • テキスト





WinFormsと比べて、WPFでは「どのようなリストを作りたいのか」が明確になっていることが分かるでしょうか。

そう、「どうしたいか」ではなく、「何をしたいか」を記述するのがWPFのやり方です!

今回はかなり簡単に説明していますが、実際のところ WinForms では要素の位置を計算する必要があったり、描画領域をクリップしたりと非常に煩雑な操作が必要となります。しかし、WPFではほとんどこの通りのまま作成することができます。


宣言型のいいところ

WPFの「組み立てる」方法を指定するというやり方について、「宣言型」という言葉が一般的に使われます。対して、WinFormsの「絵描き」のやり方は「手続き型」と呼ばれます。

宣言型のいいところはいくつか挙げられますが、今回は「意図が満たされさえすれば、どのような手続きで実現されてもいい」というところに注目してみましょう。

宣言型は「何をしたいか」を記述するので、それがどのように実現されるかは問いません。これを利用して、たとえば、リストを簡単に仮想化したり、コントロールにアニメーションを付けたりすることができます。


リストの仮想化

先ほど例に挙げたリストですが、たとえば中身が10万件あった場合はどうなるでしょうか。WinFormsでは、手続き型のプログラムでは、そもそもリストを作る手順を変更しなければいけませんね。だって「どうするか」が大きく変わりますから。

たとえば ListView を仮想表示する時には、ListView.VirtualMode プロパティ (System.Windows.Forms) - MSDN に記述されているような手順を踏む必要があります。

それと比べてWPFでは、「何をしたいか」の大元は変わらず、それに「仮想的に表示する」というのを付け加えるだけで、あとはWPFが良きように計らってくれます。

具体的には、



    • 複数のアイテムを仮想的に表示するリスト


      • 画像

      • テキスト





で終わりです。簡単ですね。


データはどこから来るの?

XAMLでユーザインタフェースを組み立てるという方針が分かったところですが、「何をしたいか」を書いただけでは「何に対して」という要素が抜けていますね。先ほどの例では、リストの組み立て方を指定しましたが、中身に何を表示するかは示していません。

ここで、冒頭に示した「Binding」が登場するわけです。

詳しくは書きませんが、だいたい以下の図のような形で指定します。

wpfs.png

図では、Windowに対してWindowViewModelのデータを表示するように指定しています。(MainWindowViewModelは今回の場合、特別な機能を何も持たない、単なるクラスだと思ってもらえれば結構です)

子要素ではどのデータを指定するかを選択するだけです。その方法がいわゆる「Binding」です!(繰り返しますがこの記事では説明しません)

これでめでたく、データとその表示方法が出揃いました。あとはWPFがやってくれます!


むすび

なんとなくの形で示したWPFですが、その力強さと表現力に、少しでも興味を持って頂けたら幸いです。

WPFは、決して「重い」そして「複雑」で「難しい」だけのUIフレームワークではありません。

WPFを正しく利用すれば、WinForms時代とは一線を画す開発の容易さを体感できます。

また、WPFを用いて作成されたアプリケーションも相当の完成度を誇ります。アニメーションやメディアを駆使しなくても、WPFを使う意味はあるのです。

WinFormsから大きな変化があるため、慣れが必要なのは否めませんが、その苦労に見合うだけ、あなたに大きな利益をもたらすはずです。

まだWinFormsでいいやとお考えの皆さんも、WPFに挑んでみたけどよく分からずすぐにやめてしまったという方も、WPFでちょっと気取ったソフトウェアを作ってみませんか?


補足: MVVMパターン

今回示したのは、実は、「MVVM(Model-View-ViewModel)パターン」と呼ばれるアーキテクチャに沿っています。MVCパターンなどのいわゆる「三層アーキテクチャ」のWPF特化版だと思っていただければ概ね間違いありません。

今回はその中でも、MVVMViewViewModelを用いた形です。ViewModelは単なるクラスだと述べましたが、特定のインタフェースを実装することによって、または特定のクラスを継承することによって、その可能性はさらに広がります。

MVVMパターンは様々な流儀があり、様々な流儀があるということは様々な宗教があるので、ここでは触れません。

さらに詳しく学びたい方は、たとえば以下のWebサイトを参照すると良いかもしれません。

いまさら聞けない「MVVM + Messenger パターン」超入門 - present

はらぺこ日誌» ブログアーカイブ » WPF と MVVM で時刻入力コントロール


MVVMパターンに対するライブラリ導入のすすめ

MVVMパターンはライブラリを使用することでより簡単に利用できるようになります。

個人的にお勧めなのが Livet - WPF4/4.5 MVVM インフラストラクチャ です。

謎の力によって公式サイトが消滅しているので怪しげなライブラリに見えますが、公式サイトが消滅しているのはお約束みたいなもので、実際のところ最も完成度が高いのではないかというライブラリです。

国産なため、比較的日本語のドキュメントが充実しています。たとえば以下のような。

Livetを使ってすっきりWPFアプリを作る ① 導入時のメリット・デメリット - 亀岡的プログラマ日記