タイプの依存関係プロパティを持つカスタム WPF コントロールがありますPen
(コントロール内の分割線のスタイルを設定するために使用されます)。
このプロパティの既定値はシステム カラーであると想定されています。この既定値を使用する場合、ユーザーが (Windows 設定で) システム カラーを変更すると、コントロールを更新する必要があります。
これまでのところ、このデフォルト値をデフォルト コントロール スタイルの一部として指定しました。
<Style TargetType="{x:Type my:Control}">
<Setter Property="DividerPen">
<Pen Brush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
Thickness="1"/>
</Setter>
...
</Style>
コントロールが単一の UI スレッドでのみ使用されている限り、これは正常に機能します。ただし、別の UI スレッドで実行される複数の最上位ウィンドウでコントロールを使用すると、次の例外が発生します。
System.Windows.Markup.XamlParseException: Cannot access Freezable 'System.Windows.Media.Pen' across threads because it cannot be frozen.
---> System.InvalidOperationException: Cannot access Freezable 'System.Windows.Media.Pen' across threads because it cannot be frozen.
at System.Windows.StyleHelper.ProcessInstanceValuesHelper(ItemStructList`1& valueLookupList, DependencyObject target, Int32 childIndex, HybridDictionary instanceValues, Boolean apply)
at System.Windows.StyleHelper.ProcessInstanceValuesForChild(DependencyObject container, DependencyObject child, Int32 childIndex, HybridDictionary instanceValues, Boolean apply, FrugalStructList`1& childRecordFromChildIndex)
at System.Windows.StyleHelper.CreateInstanceData(UncommonField`1 dataField, DependencyObject container, FrameworkElement fe, FrameworkContentElement fce, Style newStyle, FrameworkTemplate newFrameworkTemplate)
...
どうやら、<Pen>
インスタンス内で動的リソースを使用すると、スタイルが凍結されるのを防ぐことができます。
これまでのところ、2 つの解決策を考えることができます。
1) スタイルにセットx:Shared="False"
します。すべてのコントロール インスタンスは、既定のスタイルの独自のコピーを取得するため、固定する必要はありません。ただし、このスタイルには他にもいくつかのセッター (重要なテンプレートを含む) があるため、それらをコントロールの複数のインスタンスで共有できるようにしたいと考えています。
2) タイプのPen
プロパティを、ブラシ、太さ、ダッシュ スタイルなどの個別のプロパティに分割します。これにより{DynamicResource}
、スタイルのセッターで直接使用され、スタイルを固定できます。(ただし、スタイルがいつフリーズ可能になるかについての正確な詳細はわかりません)ペン型のプロパティが複数あり、それぞれにユーザーがカスタマイズしたいプロパティがかなりあるため、この解決策は望ましくありません。また、コントロールのパブリック API に重大な変更を導入することなく、この例外を修正したいと考えています。
他のアイデアはありますか?DividerPen プロパティにこの種の既定値を指定する他の方法はありますか?
複数の UI スレッドの意味について混乱があるように思われるので、独自のスレッドで新しいウィンドウを開くコードを次に示します。
public void OpenNewWindow()
{
Thread t = new Thread(new ThreadStart(Run));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
void Run()
{
var window = new Window();
window.Content = new MyUserControl();
window.Show();
// just for test purposes; a real multithreaded WPF app needs logic to shutdown the dispatcher when the window is closed
Dispatcher.Run();
}