4

有人要求我们向我们的应用程序添加动态主题切换,但我在弄清楚如何做到这一点时遇到了问题。

这是当前的情况:我们的应用程序有一个具有显式(非隐式)样式的合并资源字典。StaticResource我们应用程序中的视图通过标记扩展引用这些样式。我们不使用隐式样式的原因是我们对常见类型的控件有多种外观;例如,一个位置的按钮看起来与另一个位置的按钮不同。

所以我们想要做的是创建主题,用一组新的命名样式替换这些命名样式。

方法一

第一次尝试是为每个主题创建资源字典。我从常用字典中删除了其中一种样式,并将它们放在每个主题字典中,使每个副本具有独特的外观,如下所示:

<!-- RedTheme.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    mc:Ignorable="d"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
  <Style x:Key="HeaderContentStyle" TargetType="ContentControl">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="ContentControl">
          <Grid Margin="0" Background="Red">
            <ContentPresenter Content="{TemplateBinding Content}"/>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>


<!-- BlueTheme.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    mc:Ignorable="d"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
  <Style x:Key="HeaderContentStyle" TargetType="ContentControl">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="ContentControl">
          <Grid Margin="0" Background="Blue">
            <ContentPresenter Content="{TemplateBinding Content}"/>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

然后,我添加了代码以从 XAML 动态加载资源字典之一并将其插入到应用程序的合并字典中:

// In App.xaml.cs
var themeUri = new Uri(
    "OurApp;component/Themes/RedTheme.xaml", UriKind.Relative);
var resourceInfo = GetResourceStream(themeUri);
using (var stream = resourceInfo.Stream)
{
    using (var reader = new StreamReader(stream))
    {
        var xamlText = reader.ReadToEnd();
        var dict = XamlReader.Load(xamlText) as ResourceDictionary;
        Resources.MergedDictionaries.Add(dict);
    }
}

这在一定程度上奏效了。如果我在启动期间加载了“主题”,则会显示该主题的样式。但是,没有奏效的是在启动后尝试切换到另一个主题。将其他主题主题字典添加到合并字典不会导致 UI 被修改。也没有清除旧的主题词典并添加新的。也没有做任何这些,然后删除根视觉并重新添加它。

方法二

之后,我尝试使用 Silverlight Toolkit 主题模式。我认为这不起作用,因为它旨在切换隐式样式而不是式样式。我创建了我的 Theme 派生类,设置了它的资源字典 URI,并将该主题添加到了 root visual ContentControl。但是,当我创建主 UI 类时,找不到 UI 引用的显式样式,因此发生了运行时异常。

因此,我尝试将其中一个主题资源字典加载到应用程序的合并字典中。这允许创建主 UI 类并将其放置在Style对象内部。但是,Style资源字典中的显式样式无法覆盖应用程序合并字典中定义的样式。因此,似乎没有任何改变。

当前方法

所以现在我正在考虑第三种方法。它将涉及使用类似这样的附加属性:Theming.Style="StyleName". 然后,在应用程序的其他地方,将维护主题名称和样式名称覆盖之间的关联。每当主题更改时,将应用主题的正确样式。

困境

如果已经有一个轮子,我宁愿不重新发明一个轮子。Silverlight 中是否已经内置了允许切换包含显式样式的主题的功能?是否有任何第三方库可以让我做我想做的事?

4

1 回答 1

4

我不再在公司工作,所以我不能发布我们最终使用的具体代码,但我们确实采用了附加属性方法。Style我们将需要动态蒙皮的对象的属性分配替换为Theming.Style设置为相同值的附加属性。然后,在我们的资源字典中,我们创建了以主题名称为前缀的样式,例如,我们曾经有一个“StandardButton”样式,我们将创建名称为“Blue|StandardButton”和“Clean|StandardButton”的其他样式。然后,我们的主题引擎将能够在我们切换主题时查找那些特定于主题的样式,并将它们应用于具有附加属性的元素。

希望这对其他人有用,但最好有一个图书馆已经解决了这个问题。

于 2010-10-21T20:37:16.150 回答