Pocket

WPFは、Windows Presentaion Frameworkの略で、Windowsのデスクトップアプリケーションを作るフレームワークのことです。デスクトップアプリケーションとは、スマホにアプリをインストールするようなイメージで、Windowsにアプリをインストールする利用形態のアプリケーションです。

Windowsのデスクトップアプリケーションの王道は、Windowsフォームです。いつから存在するのは知らないのですが、相当昔(20年ぐらい前)から存在するフレームワークだと思います。特に技術的進化もないまま、互換性だけが延々キープされているというレガシーな仕組みです。

WindowsフォームとWPFでの開発を両方やって、今でもWPFで作った販売管理システムをメンテナンスしています。Windowsフォームで作ったものを、WPFで焼き直した経験があります。今はWPFをメインにしています。

WPFに移行するメリットが、あんまりない

新規開発ならWPFを選んで知見を貯めるのも有効ですが、既存のWindowsフォームをWPFに焼き直すメリットは感じられないです。

ここでのメリットは開発者目線でみたもの。「生産性」「保守性」「将来性」「技術者の確保」などが向上する見込みがある場合を指しますが、同じ機能を持っているWebアプリをPHPからPythonに差し替えるだけじゃ、あまり意味がないですよね。

WPFに移行するなら、Windowsフォームのあり方を捨てて(負債になっている箇所を整理して)、再構築するビジョンを定めないとダメです。それに、WindowsフォームとWPFの開発スタイルは同じではありません。VB.NETがc#に変わるみたいな話じゃなく、そもそも開発手順が異なります。

Windowsフォームの開発スタイルはこうです。

  1. フォーム(ユーザーコントロール)を作る
  2. ドラッグアンドドロップでパーツを貼り付ける
  3. イベントハンドラに処理を書く

WPFはこうなります。

  1. ユーザーコントロールを作成
  2. XAMLを記述して画面をデザイン
  3. XAMLに対応するViewModelを作って紐付ける
  4. XAMLにデータバインディングやコマンドを設定してViewModelにロジックを書く

アーキテクチャや開発手法がまるで違うので、WindowsフォームとWPFは全く別物です。

WPFは、今のGUI開発でスタンダードになっている「MVVM」の考え方が浸透しています。WPFでアプリを作ってMVVMの良さを味わうには、別途フレームワーク(Prism等)を入れる必要があります。素のWPFは非常にとっつきにくい。Windowsフォームのほうが楽だろに倒れるのも、私はよくわかります。

MVVMを使い込む=データバインディングをフル活用です。データバインディングにより、どのモデルの、どのプロパティを出すかだけ考えればビューの表示が切り替わります。

データを変えるだけでUIが更新されるアーキテクチャは、実はかなり先進的です。今だとReact/Vue.jsなどによって宣言的UIとUIと状態の分離は当たり前になりましたが、WPFはそれらが出てくる数年前に似たようなアーキテクチャを実装していました。パラダイムシフトが激しすぎて、ほとんどがついてこれなかったけれど。

WPFで突然やってきたパラダイムシフト

WPFの提唱したMVVMは、今までのWindowsフォーム開発と全く違うパラダイムシフトを求めました。

Windowsフォームの時代だと、コードビハインドにロジックを埋め込むしかない。WPFになると、コードビハインドはやめてCommandを使い、制御にはViewModelを使えになります。コードビハインドは死ね、と。ちゃぶ台返しにも程がありますね。こう書くと。

コードビハインドを排除することをWPFは推奨しています。端的にはコールバックをUIに埋め込んでしまうと、再利用が難しいからです。UI開発では、イベントハンドラは同じだがコールバックだけ差し替えたいケースによく出会います。 そのような場合、コードビハインドでイベントハンドラのロジックを埋め込んで画面のインスタンスで分けて〜みたいなことやるとクソなので、WPFではXAMLでイベントハンドラに対してCommandを公開し、Commandを実装をするViewModelを差し替えることで対応します。そうすると、違うのはViewModelのインスタンスだけになります。

キーイベントにはコードビハインド追い出せないだろって思うかもしれません。ですが、キーイベントには、「KeyBindings」っていう機能があって、XAML上で定義できます。マウスイベントは「MouseBinding」ってのがあります。コードビハインドでないと処理できないイベントハンドラは、恐らく存在しません。ちなみに、以下はキーイベントをKeyBindingsでフックするXAMLです。

