WPF

【WPF】XAMLのリソース定義、適用について

WPF(Windows Presentation Foundation)はマイクロソフト社が提供する開発用フレームワークの一種です。HTML+JavaScriptと同様、UI部と処理部を分離して開発することができます。(HTML+JavaScriptの分離構造を参考に開発されたようです)
また、コントロールの外見や構造を任意にカスタマイズできるなど、従来のWinFormsと比較し拡張性のある開発が可能です。

本記事ではWPFのXAML上に記述する『リソース』について、定義方法、適用方法を解説します。

この記事で分かること
  • リソースを定義するメリット
  • リソースの記述方法(Application、Window、リソースファイル、マージなど)
  • StaticResourceとDynamicResourceの違い

リソースとは

アプリケーション内で再利用するために定義したスタイルやテンプレートのことです。定義したリソースを複数のUI要素に適用することで同じスタイルを割り当てることができます。

リソースは定義したオブジェクトクラス(Style等)のインスタンスです。(XAMLで記述した内容はビルド時に該当クラスのインスタンスに変換されます。)
このインスタンスを各コントロールの該当プロパティに設定することでスタイルやテンプレートが適用されます。

リソース定義するメリット

リソースを定義することにより以下のメリットがあります。

  • メモリの節約、処理高速化
    • スタイル等のインスタンスを複数のUI要素で共有するため、UI要素毎にリソースインスタンスを生成する必要がありません。不要なインスタンスが生成されなくなるため、メモリ使用量やインスタンス生成時の処理時間を節約することができます。
  • 統一性の確保、開発工数の削減
    • アプリケーションで使用する色やスタイルなどを事前にリソースとして定義することで、開発者によらず画面デザインを統一することができます。また、各UI要素の外見に関する実装を繰り返す必要がないため開発工数の短縮も図れます。
  • 保守性の向上
    • リソース定義を変更するだけで適用されている各UI要素の外観を一括で変更できます。個別に修正する必要がないため、修正漏れがありません。また修正時の工数も削減できます。

 

リソース定義の記述方法

リソース定義の方法及び記述方法について解説します。
なお、ここでは単純のため、背景色を設定するためのSolidColorBrushのみ定義します。実際はStyleやControlTemplateなどより外観を変更するリソースも定義可能です。StyleやControlTemplateについては別記事にて解説します。

リソース定義の種類

リソースは以下の種類があります。

アプリケーションリソース アプリケーション全体で有効です。
コントロールリソース リソース定義した該当コントロール内で有効です。
Windowとかリストボックスとか。

 

アプリケーション リソース

アプリケーション全体に対して有効なリソース定義です。App.xaml上に定義します。
アプリケーション全体で統一する情報などはここに記載します。

リソース定義

リソース定義をApp.xamlの<Application.Resources>タグ内に記述します。

[App.xaml]
<Application.Resources>
    <!-- アプリケーションリソースを定義 -->
    <SolidColorBrush x:Key="ApplicationBrush" Color="Pink"/>
</Application.Resources>

 

リソース適用

適用対象のUI要素のプロパティにリソースのx:Keyを指定します。x:Key名から検索されて該当するリソースインスタンスが設定されます。アプリケーション全体に有効な定義となっているため、どのウィンドウでも使用できます。

[xxxWindow.xaml]
<!-- アプリケーションリソースをTextBlockに適用 -->
<TextBlock Background="{StaticResource ApplicationBrush}" TextAlignment="Center">Application Resrouce</TextBlock>

 

適用結果

コントロール リソース

コントロールに対して有効なリソース定義です。該当コントロールのResourcesタグ内に定義します。

リソース定義

例えば該当のWindow内で使用したいリソースの場合、リソース定義をWindowのXAMLファイル上の<Window.Resources>タグ内に記述します。

[xxxWindow.xaml]
<Window.Resources>
    <!-- ウィンドウリソースを定義 -->
    <SolidColorBrush x:Key="WindowBrush" Color="Yellow"/>
</Window.Resources>

 

リソース適用

UI要素のプロパティにリソースのx:Keyを指定します。x:Key名から検索されてインスタンスが設定されます。リソースを定義したコントロールの配下に対して使用可能です。(Windowリソースとして定義した場合は該当Window内の全UI要素に対して使用できる)

