我正在构建一个自定义控件,如下所示:
<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 个依赖项属性:悬停前景和悬停背景,我为什么要定义它们? - 所以我可以有无限数量的按钮,相对容易设置悬停颜色。
问题是,我无法在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>
我感谢任何意见。
我读了你的问题,如果我理解正确的话,在控件上设置Foreground
会覆盖你通过控件的Style
触发器对同一属性所做的更改。
我不建议您使用更改控件属性的触发器,尤其是Background
和Foreground
等常见属性。控件的属性旨在由控件的使用者从代码外部设置。如果使用Style
并在其上使用触发器来更改控件上的属性以响应某些事件,则对于控件的使用者来说,这可能是意外的,并且在使用者显式设置这些相同的属性时被重写。应在控件上动态更改的唯一属性是只读属性。
在控件的生存期内动态更改控件外观的更好方法(也是预期方法)是使用ControlTemplate
。控件模板不会更改控件的属性;相反,它们更改自己的元素的属性,这些元素用于绘制控件的视觉对象。例如,您可以在ControlTemplate
上使用一个Trigger
,当鼠标悬停在控件上时,它会更改某些视觉元素的画笔,其方式类似于您在Style
触发器上所做的那样。除了你之外,没有人可以访问你正在更改画笔的元素,所以你可以随时自由地改变你想要的东西。
此示例应让您了解如何创建为控件设置ControlTemplate
的Style
。我只是匆忙编写了代码,没有对其进行测试,所以可能是一些错别字或其他东西:
<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
时,可以使用具有TemplatedParent
模式的RelativeSource
源引用模板化控件。例如:
{Binding Path=Background, RelativeSource={RelativeSource Mode=TemplatedParent}}
但是使用TemplateBinding
,在可以使用的地方,只是更方便。所以上面的绑定可以这样写:
{TemplateBinding Background}
为控件设置自定义ControlTemplate
可以理解的缺点是我们需要从头开始编写它,这有时很痛苦。对于复杂的控件尤其如此。当我们只需要更改视觉对象上的一些内容时,似乎很容易直接在控件的Style
上执行此操作,就像您正在做的那样。但是,我建议不要这样做,因为它往往会产生比解决更多的问题。
Style
和ControlTemplate
的名字有些误导。基本上,它们都旨在以可重用的方式在应用程序中对控件执行某些操作。简而言之,当您要设置与控件外观相关的内容并且可重用时,您应该使用ControlTemplate
.
该Style
用于设置控件属性的默认值。Style
的触发因素也不例外;它们旨在为控件的属性设置默认值,但它们能够根据某些条件执行此操作。例如,TabControl
可能需要在其Style
上使用触发器,以根据TabStripPlacement
的值设置其Template
属性的值,因此控件会根据选项卡是放置在左侧、顶部、右侧还是底部自动切换模板。