<TextBox VerticalAlignment="Center" Text="{Binding Hoge,Mode=TwoWay,
            UpdateSourceTrigger=PropertyChanged}" MinWidth="80">
    <TextBox.InputBindings>
        <KeyBinding Command="{Binding FindHogeCommand}" Key="Return" />
    </TextBox.InputBindings>
</TextBox>

昨今ではWebAPI連携が当たり前なので、WPFで画面を作ってWebAPIを叩いてJSON取ってきて、デシリアライズしてオブジェクトに戻す。データバインディングでビューに変更を自動で反映。こういう作りだと、WPF+MVVMがベストです。うちの販売管理システムも、この作りです。

SQLServerを立ててLAN経由で接続して、SQLをイベントハンドラにもりもり書いちゃってるような業務アプリだと、WPFにしても旨味が少ない。SQLの組み立てのようなビジネスロジック、WPFに持ってきた所でそのままコピペするだけだから。

WPFはプレゼンテーションのフレームワークですから、UIの切り替え・表現・更新について、よりモダンな仕組みを提供しています。ビジネスロジックはスコープ外なんです。

レスポンシブ対応とデザインの変更

わかりやすいWPFのメリットです。ここは、WPFの圧勝です。XAMLによって、画面サイズに応じて動的に変わります。Windowsフォームは絶対配置が基本なため、コレが弱い。WPFはXMLでフォントサイズやグリッドの見た目、ボタンの見た目等を変更できます。CSSライクな設定ができる(スタイルを継承できる)ので、見た目の共通化はすごい楽です。データグリッドの1行2列構成も簡単に実装できます。

Windowsフォームには、その種の機能がまったくない。データグリッドのようにセルの大きさ、ソート可否、フォーカスがあたった時の色、ホバー時の色を、GUIでチマチマ設定しないといけないので辛い。

GUIでいちいちドラッグアンドドロップでスタイルやイベントハンドラを再定義するのはバカすぎるので、可能な限り部品化した経緯があります。DataGridを例に出すと、DataGridを継承したGridクラスを作って、カラム定義やサイズ、位置の指定、コールバック等を定義して、サブクラスがコールバックを実装するような感じにして、多少マシになった。

XAMLになれると、テキストボックスやデータグリッドなどをドラッグアンドドロップして貼り付ける一連の作業がアホらしくなります。XAMLはテキストデータですし、UIのスタイルもStyles.xmlとか使えば共通化できます。テキストでUIを表現できることは正義です。

データバインディングをうまく使うと、ReactやVue.jsのような宣言型UIのスタイルで開発ができます。ボタンを押すと表示と非表示を切り替えるようなUIの場合、パネルのVisibilityを切り替えるためのフラグの変数にバインドしておきます。ボタンが押されたらViewModel上のメソッドを呼び出し、True/Falseを切り替えると、データバインディングによってViewの表示・非表示が切り替わります。

Windowsフォームだと、画面のUIコンポーネントに対して名前をつけて、XXTextBox.Text = “aaa” みたいなコードを書くかもしれません。MVVMでは、ご法度です。やれなくはないですけど、ViewModelにViewのインスタンスを渡すのはViewとViewModelが疎結合にならないので、ダメです。ViewModelは自分のデータをどのViewのパーツが参照しているのか、知る必要がない。

ViewModelのプロパティの再代入を行うことで、そのプロパティを参照しているViewが再描画される。これがViewModelの肝です。WPFが目指したのは、宣言型UIの開発スタイルだったと考えています。Windowsアプリの開発者には、全く伝わっていないですけど。

パフォーマンス

今どきのマシンであればあまり問題にならないと思いますが、10万行・20万行みたいなグリッドだと、Windowsフォームのほうが強いと思います。描画パフォーマンスは、ヘボいマシンだとWindowsフォームのほうが速い。XPとWindows7のルックアンドフィールの違いのようなものだと思います。

WPFで重くなるとしたら、やっぱデータグリッドでしょう。iOSのTableViewでよくあるように、データは100件あるけど目に見えない所は初期化せず、スクロールに応じてセルを初期化してそのインスタンスを使い回すようにしないと、件数に応じて激重になる。Deferredってやつかな? 数万件の一覧表示とか、無いと思うけど。2000件程度なら大して重くならない。

あと、CollectionViewのGroupingは重い。これは使わないほうがいい。データグリッドをグルーピングすると、iOSのTableViewのセクションヘッダーみたいなものが作れる。明細一覧表示のように、どこからどこまでが同じ伝票なのよってのがわかりにくいUIにおいてちょっと便利だけど、遅いほうが嫌がられる。

印刷(帳票作成)

PrintDocumentで印刷するならどっちも変わらない。座標軸指定してDrawStringするという古のアレ。

