WPF动态更改资源文件和主题

10

我的项目在整个项目中的所有WPF窗口都使用一个ProjectTheme.xaml文件。 ProjectTheme.xaml文件引用样式主题如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <!-- In order to modify the project's theme, change this line -->
        <ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

所有的WPF窗口都引用了WindowBase.xaml。

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/MyProject;component/View/WindowBase.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

WindowBase.xaml 引用了自定义标题栏 Bar1.xaml。

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Bar1.xaml" />
</ResourceDictionary.MergedDictionaries>

Bar1.xaml 引用了 ProjectTheme.xaml

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyProject;component/ProjectTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>

因此,层次结构如下:

  • Window1 引用 WindowBase.xaml
  • WindowBase 引用 Bar1.xaml
  • Bar1 引用 ProjectTheme.xaml
  • ProjectTheme.xaml 引用实际的主题资源文件。

这个工作得很好。 现在我想在运行时动态更改项目主题而不退出应用程序。 假设我有几个主题样式文件:

  • Customized.xaml
  • Customized1.xaml
  • Customized2.xaml

我的问题是,是否可能在运行时动态更新 ProjectTheme.xaml 文件以更改以下行:

<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />

<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized1.xaml" />

如何实现我的目标? 如果可以,我该怎么做? 如果不行,原因是什么,以及实现目的的最佳(其他)方式是什么?

我已尝试以下方法,但均无法正常工作:样式未发生变化。

方法1

Application.Current.Resources.MergedDictionaries.Clear();
Uri NewTheme = new Uri(@"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Add(dictionary);

方法2

Application.Current.Resources.MergedDictionaries.RemoveAt(0);
Uri NewTheme = new Uri(@"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Insert(0, dictionary);
注意:在我的真实主题样式文件(Customized.xaml...)中,我使用了动态资源和静态资源的组合。这会有影响吗?
提前致谢。
1个回答

17

这里有几件事情需要考虑。

首先,使用 StaticResource 定义的内容不会在更改时更新。如果您希望控件在运行时支持更改主题,则需要使用 DynamicResource ,以便它知道要查找更改。

您更改主题的总体方法是正确的。最简单的方法是使用应用程序范围资源字典,确保您的ResourceDictionaryApp.xaml中定义。对于添加新资源,我使用类似以下代码片段的代码:

ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);

Application.Current.Resources.MergedDictionaries.Add(dict);

你可能会混淆的是在基类中使用资源的时候。当你在一个类中定义一个资源时,该资源将局限于该类型的实例。想象一下XAML编译成自己的InitializeComponent()方法,这意味着你不能改变原来的XAML并期望更改应用到所有实例上。同样,更改类实例上的资源不会影响其他实例。

由于你的问题实际上包含了两个不同的问题(应用主题和更改控件资源),我建议确保你的应用程序资源正确更新并使用DynamicResource,希望我提供的信息足以理解为什么某些其他资源可能尚未更新。


Will,谢谢回复。这解释了为什么它没有起作用。我没有把资源放在 App.xaml 中,因为我的项目是一个类库,旨在被非 .net 程序调用。在这种情况下,是否仍然有可能动态更改主题? - Shawn
应该是可以的。我自己没有尝试过,但我会尝试在代码中定义一个静态的“ResourceDictionary”,其中包含您的其他“MergedDictionaries”,并尝试更改它以查看是否有效。从我查看的其他问题来看,似乎这可能是其他人处理此问题的方式。https://dev59.com/4U7Sa4cB1Zd3GeqP3Wyo - Will Eddins
Will,你的解释帮我找到了正确的方向。非常感谢你,我很感激。 - Shawn
关于 DynamicResource。如果您在转换器中定义颜色,情况如何? <conv:RejectedOrMissedCallColorConverter x:Key ="RejectedOrMissedCallColorConverter" MatchColor ="Red" DefaultColor ="{StaticResource BlackColorBrush}"/>。我无法在此处使用dynamic... - JobaDiniz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接