インフラジスティックス・ジャパン株式会社Blog

インフラジスティックス・ジャパン株式会社のチームメンバーが技術トレンド、製品Tips、サポート情報からライセンス、日々の業務から感じることなど、さまざまなトピックについてお伝えするBlogです。

Themeプロパティを使用する際の一般的なスタイルの問題について

スタイルに関連して、よく目にする問題がいくつかあります。 1つは、コントロールのThemeプロパティを設定しても、一部の要素がそのテーマの外観に従っていないように見える問題です。 もう1つは、特定の要素のスタイルを作成したが、スタイルが適用されない問題です。 何が起こっているかを判断するには、WPFによってスタイルがどのように配置され、解決されるかを理解することが役立ちます。 ここではWPFでのスタイルの定義と使用について基本的な知識があることを前提として説明します。 そうでない場合は、事前にMicrosoftのヘルプのスタイルとテンプレートのトピックを読んでおくとよいでしょう。

この記事の原文は以下よりご確認いただけます。
Andrew Smith [Infragistics] / Wednesday, December 9, 2009
Common Style Issues when using the Theme property

背景

WPFでは、要素は最大2つのStyleインスタンス(「ローカル」スタイルと「テーマ」スタイル)の影響を受ける可能性があります。 ここでいうテーマとは、オペレーティングシステムのテーマ(Aero、XP Blue、XP Silverなど)であり、コントロールで公開しているThemeプロパティではないことに注意してください。 コントロールのThemeプロパティがどのような役割を果たすかについては、後ほど説明します。

テーマスタイルは、WPFが要素のデフォルトとして使用するスタイルです。 本題に関係がないためあまり詳しく説明しませんが、基本的にWPFフレームワークは、DefaultStyleKeyの値(通常は要素のType)と現在のOSテーマ名に基づいて、指定された要素のStyleをロードします。 基本的に、これはコントロールの開発者によって提供されるスタイルです。

ローカルスタイルは、その要素が使用されているアプリケーションで提供されるスタイルです。 要素のStyleプロパティが設定されている場合はそれがローカルスタイルになります。 Styleプロパティが設定されていない場合、WPFは暗黙的なスタイルを見つけ、それをローカルスタイルとして使用しようとします。 暗黙的なスタイルとは、要素とその祖先のResourcesをチェックしてResourceDictionaryのキーが要素のTypeであるスタイルを見つける解決プロセスです。 最初に見つかったスタイルがローカルスタイルとして使用されます。 見つからない場合は、アプリケーションのResourcesがチェックされますが、ここでも要素の特定のTypeをキーとしてディクショナリ内のスタイルが検索されます。 XAMLのResourceDictionaryでスタイルを定義する場合、キーを明示的に指定しない場合はTargetTypeがキーとして使用されることに注意してください。

ローカルスタイルが見つかった場合、そのスタイルのセッターとトリガーがテーマスタイルのものよりも優先されます。 OverridesDefaultStyleプロパティがtrueに設定されている場合(要素に直接またはローカルスタイルで)、WPFはテーマスタイルからの情報を要素インスタンスに適用しないことにも注意してください。

InfragisticsのThemeプロパティは、WPFの暗黙的なローカルスタイル機能を利用します。 コントロールにThemeプロパティを設定すると、ThemeManagerクラスは、そのテーマで定義されたリソースを含むResourceDictionaryをロードし、それらをその要素のResourcesのMergedDictionariesに配置します。

WPFがスタイルを見つける方法とThemeプロパティがどのように関係するかを理解したところで、元の問題が発生した理由を見てみましょう。

問題 1 - 要素がテーマを使用しない

最初に述べた問題は、Themeプロパティが設定されているにもかかわらず、要素にそのテーマの外観が適用されないことでした。 これが発生するシナリオはいくつかあります。

シナリオ 1

一つ目の状況は、使用する要素がInfragisticsのクラスの派生インスタンスである場合です(要素の内容を分離して定義する場合など)。 例えば、タブで使用するRibbonGroupとToolをXamRibbonの配置されたWindow内ですべて定義するのではなく、別の場所で定義するため、RibbonTabItemから派生したインスタンスを作成するとします。

<igRibbon:RibbonTabItem x:Class="StyleIssues.DerivedNotUsingTheme.MyRibbonTabItem"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:igRibbon="http://infragistics.com/Ribbon"
   Header="Home">
    <igRibbon:RibbonGroup Caption="Clipboard">
...
    </igRibbon:RibbonGroup>
</igRibbon:RibbonTabItem>

そして、その派生タブのインスタンスを作成し、XamRibbon内で使用します。

<Window x:Class="StyleIssues.DerivedNotUsingTheme.TestWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="clr-namespace:StyleIssues.DerivedNotUsingTheme"
   xmlns:igRibbon="http://infragistics.com/Ribbon"
   Title="TestWindow" Height="300" Width="300">
    <StackPanel>
        <igRibbon:XamRibbon Theme="Office2k7Black">
            <local:MyRibbonTabItem />
           
            <igRibbon:RibbonTabItem Header="Other" />
        </igRibbon:XamRibbon>
    </StackPanel>
</Window>

アプリケーションを実行したときに、タブの上にマウスをホットトラックさせると、タブが正しく表示されないことにお気づきでしょう。