WPFはちょっとモダンな仕組みがある。XAMLを帳票テンプレートにしてFixedPageでXPSに流し込むという技。出力帳票のレイアウトをXAMLで書けてバインディングすれば帳票ができます。

ただ、単純な帳票しか作れません。データの中身に合わせて罫線を引くような書き方をすると、かなり重くなった記憶があります。5年ぐらい前なので、今だとあまり当てにならないけど。

請求書一括100pは超えるであろう帳票の印刷については、自助努力で頑張るかツール買うしか無いと思います。標準機能で140pのPDFのプレビューが秒で出せたら、教えて欲しい…

ウチはサーバーサイドでPDFを生成してダウンロードしてから、ダイレクト印刷を行う方法を取りました。なんか、CrystalReportに近い拡張機能がMicrosoftから出たらしいので、それを使ってもいいかも。

将来性

どっちも同じです。開発手法が大きく変わることはなく、大幅なバージョンアップも考えにくい。

MicrosoftもさすがにWindowsフォームとWPF資産を切り捨てることはしない。Windows11でWindowsフォームアプリとWPFアプリは作れなくなる or 動かなくなる、なんてことはあり得ないでしょう。粛々と淡々と生き延びていく。

デスクトップアプリケーションの技術的進化があるとすれば、Flutterのようなクロスプラットフォームフレームワーク方面だけだと思います。Flutterはすごいです。iOS / Android / PWA / WindowsAppを、ワンソースで作れる時代を作ろうとしてます。弊社はFlutter大好きです。

Windowsデスクトップアプリケーションの開発技術は、とてもマイナーな技術。2021年!よし!今からWindowsのWPFを学んでスキルアップ、なんて話はありません。Webとモバイルが全盛の時代で、Windowsデスクトップアプリケーションに資源を新規に投入する理由が少ない。WPF専業です!って聞いたことがない。

2020年以降でフロントエンドやるならJavaScript一択だと思う。当方は2012年頃に作ったシステムがWindowsフォーム(主に印刷面の要件でこれしかなかった)であって、2018年にWPFに焼き直したという経緯があるだけで。

技術者の確保は母集団形成の段階で難しそう。日本語及び英語の技術情報のボリュームも、WPFはPHPに比べると体感で1/100です。1%。Stackoverflowだけが友達です。

WPFは、開発手法やアーキテクチャ、フレームワークに関する記事が極めて少ない。WPFで100画面近いシステムを作っていい感じにコードをメンテナンスする仕組みを掲載した技術的記事、どこ探してもない。データバインディング便利!MVVMしよう的な入門に毛が生えたような情報しかなく、アプリケーションの作り方や保守性のヒントになる情報は全然ありません。

業務アプリケーションといっても、WPFが使われているのは3D関係が多い気がします。OpenGLを使ったシュミレーション系。事務処理メインの業務システムに出会ったケースはあまり多くないです。

https://qiita.com/sekky0816/items/91bc38b0242494ba9954

意外と情報がなくて困ったのが、WindowsのインストーラーやEXEのビルド方法などの、Windowsアプリケーションの配布技術。ClickOnceではない方法を探すのに苦労しました。Microsoft Visual Studio Installer Projects を使いました。

保守性

ライブラリのメンテナンスという観点で言えば、Windowsフォームのほうがコストが低くなる可能性が高い。OSSが勃興する前から存在する技術なので、NuGetなどでOSSのコードを取得してバージョンアップ対応するケースが、WPFより少なかったです。

VisualStudioのバージョンアップに応じてビルドだけ通せば、今後もずっと使える。.NET Frameworkのバージョンアップはかなり簡単です。サポートされる期間も長い。エンプラに強いMSの真骨頂。

WPFのようなデータバインディング前提の作りで行くとMVVMフレームワークを入れないと良さが出ないから、ライブラリのメンテナンスが発生する。月1回ぐらいかな、せいぜい。たまに破壊的変更が入るけど、まぁなんとかなります。iOS/Androidの開発では当たり前なんだけど、Windowsフォームしか知らない人だと工数だけが増えるだけで良いことがないって思うかもしれない。このご時世でOSSライブラリのメンテナンスをしたことがないエンジニアがいるのかって感じだけど。

ソースコードの保守性は、それはもうソフトウェア設計の話なので、Windowsフォーム / WPFはあんまり関係ないと思います。

Windowsフォームの良さは「とっつきやすさ」

