概要
この記事ではスタイルでよく見られる問題をご紹介します。 1つ目は Theme プロパティを設定しても要素がテーマの外観に従って表示されない問題です。 2 つ目は、要素のスタイルを作成してもスタイルが使用されないという問題です。 はじめに WPF でスタイルがどのように配置され、解決されるかを確認しましょう。
この記事は WPF のスタイルの定義について基本的な知識があることを前提としています。スタイルの基本についてはマイクロソフトのスタイルとテンプレートをご覧ください。
背景
WPF の要素は 2 つのスタイル インスタンスによって影響されます。スタイル インスタンスは 「ローカル」スタイルと「テーマ」です。
注:OS システムのテーマ (Aero、XP Blue、XP Silver など) を指します。インフラジスティックス コントロールで公開する Theme プロパティではありません。 インフラジスティックス Theme プロパティについては後ほどご紹介します。
テーマのスタイルはWPF が要素のデフォルトスタイルとして使用する Style です。 WPF フレームワークは、その DefaultStyleKey の値に基づいて要素に Style を読み込みます。これは通常要素の型 + OS のテーマ名という形で名付けられます。これはコントロールの開発者によって提供される Style です。
ローカル スタイルは要素が使用されているアプリケーションによって提供されるスタイルです。 要素の Style プロパティが設定され、それがローカルスタイルになります。 Style プロパティが設定されていない場合、WPF は明示的なスタイルを見つけてローカル スタイルとして使用します。 明示的なスタイルはプロセスで、要素のリソースと ResourceDictionary のキーが要素の特定の型であるそのスタイルの先祖をチェックします。 最初に見つかったスタイルはローカル スタイルとして使用されます。 見つからない場合はアプリケーションのリソースをチェックします。要素の特定の型は、ディクショナリのスタイルを見つけるために使用するキーとして使用されます。
注:XAML の ResourceDictionary でスタイルを定義する際にキーを明示的に指定しない場合、TargetType がキーとして使用されます。
ローカル スタイルが見つかると、スタイルのセッターとトリガーがテーマ スタイルより優先されます。また、OverridesDefaultStyle プロパティを設定した場合 (要素またはローカルスタイルに直接)、WPF は要素インスタンスにテーマスタイルの情報を適用しません。
Infragistics Theme プロパティは、WPF の暗黙的ローカル スタイルの機能を利用します。 コントロールに Theme プロパティを設定した場合、ThemeManager クラスは、Theme に定義されたリソースが含まれる ResourceDictionary を読み込み、要素の Resources の MergedDictionaries へ置きます。
WPF が使用するスタイルをどのように決定して、Theme プロパティがどのように関わっているかがお分かりいただけたと思います。さて、次は問題を検証していきましょう。
問題 1 – 要素がテーマを使用しない
最初の問題は Theme プロパティが設定されているのに要素がそのテーマに従っていない問題です。
この問題には、いくつかのケースが考えられます。
ケース 1
使用している要素が、インフラジスティックスのクラス インスタンスから派生している場合です。たとえば、xamRibbon を含む Window 内すべてを定義するかわりに、RibbonTabItem から派生させて、タブで使用する RibbonGroups および Tools を単独で定義した場合です。
<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>
生成したタブのインスタンスを作成してリボン内で使用します。
<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 が暗黙的なローカル スタイルを探す場合、クラスの実際の型を使用します。Theme プロパティを設定すると、RibbonTabItem のスタイルを含む ResourceDictionary が配置されます。 ただし、WPF は型を探さずに、この場合では Key が 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
この問題が発生するもう 1 つのケースとして、要素の Style を明示的または暗黙的に設定した場合が考えられます。 よくある原因の 1 つとして、フィールドに 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 の Style は使用されません。
この問題を回避するには、Style の Based on を使用している Theme の 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 プロパティを明示的に設定せずに、 Style をターゲット要素自身、または先祖の 1 つである要素の Resources に置いた場合に同じような問題が発生します。回避策は前途と同じです。BasedOn を Style に設定してください。
問題 2 – カスタム スタイルが使用されない。
2 つ目は、要素に Style を設定しているにもかかわらず、Theme プロパティが設定されている場合に Style を使用しないという問題です。
<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 プロパティを設定した場合、Style は Theme を設定したコントロールの Resource に置かれます。 WPF は暗黙的な Style を検索する場合に、その要素 (この場合 xamTabControl) の Resource を見つけるとそれ以上検索しません。つまり、Window や Application に設定した Style は無視されてしまいます。 回避策としては、Style を Theme を設定した要素の Resource へ移動する方法があります。
注:この Style は最初に見つけられるため (Theme プロパティによって提供された ResourceDictionary が Resources.MergedDictionariesにあり、MergedDictionaries がリソースより先にチェックされるため)、Theme によって提供された Style は使用されません。そのため、問題 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>