[xxxWindow.xaml]
<!-- ウィンドウリソースをTextBlockに適用 -->
<TextBlock Background="{StaticResource WindowBrush}" TextAlignment="Center">Window Resrouce</TextBlock>

 

適用結果

別ファイルへの分離

前述のようにApp.xamlやWindowのXAMLファイル上にリソース定義を直接記述するとコードが膨大になり保守性が低下します。このためリソースを別ファイルに定義し、そのファイルを読み込むようにするべきです。

リソース定義

ソリューション内にリソース定義用のファイルを作成し(ここではResources.xamlとします)、ResourceDictionaryタグ内にリソース定義を記述します。

[Resources.xaml]
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!-- リソースを定義 -->
    <SolidColorBrush x:Key="ResourceFileBrush" Color="LightGreen"/>
    
</ResourceDictionary>

リソースファイルはVisualStudioのソリューションエクスプローラより
追加 ⇒ 新しい項目 ⇒ リソースディクショナリ(WPF)
を選んで追加してください。

リソース適用

作成したリソース定義ファイルをApp.xamlまたはWindowのXAMLに読み込みます。適用範囲は前述のとおりです。
読み込み後はx:Key名でリソースを指定して適用します。

[xxxWindow.xaml]
<Window.Resources>
    <!-- リソース定義ファイルを読み込み -->
    <ResourceDictionary Source="Styles/Styles.xaml"/>
</Window.Resources>

<Grid>
    <!-- リソースファイル上で定義したリソースを適用 -->
    <TextBlock Background="{StaticResource StyleFileBrush}" TextAlignment="Center">File Resrouce</TextBlock>
</Grid>

 

適用結果

リソースのマージ

これまでリソースの定義・適用方法について解説してきましたが、リソースファイルの読み込みを行う際はリソースのマージが必要となることがあります。
下記2点のいずれかに該当する場合はリソースのマージが必要となります。

  • 複数のリソースファイルを読み込む
  • 直接リソース定義が記述されているResourcesタグ内に追加でリソースファイルを読み込む

リソースタグ<hoge.Resources>の中に記載したリソースはビルド時にResourceDictionaryクラスのインスタンスに変換されhoge.Resourcesプロパティ(ResourceDictionary型)に設定されます。

この時、リソースファイルも一つのResourceDictionary型インスタンスとして生成されるためResourceDictionaryが複数(または入れ子)となり競合⇒エラーとなってしまいます。

このため、各ResourceDictionaryをマージして新しい単一のResourceDictionaryを再生成する処理が必要となります。

記述例

Window.Resourcesタグに記述する例を示します。(他のResourcesタグも同様です)

<Window.Resources>

    <!-- 複数のResourceDictionaryをマージして新しいResourceDictionaryとして再生成する -->
    <ResourceDictionary>
        <!-- タグ内に記述したResourceDictionaryをマージする-->
        <ResourceDictionary.MergedDictionaries>

            <!-- ResourceDirecotry① Window.Resourcesに直接記述した定義 -->
            <ResourceDictionary>
                <SolidColorBrush x:Key="WindowBrush" Color="Yellow"></SolidColorBrush>
                <SolidColorBrush x:Key="TestBrush" Color="Blue"/>
            </ResourceDictionary>
                
            <!-- ResourceDictionary② リソースファイルStyles.xaml -->
            <ResourceDictionary Source="Styles/Styles.xaml"/>
            <!-- ResourceDictionary③ リソースファイルStyles2.xaml -->
            <ResourceDictionary Source="Styles/Styles2.xaml"/>


        </ResourceDictionary.MergedDictionaries>

    </ResourceDictionary>

</Window.Resources>

Window.Resources直下にResourceDictionaryタグを記述することで新しいResourceDictionaryインスタンスを生成します。生成するインスタンスはResourceDictionary.MergedDictionariesタグ配下に記述した複数のResourceDictionaryインスタンスを結合したものとなります。

この時、元々Window.Resourcesに直接記述されていたリソース定義もResourceDictionaryタグで囲われていることに注意してください。マージのため明示的にResourceDictionaryインスタンスにしています。

 