WPFのXAMLも慣れたら生産性上がってくるんですが、WPFに習熟するまでのコストが必要です。コードビハインドを排除し、データバインディングだけでやっていくスタイル。3ヶ月〜半年ぐらいでしょうか。WPFはアーキテクチャを理解すると進みが速くなるけど、それが一番時間がかかる。

その点、Windowsフォームはとっつきやすい、始めやすい。それが最大の利点だと感じます。 D&Dで部品をはっつけ→ダブルクリックでイベントハンドラ作成→ロジックを書くという、大変わかりやすい方法で開発できます。言い方悪いけど、単純。アーキテクチャとか意識する機会が少ない。ビューにロジックを埋め込んじゃうんだから。

WPFの場合はMVVMに沿ってアプリケーションの設計を行うため、データバインディングへの慣れやXAMLの書き方の慣れ、Prism等MVVMフレームワークへ習熟が必要です。コードビハインドをベタベタに書けば変わらないけど、WPFで開発体験が変わらなければ意味がないでしょ?

MVVMの観点から見るとお前ふざけるなよって感じですが、ビューとロジックをガッツリ統合して生産性上げるのがWindowsアプリケーションの開発スタイルな気がするので、そのメリットをWPFで享受するのは無理かなって思う。

WebシステムのクライアントサイドにSQLを書くなんてことはありえないですけど、Windowsフォームのようにクラサバ前提の仕組みだったらビューにDB操作ロジックを書くのは理解できる。DBアクセス用の関数を別出しするのはありだけど、ビューからSQLを叩くことは変わらない。

テスタビリティ

GUIアプリケーションのテスタビリティを考えるのは結構悩ましい問題です。結局の所、意図したようにビューが切り替わっているかをチェックするしか無いので、いわゆる単体テストのように関数だけのテストを行ってもあまり意味がない。

単純な例を出すと、例えばグリッドの小計の縦計をテキストボックスに表示するとします。縦計のロジックなんて、HogeList.Sum(x => x.subtotal)で終わり。問題は集計した値が、ちゃんとテキストボックスに反映されているか。そこでしかバグの発見が出来ない。

Seleniumのようにコードを書いてUIをエミュレートしてテストする方式(E2E)が最も品質の高いテストが作れます。VisualStudioの一番高いエディションで「UI Automation」なるものがあったような記憶がありますが、パッとみて使うのを辞めた。

弊社はFriendlyというテストフレームワークを使わせてもらっています。Nugetで取得できます。UI部品の指定がElementNameじゃなくて、Bindingで取れるのがでかい。Elementの名前なんていちいち命名しない。HTMLの各パーツにname要素をベタ打ちする人います?って話で。

Seleniumやったことがある人ならわかると思いますが、E2EテストはPageObjectを作るまでが8割です。PageObjectの作成及び操作のエミュレーターとして、Friendlyは私達にとって非常に使いやすく、UI Automationより直感的にコードが書けました。Windowsフォームのテストもサポートしています。

Flutterだと通信部分をMockに差し替えてみたいなコードが書けるけど、Windowsアプリの場合、そのような仕組みがない。見つけられなかった。Debug/Releaseで差し替えるのかな?

MVVMを採用する意味

ウチはAPIサーバを別途立ててクライアントはJSONのやり取りに特化する作り(モバイルアプリケーションが多く、このやり方で統一している)ので、WindowsアプリケーションはWPFで作りました。DB接続などは一切書かず、ビジネスロジックは全部サーバ側。ユーザー操作以外のロジックを載せないスタイルです。

MVVMのコアになっているのは「ビューの責務を減らして、ビューに依存しなくても自動テストをできるようにする」です。 ソフトウェアの品質は不具合がないこと。不具合がない=テストが網羅されていること。ビュー単体でのテストは難しく、UIは最も頻繁に変更が入る箇所です。ビューが変わってもロジックのテストが出来るように、その責務を分けられるようにViewModelという概念が出てきたと考えています。

でも、ビューにSQL書いちゃうタイプのアプリだと、ビューの責務 is 全部俺だから、相性悪い。テスタビリティも低くなりやすい。MVVMの開発スタイルを経験することは、フロントエンドエンジニアなら絶対やるべきです。考え方が変わります。

WPFはWindowsフォームのUI開発の辛さを解決するためのもの

WindowsフォームでUIを作ることに疲れており、SQLモリモリに書いていないアプリケーションであれば、WPFに置き換える意味は大いにあると思います。UI開発の辛みを解決したいという狙いが明確だからです。ただ、ビジネスロジックの複雑さは、WPFで解決できるものではありません。

この記事がWPFの適材適所を知るきっかけになれば幸いです。