Было запрошено, чтобы мы добавили динамическое переключение тем в наше приложение, и у меня возникают проблемы с выяснением, как это сделать.
Вот текущая ситуация: наше приложение имеет объединенный словарь ресурсов с явными (неявными) стилями. Представления в нашем приложении ссылаются на эти стили через расширение разметки StaticResource
. Причина, по которой мы не используем неявные стили, состоит в том, что у нас несколько типов общих элементов управления; например, кнопки в одном месте выглядят иначе, чем кнопки в другом.
Итак, мы хотим создать темы, которые заменят эти именованные стили новым набором именованных стилей.
Подход 1
Первой попыткой было создать словари ресурсов для каждой темы. Я удалил один из стилей из общих словарей и поместил их в каждый словарь темы, придав каждой копии индивидуальный вид, например:
<!-- 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);
}
}
Это сработало в некоторой степени. Если я загрузил «theme» во время запуска, отобразился стиль этой темы. Однако, то, что не работало, пыталось переключиться на другую тему после запуска. Добавление словаря другой темы к объединенным словарям не привело к изменению пользовательского интерфейса. Ни один из них не очистил старый словарь тем и не добавил новый. Ни один из них не сделал ничего, после чего удалил корневой визуал и добавил его снова.
Подход 2
После этого я попытался использовать шаблон тем Silverlight Toolkit. Полагаю, это не сработало, потому что предназначено для переключения неявных стилей вместо явных . Я создал свой производный от Theme класс, установил URI его словаря ресурсов и добавил эту тему в корневой визуальный элемент ContentControl
. Затем, однако, когда я создал свой основной класс пользовательского интерфейса, явный стиль, на который ссылается пользовательский интерфейс, не был найден, поэтому возникло исключение времени выполнения.
Итак, я попытался загрузить один из словарей ресурсов темы в объединенные словари приложения. Это позволило создать основной класс пользовательского интерфейса и поместить его в объект Style
. Однако явные стили внутри словаря ресурсов Style
не смогли переопределить стили, определенные внутри объединенных словарей приложения. Следовательно, ничего не изменилось.
Текущий подход
Так что теперь я рассматриваю третий подход. Это будет связано с прикрепленным свойством, используемым примерно так: Theming.Style="StyleName"
. Затем в другом месте приложения будет поддерживаться связь между именами тем и переопределениями имен стилей. Всякий раз, когда тема меняется, применяются правильные стили для темы.
Дилемма
Я бы не стал изобретать колесо, если оно уже есть. В Silverlight уже есть что-то, что позволяет переключать темы, содержащие явные стили? Есть ли сторонние библиотеки, которые позволили бы мне делать то, что я хочу?