リソース適用(StaticResource, DynamicResource)

定義したリソースをコントロールに適用する際、StaticResourceとDynamicResourceの2種類の適用方法があります。ここでは両者の違いを解説します。

<TextBlock Background="{StaticResource hogehoge}"></TextBlock>
<TextBlock Background="{DynamicResource hogehoge}"></TextBlock>

『StaticResource』と『DynamicResource』の違い

両者の違いは『リソース定義の内容が変更された場合に画面に反映されるかどうか』です。

StaticResource 画面起動時に一度だけ適用。
表示後のリソース内容の変更は反映されない。
DynamicResource 随時反映。
表示後にリソース変更が画面上に反映される。

なお、リソースの適用はStaticResource/DynamicResourceどちらでも可能です。

あくまでもリソースをいつ反映するかの指定なので、リソースの内容によりStaticResourceかDynamicResourceのどちらかが使えない・・・といったことはありません。
用途に合わせてを指定してください。

『StaticResource』と『DynamicResource』の動作の違い

背景色を設定するリソースをそれぞれStaticResourceとDynamicResourceで適用後、途中でリソースの背景色設定を変更した際の動作を確認します。

ソースコード

ウィンドウのXAMLにStaticResouceとDynamicResouceでリソース(背景色=青)を適用したコントロールを配置します。また、リソース変更のイベントを発火するボタンも設置しておきます。

[xxWindow.xaml]
<Window.Resources>
    <SolidColorBrush x:Key="TestBrush" Color="Blue"/>
</Window.Resources>

<Grid>
    <TextBlock Text="StaticResourceとDynamicResourceの違い" HorizontalAlignment="Center" Margin="0,30,0,0"></TextBlock>
    <!--StaticResource--> 
    <TextBlock Background="{StaticResource TestBrush}" TextAlignment="Center">Static Resoruce</TextBlock>
    <!--DynamicResource--> 
    <TextBlock Background="{DynamicResource TestBrush}" TextAlignment="Center">Dynamic Resoruce</TextBlock>

    <!-- リソース変更ボタン -->
    <Button x:Name="ResouceBtn" Content="Resource変更" Click="ResouceBtn_Click"></Button>
</Grid>

リソース変更ボタンクリック時にリソースを変更(背景色=赤)にするロジックを画面の.xaml.csソースに記載します。

[xxWindow.xaml.cs]

private void ResouceBtn_Click(object sender, RoutedEventArgs e)
{
    // TestBrushリソースを赤ブラシで差し替え
    this.Resources["TestBrush"] = new SolidColorBrush(Colors.Red);
}

 

結果
  • 画面表示時
    どちらもデフォルトの背景色青で表示されています。
  • リソース変更後
    DynamicResourceのみ反映されました。
    StaticResourceの方は最初の状態が維持されています。

システム開発におけるポイント

多人数によるシステム開発時は開発者によるバラツキや定義の重複を回避するため、システムの基底となる要素(色やサイズ情報など)を事前にリソースとして定義しましょう。

後から作成するカスタムコントロールなどもすべて基底のリソースを参照して作成するようにするとシステム全体として統一感が保てます。

例えばシステムで使用可能な色を限定し、

「各色をリソースで定義」 ⇒ 「配色はすべて該当の色リソースを参照」

とすることでより統一性のあるシステムを作成できます。

[App.xaml]

<Application.Resources>
    <!-- 色定義 ここで定義した以外の色は使用しない -->
    <Color x:Key="Color_Commit">#005555</Color>
    <Color x:Key="Color_Cancel">Gray</Color>

    <!-- アプリケーションで使用するリソースの定義 -->
    <!-- ブラシ (色定義で定義された色のみ使用) -->
    <SolidColorBrush x:Key="BrushCommitBtn" Color="{StaticResource Color_Commit}"></SolidColorBrush>
</Application.Resources>

 

まとめ

今回はWPFのリソースについて解説してきました。

リソースを再使用することでソースをスッキリできるほか、多人数開発においても外観に統一性を持たせることができます。リソースを使いこなして効率的な開発を行いましょう!

勉強中の身ですので、誤りや不備等ありましたらご指摘いただけますと幸いです。

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA