Pocket

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

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

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

WPFに移行するメリットは、ありません

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

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

WindowsフォームとWPFの開発スタイルは同じではないんです。言語は同じだけど。

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

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

WPFはこうなります。

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

すごい変化ですよね。アーキテクチャや開発手法がまるで違うので、WindowsフォームとWPFはマジで別物です。

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

MVVMを使い込む=データバインディングをフル活用です。データバインディングにより、どのモデルの、どのプロパティを出すかだけ考えればビューの表示が切り替わるから、データバインディングだけを考えて実装する。これがWPFの最大のメリットだと思います。ビューの状態を全部ViewModelに寄せて、イベントハンドラを排除していくのがWPFらしい開発スタイル。

イベントハンドラ(例えばキーイベント)については、「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に焼き直すメリットはゼロです。MVVM形式に焼き直すと品質が上がる気もしません。技術の伝承という意味で、式年遷宮でやるのはありですけど。

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

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

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

パフォーマンス

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

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

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

印刷(帳票作成)

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

WPFはちょっとモダンな仕組みがある。XAMLを帳票テンプレートにしてFixedPageでXPSに流し込むという技がWPFでは使えるので、行が可変じゃない送り状やカバーレターみたいなものなら、生産性上がると思います。単票っていうのかな。

ただ、データバインディングで行が可変になって、罫線をそのつどデータの数だけ引くみたいな作り方をすると、かなり重くなる。実務に耐えきれない。

請求書一括みたいな、100pは超えるであろう帳票の印刷については、自助努力で頑張るかツール買うしか無いです。大量出力サポートしているツールを買ったほうが安いっす。そこで苦しむと辛いですよ。

ウチはサーバーサイドでPDFを生成してダウンロードしてから、ダイレクト印刷を行う方法を取りました。

将来性

どっちも同じです。開発手法が大きく変わることはなく、大幅なバージョンアップも考えにくい。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のバージョンアップはかなり簡単。ビルド回りの仕組み変わらないからね。iOSやAndroidは、その辺が変わりやすい。

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

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

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

WPFのXAMLも慣れたら生産性上がってくるんですが、WPFに習熟するまでのコストが必要です。半年から1年。WPFはアーキテクチャを理解すると進みが速くなるけど、それが一番時間がかかる。

その点、Windowsフォームはとっつきやすい、始めやすい。それが最大の利点だと感じます。

WPFの場合はMVVMに沿ってアプリケーションの設計を行うため、データバインディングへの慣れやXAMLの書き方の慣れ、Prism等MVVMフレームワークへ習熟が必要です。イベントハンドラをベタベタに書けば変わらないけど、WPFにする意味がない。レスポンシブ / 高DPI対応がマストじゃないなら。

Windowsフォームはドラックアンドドロップで部品をはっつけ→ダブルクリックでイベントハンドラ作成→ロジックを書くという、大変わかりやすい方法で開発できます。MVVMの観点から見るとお前ふざけるなよって感じですが、ビューとロジックをガッツリ統合して生産性上げるのがWindowsアプリケーションの開発スタイルな気がするので、そのメリットをWPFで享受するのは無理かなって思う。

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

テスタビリティ

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

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

Flutterのようにウイジェットレベルのテストをサポートする仕組みがないので、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アプリの場合、そのような仕組みがない。見つけられなかった。

MVVMを採用する意味

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

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

でも、ビューにSQL書いちゃうタイプのアプリだと、ビューの責務 is 全部俺だから、相性悪い。

MVVMの開発スタイルを経験することは、フロントエンドエンジニアなら絶対やるべきです。考え方が変わります。