WPFプログラミング備忘録

WPF StaticResourceマークアップ拡張とDynamicResourceマークアップ拡張の違い

WPF

はじめに

この記事ではStaticResourceマークアップ拡張とDynamicResourceマークアップ拡張の動作の違いついて確認します。

今回の開発環境は以下のとおりです。

オペレーティングシステムWindows10 x64
Visual StudioMicrosoft Visual Studio Community 2019 Version 16.11.2
.NET Framewrok4.7.2

StaticResourceマークアップ拡張の動作

2つのRadioButtonとLabelを用意します。RadioButtonにはそれぞれ赤と青を指定します。

XAMLファイルは以下のとおりです。Label要素のBackGround属性にStaticResourceマークアップ拡張を使用しています。

<Window x:Class="WpfStaticDynamicResource.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfStaticDynamicResource"
        mc:Ignorable="d"
        Title="MainWindow" Height="180" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="myBrush"
                         Color="Red"/>
    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <StackPanel Margin="10" VerticalAlignment="Center">
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="赤"
                         IsChecked="True"
                         Margin="4"
                         Checked="OnRedRadioButtonChecked"/>
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="青"
                         Margin="4"
                         Checked="OnBlueRadioButtonChecked"/>
        </StackPanel>
        <Label Width="150" Height="80" Background="{StaticResource myBrush}"/>
    </StackPanel>
</Window>

コードビハインドは以下のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfStaticDynamicResource
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnRedRadioButtonChecked(object sender, RoutedEventArgs e)
        {
            Resources["myBrush"] = new SolidColorBrush(Colors.Red);
        }

        private void OnBlueRadioButtonChecked(object sender, RoutedEventArgs e)
        {
            // リソースmyBrushを青色のブラシに変更する
            Resources["myBrush"] = new SolidColorBrush(Colors.Blue);
        }
    }
}

OnRedRadioButtonCheckedイベントハンドラではキー属性myBrushに対応するリソースを赤色のブラシに設定します。

OnBlueRadioButtonCheckedイベントハンドラではキー属性myBrushに対応するリソースを青色のブラシに設定します。

プログラムを実行すると以下のウィンドウが表示されます。

ここでラジオボタンの青をチェックしても下図のとおりLabelの背景色が赤色のまま変わらないことが確認できます。

StaticResourceマークアップ拡張はリソース構築時に一度だけバインドされるので、このような動作となります。

DynamicResourceマークアップ拡張の動作

ではStaticResourceマークアップ拡張をDynamicResourceマークアップ拡張に変更するとどうなるでしょうか?

XAMLファイルは以下のとおりです。

<Window x:Class="WpfStaticDynamicResource.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfStaticDynamicResource"
        mc:Ignorable="d"
        Title="MainWindow" Height="180" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="myBrush"
                         Color="Red"/>
    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <StackPanel Margin="10" VerticalAlignment="Center">
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="赤"
                         IsChecked="True"
                         Margin="4"
                         Checked="OnRedRadioButtonChecked"/>
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="青"
                         Margin="4"
                         Checked="OnBlueRadioButtonChecked"/>
        </StackPanel>
        <Label Width="150" Height="80" Background="{DynamicResource myBrush}"/>
    </StackPanel>
</Window>

コードビハインドに変更はありません。プログラムをビルドして実行すると以下のウィンドウが表示されます。

青をチェックしてみると下図のとおりLabelの背景色が青色に変わることが確認できます。

その後も赤をチェックするとLabelの背景色が赤色になり、青をチェックすると青色に変わります。

DynamicResourceマークアップ拡張は動的にリソースをバインドするため、このような動作となります。

ここで以下のようにXAMLファイル内には存在しないキー属性myOtherBrushを指定してみます。DynamicResourceマークアップ拡張はStaticResourceマークアップ拡張に戻しています。

<Window x:Class="WpfStaticDynamicResource.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfStaticDynamicResource"
        mc:Ignorable="d"
        Title="MainWindow" Height="180" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="myBrush"
                         Color="Red"/>
    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <StackPanel Margin="10" VerticalAlignment="Center">
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="赤"
                         IsChecked="True"
                         Margin="4"
                         Checked="OnRedRadioButtonChecked"/>
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="青"
                         Margin="4"
                         Checked="OnBlueRadioButtonChecked"/>
        </StackPanel>
        <Label Width="150" Height="80" Background="{StaticResource myOtherBrush}"/>
    </StackPanel>
</Window>

ビルドして実行するとmyOtherBrushというリソースが無いためSystem.Windows.Markup.XamlParseException例外が投げられ異常終了します。

一方以下のようにDynamicResourceマークアップ拡張にするとどうなるでしょうか?

ここでは後の検証のため3つ目のRadioButtonを追加しています。

XAMLファイルは以下のとおりです。

<Window x:Class="WpfStaticDynamicResource.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfStaticDynamicResource"
        mc:Ignorable="d"
        Title="MainWindow" Height="180" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="myBrush"
                         Color="Red"/>
    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <StackPanel Margin="10" VerticalAlignment="Center">
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="赤"
                         IsChecked="True"
                         Margin="4"
                         Checked="OnRedRadioButtonChecked"/>
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="青"
                         Margin="4"
                         Checked="OnBlueRadioButtonChecked"/>
            <RadioButton GroupName="RedOrBlueGroup"
                         Content="黄"
                         Margin="4"
                         Checked="OnYellowRadioButtonChecked"/>
        </StackPanel>
        <Label Width="150" Height="80" Background="{DynamicResource myOtherBrush}"/>
    </StackPanel>
</Window>

この場合は下図のとおりmyOtherBrushに対応するブラシが存在しないためLabelの背景色に色は着きませんが実行時エラーにはなりません。

コードビハインドは以下のとおりです。OnYellowRadioButtonCheckedイベントハンドラではmyOtherBrushに黄色のブラシを割り当てています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfStaticDynamicResource
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnRedRadioButtonChecked(object sender, RoutedEventArgs e)
        {
            Resources["myBrush"] = new SolidColorBrush(Colors.Red);
        }

        private void OnBlueRadioButtonChecked(object sender, RoutedEventArgs e)
        {
            Resources["myBrush"] = new SolidColorBrush(Colors.Blue);
        }

        private void OnYellowRadioButtonChecked(object sender, RoutedEventArgs e)
        {
            // キー属性myOtherBrushに黄色のブラシを動的に割り当てる
            Resources["myOtherBrush"] = new SolidColorBrush(Colors.Yellow);
        }
    }
}

プログラムを実行して黄をチェックするとLabelの背景色が黄色になることが確認できます。

繰り返しになりますが、DynamicResourceマークアップ拡張はリソースが存在した時点で動的にバインドするため、このような動作となります。

どちらを使用するべきか

画面表示のパフォーマンスを考えると可能な限りStaticResourceマークアップ拡張を使用した方がよいでしょう。

DynamicResourceマークアップ拡張は動的にリソースを切り替える必要がある時だけに限定した方が無難かと思います。

今回はStaticResourceマークアップ拡張とDynamicResourceマークアップ拡張の動作の違いついて確認しました。

以上です。

コメント