WPF が暗黙のローカル スタイルを検索するときは、常にクラスの実際のTypeを使用すると説明したのを覚えていますか。 Theme プロパティを設定すると、RibbonTabItem のスタイルなどを含む ResourceDictionary が配置されます。 しかし、WPF はその Type を探すのではなく、キーが MyRibbonTabItem である Style を探します。 この問題を回避するには、いくつかの方法があります。ひとつは、派生クラスを作成せずxamlのみ定義し、LoadComponentを使用することです。 この方法については、こちらのフォーラムに投稿しています。 もう一つの方法は、インスタンスの Style プロパティを、ベース タイプをキーとする DynamicResource に設定することです。

<igRibbon:RibbonTabItem x:Class="StyleIssues.DerivedNotUsingTheme.MyRibbonTabItem"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:igRibbon="http://infragistics.com/Ribbon"
    Style="{DynamicResource {x:Type igRibbon:RibbonTabItem}}"
   Header="Home">
    <igRibbon:RibbonGroup Caption="Clipboard">
...
    </igRibbon:RibbonGroup>
</igRibbon:RibbonTabItem>
シナリオ 2

この現象が発生するもう一つのケースは、明示的または暗黙的に要素のStyleを設定する場合です。 最も一般的な例としては、XamDataGridのFieldにEditorStyleを設定する場合が挙げられます。

<Window x:Class="StyleIssues.SetStyle.TestWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:igDP="http://infragistics.com/DataPresenter"
   xmlns:igEditors="http://infragistics.com/Editors"
   xmlns:sys="clr-namespace:System;assembly=mscorlib"
   Title="TestWindow" Height="300" Width="600">
    <Window.Resources>
        <igEditors:ComboBoxItemsProvider x:Key="departments">
            <sys:String>Accounting</sys:String>
            <sys:String>Marketing</sys:String>
            <sys:String>Sales</sys:String>
            <sys:String>Admin</sys:String>
        </igEditors:ComboBoxItemsProvider>
       
        <Style x:Key="comboStyle" TargetType="igEditors:XamComboEditor">
            <Setter Property="ItemsProvider" Value="{StaticResource departments}" />
        </Style>
    </Window.Resources>
    <Grid>
        <igDP:XamDataGrid Theme="LunaOlive" BindToSampleData="True">
            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout>
                    <igDP:Field Name="department">
                        <igDP:Field.Settings>
                            <igDP:FieldSettings EditorStyle="{StaticResource comboStyle}" />
                        </igDP:Field.Settings>
                    </igDP:Field>
                </igDP:FieldLayout>
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>
    </Grid>
</Window>

WPF がローカル スタイルを検索するとき、最初に Style プロパティをチェックすることを思い出してください。 プロパティが設定されている場合、それがローカル スタイルとなるので暗黙のスタイルを探すことはありません。 暗黙のスタイルが使用されないため、テーマから要素に提供されたスタイルが使用されません。 これを回避するには、対象のThemeのスタイルをBasedOnを使用してStyleに設定する方法があります。

   xmlns:igThemes="http://infragistics.com/Themes"
   Title="TestWindow" Height="300" Width="600">
    <Window.Resources>
...
       
        <Style x:Key="comboStyle" 
              TargetType="igEditors:XamComboEditor"
               BasedOn="{x:Static igThemes:EditorsLunaNormal.XamComboEditor}"
              >
            <Setter Property="ItemsProvider" Value="{StaticResource departments}" />
        </Style>
    </Window.Resources>

Style プロパティを明示的に設定するのではなく、ターゲット要素自体、またはターゲット要素からTheme プロパティを設定した要素までの間の先祖にあたる要素の Resources にStyleを設定した場合にも、同様の問題が発生することに注意してください。 回避策は前述と同じく、スタイルに BasedOn を設定する方法になります。

問題 2:カスタムスタイルが使用されていない

次に、ある要素にスタイルを定義したとき、そのスタイルがTheme プロパティが使用されると適用されなくなる問題です。

    <Window.Resources>
        <Style TargetType="igWindows:TabItemEx">
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
    </Window.Resources>
    <Grid>
        <igWindows:XamTabControl Theme="Office2k7Black">
            <igWindows:TabItemEx Header="One" />
            <igWindows:TabItemEx Header="Two" />
            <igWindows:TabItemEx Header="Three" />
        </igWindows:XamTabControl>
    </Grid>

ここまでで、何が問題なのかがわかると思います。 Themeプロパティを設定すると、StylesはThemeを設定したコントロールのResourceに置かれます。 WPFの暗黙のスタイル検索はそこで要素(この場合はxamTabControl)のリソースを見つけ、検索はストップするため、 ウィンドウまたはアプリケーションのResourcesに配置したスタイルを無視することになります。 回避策としては、テーマを設定した要素のResourcesにそのスタイルを移動する方法があります。 このスタイルは最初に検出されるため(Theme プロパティによって提供される ResourceDictionary は Resources.MergedDictionaries に置かれ、 MergedDictionaries は Resources に直接置かれたリソースの後でチェックされるため)、 Theme によって提供されたスタイルは使用されません。そのため、先の問題 1 のシナリオ 2 で推奨されたアプローチも使用しなければならないことに注意してください。

        <igWindows:XamTabControl Theme="Office2k7Black">
            <igWindows:XamTabControl.Resources>
                <Style TargetType="igWindows:TabItemEx"
                      BasedOn="{x:Static igThemes:PrimitivesOffice2k7Black.TabItemEx}">
                    <Setter Property="FontWeight" Value="Bold" />
                </Style>
            </igWindows:XamTabControl.Resources>
            <igWindows:TabItemEx Header="One" />
            <igWindows:TabItemEx Header="Two" />
            <igWindows:TabItemEx Header="Three" />
        </igWindows:XamTabControl>