2017年にWPFでデスクトップアプリを新規作成する際に考慮すること

2017年4月現在、新規にWindows上で動作するクライアントアプリのベターな選択は何か?WPFのデスクトップアプリケーションを選んだ場合、実装時に外観上のプログラミング時に考慮しなくてはいけない事を、業務アプリ的な目線で考察をしてみました。

いちおう、前向きにWPFアプリでデスクトップアプリを実装しましょうという話の流れになります。

デスクトップアプリを取り巻く環境

まずは状況の確認です。

ハッキリ言って、デスクトップのWPFアプリはMSもう切り捨ててますね。少なくとも新規にリソースを裂いている様子は全くないです。

が、以下のOSのシェアの表の通り、半数は依然としてWindows7という状況にあってUWPをマイクロソフトがいくらゴリ押ししてるといっても、その動作環境たるWindows10が非多数派の現状、どの事務用PC上でも動作するクライアントというものは事実上"デスクトップアプリ"一択であると言えます。従って、デスクトップアプリを作成し、余力があればUWPでもアプリを展開するという姿勢を取らざるを得ません。

2017年4月現在、WinのOSシェア内訳は以下の通り。

OS 割合
Windows 7 ≒ 50%
Windows 8.x 26%
Windows 10 7%

ただし、企業内のWin7の新規調達はそろそろ無くなくなりつつあり、Win10に徐々に移り変わっているという状況です。つまり、2017年から、Win7のサポートが完全に切れる2020年まで(+α)は過渡期と言えそうです。

昨今、MSはUWPを押していますが、WFPデスクトップ以上にとても不人気(そりゃWin10とモバイル、ARMタブ環境で実行でいるといわれてもねぇ)かつ、状況が今後も覆らないと思うのでこれも心にとめておいたほうがいいと思います。

Windows7と8.x、10で見た目が全然違う問題をどうにかする

で、ここからはWPFで実装する場合どうしようという話です。

WPFで、Windonws7とそれ以降のOS上で動作するデスクトップアプリケーションを作成しようとなった場合、標準コントロールの外観がOSごと(テーマごと)にかなり違います。デザイン上のサポートを何らかの形で行わないと、いざ実行して、、、あれ?ということになってしまいます。

具体的に何が違うかというと

  • Win7はAero Glass系統で半透明のウインドウフレーム、立体的なボタン、緩やかなグラデーション
  • Win8以降はいわゆるフラットデザイン系

となり、主要な対応方針は

  1. 立体/フラット、両方のデザインを、違和感なく両立させる。
  2. 頑張って独自のデザインテンプレートを開発する
  3. 3rd製ライブラリでスタイルを統一する(MahApps, Modern UI)
  4. WPFの設定でどうにかする

などが挙がります。。ただ、これから消えていくWin7を頑張る事はしたくないのと、会社でリリースするアプリが3rd製オープンソースのUIのテーマを転用したデザインというのも少し違うかなと思います。

従って、最後のWPFの設定でどうにかできないかで検討します。なるべく頑張らずに。*1

なので、「Windows7で、フラットデザイン風の表示されるようにする」方法で課題の解決を図ることにします。そうすることで少なくともウインドウフレーム以外の中身は、全OS共通でフラットデザインで表示されるようになるからです。


前置きが長くなりましたが、早速設定の話に入りたいと思います。まず、アプリの参照へ以下アセンブリを追加し、追加後に、"ローカルコピー"を"true"に設定します。

PresentationFramework.AeroLite

また、App.xamlを以下の通り編集します。

<Application x:Class="WpfAppDesign.App" ...{省略}...>
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
         <ResourceDictionary
              Source="/PresentationFramework.AeroLite;component/themes/aerolite.normalcolor.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

これで、Win7側は、特に何もせずに配置したコントロールがすべてフラットデザイン風の見た目に変更されます。

適用前のWin7上のコントロールの表示は以下の通り。

f:id:Takachan:20170701173052p:plain

適用後の表示は以下の通りとなります。

f:id:Takachan:20170701173125p:plain

参考までにWin8で上記設定済みをアプリを起動したときの見た目です。

f:id:Takachan:20170701173152p:plain

Win10では以下の通り。

f:id:Takachan:20170701173206p:plain

デフォルトのスタイルを踏襲する

上記手順で、コントロールの外観が、フラットデザイン風に変更されましたが、XAML上でStyleの定義を追加すると、表示が元に戻ってしまいます。なので、スタイルを定義する際は、スタイル定義毎に、BaseOnプロパティの指定を行います。

例えば、TreeViewへスタイルを定義する場合以下のようになります。

<TreeView>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Childs}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Text}"/>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>

    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}" 
               BasedOn="{StaticResource {x:Type TreeViewItem}}"> <!-- ★この部分 -->
            <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
        </Style>
    </TreeView.ItemContainerStyle>

BaseOnへ標準コントロールの型を指定しています。

BasedOn="{StaticResource {x:Type TreeViewItem}}"

これで、デザインが独自のスタイル定義によって崩れることを防止できます。

メッセージボックスの見た目がおかしい問題

これも、Win7以前の環境固有の問題ですが、WPFのデスクトップアプリで、メッセージボックスを表示すると、AeroGlassが適用されていないクラシックテーマのような表示になってしまします。これを防止するために、WPF アプリへ、マニフェストを用いた視覚スタイルの適用を行います。

まず、WPFアプリケーションへ、「アプリケーションマニフェスト」を追加します。

プロジェクトエクスプローラ上のプロジェクトから「追加」> 新しい項目 > アプリケーション マニフェスト ファイル

を選択し、デフォルトの"app.manifest"のままファイルを追加します。次に、マニフェストファイルを開き、コメントアウトされている以下の項目をコメントインします。

<!-- Windows のコモン コントロールとダイアログのテーマを有効にします (Windows XP 以降) -->
<!-- この部分のコメントアウトを外す
<dependency>
  <dependentAssembly>
    <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        processorArchitecture="*"
        publicKeyToken="6595b64144ccf1df"
        language="*"
      />
  </dependentAssembly>
</dependency>
-->

その後、MessageBox.Show(...)を呼び出すと、Winodws7上で視覚スタイルが適用されます。(メインコントロールはフラットデザインなのに、メッセージボックスはAero Glassになってしまいますが、OpenFileDialogなどのダイアログ系は、すべてそうなるのでまぁ、、、諦めましょう。

適用前のWin7でのメッセージボックスの表示の見た目は以下の通り。

f:id:Takachan:20170701173304p:plain

適用後は以下になります。

f:id:Takachan:20170701173318p:plain

以上で、ある程度Win7 - 8.x,10の見た目が程度統一できたかと思います。

*1:ちなみに以降、圧倒的少数派のモバイル/タブレットWin端末と、無かった事にされた、Windows8は、ほとんど無視して話を進めています。