我正在构建如下所示的自定义控件:
<UserControl x:Class="App.Views.Components.MenuButton"
[...]>
<UserControl.Resources>
<Style TargetType="local:MenuButton">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{Binding HoverForeground, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Background" Value="{Binding HoverBackground, RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
</UserControl>
上述控件具有2个依赖项属性:HoverForeground和HoverBackground,为什么要定义它们? -因此,我可以使用相对容易设置的悬停颜色来设置无限数量的按钮。
问题是,我无法绕过DPVP,并且每当在另一个控件中设置“前景”时(如下图所示),前景在IsMouseOver事件发生时就不会更改。
这是另一个控件的代码:
<UserControl x:Class="App.Views.Components.Menu"
[...]>
<StackPanel Orientation="Vertical">
<local:MenuButton Foreground="{DynamicResource BackgroundDark}" HoverForeground="Red" HoverBackground="Red" Margin="8" Content="" FontSize="24"/>
<local:MenuButton Foreground="{DynamicResource BackgroundDark}" HoverForeground="Green" HoverBackground="Green" Margin="8" Content="" FontSize="24"/>
<local:MenuButton Foreground="{DynamicResource BackgroundDark}" HoverForeground="Blue" HoverBackground="Blue" Margin="8" Content="" FontSize="24"/>
<local:MenuButton Foreground="{DynamicResource BackgroundDark}" HoverForeground="Black" HoverBackground="Yellow" Margin="8" Content="" FontSize="24"/>
</StackPanel>
</UserControl>
我非常感谢您的投入。
答案 0 :(得分:1)
我阅读了您的问题,如果我理解正确,则在控件上设置Foreground
会覆盖您通过控件的Style
触发器对该属性进行的更改。
我不建议您使用触发器来更改控件的属性,尤其是Background
和Foreground
之类的常见属性。控件的属性旨在由控件的使用者从代码外部设置。如果您使用Style
并在其上使用触发器以响应某些事件而更改控件的属性,则这对于控件的使用者而言可能是意外的,而当使用者明确设置这些属性时,这些控件将被覆盖。您应该在控件上动态更改的唯一属性是只读属性。
一种动态更改控件生命周期的更好方法(也是预期的方法)是使用ControlTemplate
。控件模板不会更改控件的属性。取而代之的是,它们更改了自己元素的属性,用于绘制控件的视觉效果。例如,您可以在Trigger
上使用ControlTemplate
,当鼠标悬停在控件上时,它会更改某些视觉元素的笔刷,其方式类似于在{{1 }}的触发器。除了您之外,没有其他人可以访问要更改画笔的元素,因此您可以随时更改要更改的内容。
此示例应该使您了解如何创建为控件设置Style
的{{1}}。我只是匆忙编写了代码,没有对其进行测试,因此可能是拼写错误或其他内容:
Style
关于ControlTemplate
的一些注意事项是,它通常使用<Style TargetType="{x:Type local:MenuButton">
<Setter Property="Background" Value="#282828"/>
<Setter Property="Foreground" Value="#D0D0D0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MenuButton}">
<Border x:Name="TemplateRoot"
Background="{TemplateBinding HoverBackground}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"
HorizontalAlignement="{TemplateBinding HorizontalContentAlignement}"
VerticalAlignement="{TemplateBinding VerticalContentAlignement}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="TemplateRoot" Property="Background"
Value="{Binding HoverBackground,
RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="contentPresenter" Property="TextElement.Foreground"
Value="{Binding HoverForeground,
RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
扩展名绑定到模板化控件的属性,而不是通常的ControlTemplate
,尽管这最后一个很好也一样使用TemplateBinding
时,请使用Binding
模式的Binding
源引用模板控件。例如:
RelativeSource
但是在可能的地方使用TemplatedParent
更加方便。因此,上面的绑定可以这样写:
{Binding Path=Background, RelativeSource={RelativeSource Mode=TemplatedParent}}
为控件设置自定义TemplateBinding
的不利方面是可以理解的,那就是我们有时需要从头开始编写,这有时很痛苦。对于复杂的控件尤其如此。当我们只需要更改视觉上的一些内容时,就像您正在做的那样,似乎很想直接在控件的{TemplateBinding Background}
上进行更改。但是,我建议您反对这种做法,因为它往往会产生更多无法解决的问题。
ControlTemplate
和Style
的名称有些令人误解。基本上,它们都旨在以可重用的方式在您的应用程序中对控件做一些事情。简而言之,当您要设置与控件外观相关的并且可重用的内容时,应使用Style
。
ControlTemplate
用于设置控件属性的默认值。 ControlTemplate
的触发器也不例外。它们旨在将默认值设置为控件的属性,但是它们可以根据某些条件进行设置。例如,Style
可能需要在其Style
上使用触发器,以根据TabControl
的值来设置其Style
属性的值,因此控件会自动切换模板取决于选项卡是放在左侧,顶部,右侧还是底部。