[WPF]マウスオーバーでコントロールを変更(.NET 3.5版)

最近、またWPFでクライアントアプリを作る仕事があってやっているんだけど、客先の指定が「.NET 3.5で作ってね」とのこと。さ、3.5?俺は耳を疑った。今さら3.5で作れと言うのか。NuGetでEntityFramework探したら一番古いバージョンが4.0だった。EF4.0は.NET4.0以上に依存している。EF4未満はちょっと私の知識が追い付かないくらい古い。POCOすら使えない。EF使うメリットも無く、むしろDataTable使った方が楽かもしれないような気がしてくる。

他にも、.NET3.5だと、WPFでDateTimePickerすら使えない。日付選択すらできない。DataGridも無い。日本人大好きな表も使えない。正直、こんな状態ではやる気が起きない。

なぜ3.5を指定してきたのかというと、まあ大体予想はしていたが「Win7に入っているデフォルトの.NETが3.5で、このアプリをインストールするにあたって追加ライブラリをインストールさせるという行為をユーザーに強要したくない(キリッ」などと言っていた。まあ、一理あるが、そのために私は.NET 3.5でサポートされていないが.NET 4.0以降ではサポートされている機能のいくつかを再発明しなければならないのであって、不毛であるなあと思う。

ユーザーに新しい.NETをインストールしたくないのであれば、今後のアプリ開発はすべて.NET3.5ベースで行うことになる。これはかなり非効率である。ユーザー全員が.NETをインストールすると言っても、あんなもん、数クリックで終わるだろう。インストール中は他の作業もできるのだし、ユーザーが1000人であったとしても、積算で1時間もかからない筈だ。その1時間を惜しみ、開発で1時間以上の無駄を生み出すというのは私にはとても非合理的であると思えるのだが。

もちろん私はそれを批判したが、「お客さん(エンドユーザー)の意向だから」というのを盾に交渉しようとすらしてくれなかった。我々の開発チームも、「その分お金出るんだからいいじゃん」などと言っている。はあ・・・。無駄な作業だなあ。モチベーションが上がらない。

本題

というわけで.NET 3.5 + WPFでクライアントアプリを作ろうとしたのだが、なんと、VisualStateManager(VSM)が使えないのだ。Blend for visual studioで「状態」のところに状態の追加ボタンが出てこないのだ。え?なにこれ?どういうこと?もしかして3.5だから?と思い検索してみると、なんと、VSMは.NET 4.0からのサポートらしい。えっ、これ4.0からなんですか?マジですか。

VSMが使えないとなると、コントロールのマウスホバー時の見た目を変更するなどという単純な作業ですら面倒だ。どうやって書くべきか。コードビハインド?いやいや、そんなことで書きたくない。じゃあ、ViewModelに背景色とかサイズとか定義してバインド?いやいや、XAMLだけで書くべきだろ。こんなもん。じゃあ、Expression SDKを使ってEventTriggerを取るか・・・と思ったら?あれ?Expression Blend 4 は.NET 3.5非対応?うむむ、だんだん腹が立ってきた。俺はいったい何をやっているんだ。

結論

以上のようなもろもろがあり、なんとかXAMLだけでVSM的なことができたのでまとめる。

まず、Expression Blend SDK 3をインストールします。4以降は.NET 3.5で使えないので注意。インストールしたらDLLをプロジェクトから参照します。C:\Program Files (x86)\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\WPFあたりにあります。

次に、以下ふたつのxmlネームスペースを追加します。

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"

マウスオーバーで色を変えるなどという処理は以下のように書きます。

<TextBlock Foreground="Black">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseEnter">
      <ei:ChangePropertyAction PropertyName="Foreground" Value="Red" />
    </i:EventTrigger>
    <i:EventTrigger EventName="MouseLeave">
      <ei:ChangePropertyAction PropertyName="Foreground" Value="Black" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
  ああああああ
</TextBlock>

お疲れ様でした。