Создание определений псевдонимов цветов для нескольких словарей динамических тем оформления с помощью WPF.

Я пытаюсь настроить структуру темы в большом приложении WPF. В настоящее время решение, которое мы нашли, заключается в создании отдельных файлов .xaml для каждой цветовой палитры, например:

<LightColors.xaml>
<Color x:Key="MainPanelColor">Aqua</Color>
<Color x:Key="MainItemColor">Orange</Color>
<SolidColorBrush x:Key="MainPanelBrush" Color="{StaticResource MainPanelColor}" />
<SolidColorBrush x:Key="MainItemBrush"  Color="{StaticResource MainItemColor}" />

Затем пользовательский интерфейс ссылается на эти элементы следующим образом:

<Textblock Foreground="{DynamicResource MainItemBrush}"/>

Цветовые палитры изменяются во время выполнения в C#. Эта структура тем позволяет изменять темы во время выполнения.

Проблема: я хочу создать слой между пользовательским интерфейсом и цветами, чтобы цвета палитры можно было связать с большим списком определений цветов, используемых в пользовательском интерфейсе. Единственное решение, которое я нашел близко к работе, это добавить такой файл:

<ColorDefinitions.xaml>
<DynamicResource x:Key="Textblock_SetupPage_Foreground" ResourceKey="MainItemBrush" />
<DynamicResource x:Key="SecondDefinition" ResourceKey="MainItemBrush" />

И ссылка на этот новый ресурс в пользовательском интерфейсе выглядит так:

<Textblock Foreground="{StaticResource Textblock_SetupPage_Foreground}" />

Однако это решение не работает в полной мере. Он позволяет только одному элементу пользовательского интерфейса использовать один из DynamicResources, например «Textblock_SetupPage_Foreground», а изменение ссылки Textblock на DynamicResource приводит к ошибке. Как я могу выполнить эту задачу?


person Z Lang    schedule 19.10.2018    source источник


Ответы (1)


Не совсем уверен, решит ли это вашу проблему, но я могу показать вам, как я реализую скиннинг в наших бизнес-объектах. Пример решения состоит из двух сборок и примера приложения.

MyCustomControlLibrary определяет цвета и кисти, а также пример пользовательского элемента управления. Цвета и кисти могут быть дополнительно разделены на дополнительную сборку.

MySkinsLibrary определяет скины и использует определения (ресурсы) из MyControlLibrary.

WpfSkinTestApp использует скины и косвенно использует MyCostumControlLibrary.

Решение для тестирования WPFSkinning

Цвета.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:MyCustomControlLibrary">
<Color x:Key="MyDarkColor">#FF123456</Color>
<Color x:Key="MyLightColor">#FF456789</Color>
<Color x:Key="MyNeutralColor">#FF666666</Color>

Brushes.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:MyCustomControlLibrary">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyCustomControlLibrary;component/ResourceDictionaries/Colors.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="MyDarkColorBrush" Color="{StaticResource MyDarkColor}"/>
<SolidColorBrush x:Key="MyLightColorBrush" Color="{StaticResource MyLightColor}"/>
<SolidColorBrush x:Key="MyNeutralColorBrush" Color="{StaticResource MyNeutralColor}"/>

Generic.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyCustomControlLibrary">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyCustomControlLibrary;component/ResourceDictionaries/Brushes.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Background" Value="{StaticResource MyNeutralColorBrush}"/>
    <Setter Property="BorderBrush" Value="White"/>
    <Setter Property="BorderThickness" Value="6"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">

                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

DarkSkin.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:MySkinsLibrary.Skins"
                xmlns:customControls="clr-namespace:MyCustomControlLibrary;assembly=MyCustomControlLibrary">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyCustomControlLibrary;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>

<Style x:Key="MyTextBlockStyle" TargetType="TextBlock">
    <Setter Property="Background" Value="Gray"/>
    <Setter Property="Foreground" Value="{StaticResource MyDarkColorBrush}"/>
</Style>

<Style x:Key="MyCustomControlStyle" TargetType="{x:Type customControls:MyCustomControl}" BasedOn="{StaticResource {x:Type customControls:MyCustomControl}}">
    <Setter Property="Background" Value="{StaticResource MyDarkColorBrush}"/>
</Style>

LightSkin.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:MySkinsLibrary.Skins"
                xmlns:customControls="clr-namespace:MyCustomControlLibrary;assembly=MyCustomControlLibrary">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyCustomControlLibrary;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>

<Style x:Key="MyTextBlockStyle" TargetType="TextBlock">
    <Setter Property="Background" Value="Gray"/>
    <Setter Property="Foreground" Value="{StaticResource MyLightColorBrush}"/>
</Style>

<Style x:Key="MyCustomControlStyle" TargetType="{x:Type customControls:MyCustomControl}" BasedOn="{StaticResource {x:Type customControls:MyCustomControl}}">
    <Setter Property="Background" Value="{StaticResource MyLightColorBrush}"/>
</Style>

In your application you can use the different skins in the app.xaml, like this: (for design purpose)

<Application x:Class="WpfSkinTestApp.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:WpfSkinTestApp"
         StartupUri="MainWindow.xaml">
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/MySkinsLibrary;component/Skins/LightSkin.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

and changing it during runtime e.g. like this:

private void RadioButton_Checked(object sender, RoutedEventArgs e)
    {
        string skinName = "LightSkin";

        if (((RadioButton)sender).Name == "DarkSkin")
        {
            skinName = "DarkSkin";
        }

        ResourceDictionary resources = new ResourceDictionary();
        resources.Source = new Uri($"pack://application:,,,/MySkinsLibrary;component/Skins/{skinName}.xaml");
        Application.Current.Resources = resources;
    }

Для полноты картины это главное окно тестового приложения:

<Window x:Class="WpfSkinTestApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:customControls="clr-namespace:MyCustomControlLibrary;assembly=MyCustomControlLibrary"
    xmlns:local="clr-namespace:WpfSkinTestApp"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525"
    Background="{StaticResource MyNeutralColorBrush}">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Column="1"
               Grid.Row="1"
               Text="Example Colors"
               Style="{DynamicResource MyTextBlockStyle}"/>
    <customControls:MyCustomControl Grid.Column="1"
                                    Grid.Row="2"
                                    Style="{DynamicResource MyCustomControlStyle}"/>
    <StackPanel Grid.Column="2"
                Grid.Row="2"
                Margin="24"                    >
        <RadioButton x:Name="LightSkin" GroupName="1" Content="Light Skin" IsChecked="True" Checked="RadioButton_Checked"/>
        <RadioButton x:Name="DarkSkin" GroupName="1" Content="Dark Skin" Checked="RadioButton_Checked"/>
    </StackPanel>
</Grid>

Let me know, if this is what you´re looking for.

person Todd Nedd    schedule 28.10.2018
comment
Спасибо за усилия, приложенные к этому ответу. Ваше решение похоже на то, что я в итоге использовал: переопределение ресурсов в каждом словаре ресурсов скина. Я переопределяю ресурсы, такие как Button_Base_BorderDisabled, для цветов и кистей, а вы переопределяете стили, такие как MyTextBlockStyle. Я надеюсь, что все еще существует... это способ связать цвета в один псевдоним. Например, создайте псевдоним для MyDarkColor, MyLightColor и MyNeutralColor, такой как MainColor, и используйте только ссылку MainColor во всем пользовательском интерфейсе, но сделайте ее динамически изменяемой. - person Z Lang; 30.10.2018