リソースディクショナリの継承の罠
別のリソースディクショナリで定義したタイプスタイルを継承して拡張定義するためには、MergedDictionariesだとダメだった。例えば以下のような感じで、先に指定したリソースの中で定義しているものをBasedOnしているはずだが、実行時に当たったり当たらなかったりと不安定になる。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/presentationframework.Aero;component/themes/aero.normalcolor.xaml"/> <ResourceDictionary> <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Setter Property="FontFamily" Value="component/#myfont" /> </Style> <!-- 以下略。ほとんどのコントロールのFontFamilyの上書き。--> </ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
気持ち悪いことに、たとえばボタンが2つ並んでいるのに1つめは当たって2つめは当たらないという現象もあった。コンテキストメニューが1回目のポップアップでは適用しないが2回目のポップアップでは適用される…など、まったく安定しない動作だった。非同期でマージしているんじゃなかろうか。
では…と、こちらの、まめしば雑記にもあるように、以下のようにインクルード的にMargedDictionariesを使うという方法をやってみた。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/presentationframework.Aero;component/themes/aero.normalcolor.xaml"/> <ResourceDictionary> <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Setter Property="FontFamily" Value="component/#myfont" /> </Style> <!-- 以下略。ほとんどのコントロールのFontFamilyの上書き。--> </ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
すると、なんとロード中にStackOverFlowになってしまう。たぶんだけどType Buttonのスタイル定義の循環参照を延々やっているんじゃなかろうか。ひどすぎる。
そういった紆余曲折と試行錯誤を経て、最終的に以下のような哀しい回避策で落ち着いた。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/presentationframework.Aero;component/themes/aero.normalcolor.xaml"/> <!-- WPFのStackOverFlowバグを回避するため、一旦別キーで定義 --> <ResourceDictionary> <Style x:Key="__Type_Button__" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Setter Property="FontFamily" Value="component/#myfont" /> </Style> <!-- 以下略。ほとんどのコントロールのFontFamilyの上書き定義を__Type_XXXX__キーで。--> </ResourceDictionary> </ResourceDictionary.MergedDictionaries> <Style TargetType="Button" BasedOn="{StaticResource __Type_Button__}"/> <!-- 以下略。ほとんどのコントロールをBesedOn=__Type_XXXX__で再定義。--> </ResourceDictionary>
ここにくるまでに、TargetType="Control"のMargedDictionariesで一発解決できるかなーと甘いチャレンジもしてみたけど敢え無く撃沈した。
動作確認はもちろん .NET3.5 SP1おんりー。