Я хочу создать стиль для двух кнопок, причем обе они будут иметь одинаковые свойства, за исключением их цветов. См. Это изображение для справки.
В идеале я хотел бы создать базовый стиль с общим ControlTemplate для этих кнопок один раз, а затем каким-то образом переключить цвета.
Первоначально я думал создать базовый стиль, который использует ранее определенные SolidColorBrush
ресурсы для цветов, а затем добавить два дополнительных стиля (Accent1Button-Style и Accent2Button-Style), которые перезаписывают эти SolidColorBrush
ресурсы. Код будет выглядеть так:
<!-- Define color resources which are used by the Base-Button Style -->
<SolidColorBrush x:Key="ButtonBackgroundBrush" Color="{Binding Color, Source={StaticResource BaseMediumBrush}}" />
<SolidColorBrush x:Key="ButtonBackgroundHoverBrush" Color="{Binding Color, Source={StaticResource BaseHighBrush}}" />
<SolidColorBrush x:Key="ButtonBackgroundPressedBrush" Color="{Binding Color, Source={StaticResource BaseLowBrush}}" />
<SolidColorBrush x:Key="ButtonBackgroundDisabledBrush" Color="{Binding Color, Source={StaticResource BaseHighBrush}}" />
<SolidColorBrush x:Key="ButtonBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderHoverBrush" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderPressedBrush" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderDisabledBrush" Color="Transparent" />
<Style x:Key="StandardButton" TargetType="Button">
<!-- Properties removed to shorten the code -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd"
CornerRadius="{StaticResource ButtonCornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.2" />
<VisualTransition GeneratedDuration="0" To="Disabled" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Bd"
Storyboard.TargetProperty="Background.Color">
<EasingColorKeyFrame KeyTime="0" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Bd"
Storyboard.TargetProperty="BorderBrush.Color">
<EasingColorKeyFrame KeyTime="0" Value="{Binding Color, Source={StaticResource ButtonBorderHoverBrush}}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<!-- ... -->
</VisualState>
<VisualState x:Name="Disabled">
<!-- ... -->
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource StandardButton}">
<Style.Resources>
<!-- Overwrite the above colors here -->
<SolidColorBrush x:Key="ButtonBackgroundBrush" Color="{Binding Color, Source={StaticResource Accent1MediumBrush}}" />
<SolidColorBrush x:Key="ButtonBackgroundHoverBrush" Color="{Binding Color, Source={StaticResource Accent1HighBrush}}" />
<!-- And so on... -->
</Style.Resources>
</Style>
Однако это не работает, поскольку WPF не перезаписывает ресурсы.
Другим подходом было создание вспомогательного класса, который предлагает присоединенное свойство (например, ColorHelper.HoverColor
). Я думал об использовании этого свойства в шаблоне и настройке его в стилях Accent1 / Accent2, но это невозможно, поскольку для этого потребуются привязки TemplateBindings, которые не поддерживаются ColorAnimation
, используемым в шаблоне.
Прямо сейчас, единственный вариант, который я вижу, - это скопировать / вставить весь шаблон и переключить цвета в соответствующих местах (например, внутри анимации). Однако я бы хотел этого избежать.
Итак, наконец, есть способ определить мой базовый стиль только один раз, одновременно создавая отдельные «подстили», которые изменяют специфичные для шаблона свойства, которые не являются частью класса Button (например, HoverColor
, PressedColor
, ...)?
Edit:
Чтобы выяснить, почему подход с вложенными свойствами не будет работать: предположим, что я ввел класс ColorHelper
со следующим вложенным свойством:
public static readonly DependencyProperty HoverColorProperty =
DependencyProperty.RegisterAttached("HoverColor", typeof(Color), typeof(ColorHelper), new PropertyMetadata(Colors.Black));
public static Color GetHoverColor(DependencyObject obj)
{
return (Color)obj.GetValue(HoverColorProperty);
}
public static void SetHoverColor(DependencyObject obj, Color value)
{
obj.SetValue(HoverColorProperty, value);
}
Теоретически я мог бы установить это свойство в своих подстилях так:
<Style TargetType="Button" BasedOn="{StaticResource StandardButton}">
<Setter Property="local:ColorHelper.HoverColor" Value="Red" />
</Style>
<Style TargetType="Button" BasedOn="{StaticResource StandardButton>}">
<Setter Property="local:ColorHelper.HoverColor" Value="Blue" />
</Style>
Проблема этого решения заключается в базовом стиле, а точнее в следующей части кода:
<!-- snip -->
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Bd" Storyboard.TargetProperty="Background.Color">
<EasingColorKeyFrame KeyTime="0"
Value="{Binding Path=(local:ColorHelper.HoverColor), RelativeSource={RelativeSource TemplatedParent}}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- snip -->
Как вы можете видеть, значение EasingColorKeyFrame
через Binding
связано с TemplatedParent
(и я не вижу другого способа сделать это). Это не поддерживается WPF (из-за того, что Freezeables работают вместе с анимацией), и цвет никогда не используется в анимации.
Я также получаю следующее сообщение об ошибке в моем окне вывода: System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=(0); DataItem=null; target element is 'EasingColorKeyFrame' (HashCode=8140857); target property is 'Value' (type 'Color')