wpf - Workaround for dependency property value precendence
问题描述
I'm builiding a custom control as seen below:
<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>
The said control has 2 dependency properties: HoverForeground and HoverBackground, why did I define them? - So I can have infinite amount of buttons with relatively easy to set hover color.
The issue is, I can't walk around DPVP and whenever I set Foreground in another control (as seen below), the foreground will no longer change upon IsMouseOver event.
Here's the code for another control:
<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>
I appreciate any input.
解决方案
我阅读了您的问题,如果我理解正确,Foreground
在您的控件上设置 会覆盖您通过控件的Style
触发器对同一属性所做的更改。
我不建议您使用更改控件属性的触发器,特别是常见的属性,如Background
和Foreground
。您的控件的属性旨在由您的控件的使用者从您的代码外部设置。如果您Style
在其上使用并使用触发器来更改控件上的属性以响应某些事件,那么这对于您的控件的使用者来说可能是意外的,并且当使用者显式设置这些相同的属性时会被覆盖。您应该在控件上动态更改的唯一属性是只读属性。
在控件的生命周期内动态更改控件外观的更好方法(也是预期的方法)是使用ControlTemplate
. 控件模板不会更改控件的属性;相反,它们会更改自己元素的属性,这些元素用于绘制控件的视觉效果。例如,您可以使用 a Trigger
on yourControlTemplate
来在鼠标悬停在控件上时更改某些视觉元素的画笔,其方式与您在Style
's 触发器上所做的操作类似。除了您之外,没有人可以访问您正在更改画笔的元素,因此您可以随时随意更改您想要的内容。
此示例应该让您了解如何创建为您的控件Style
设置 a 的 a 。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
旨在用于设置控件属性的默认值。a 的触发器Style
也不例外;它们旨在为控件的属性设置默认值,但它们能够根据某些条件进行设置。例如,aTabControl
可能需要在其上使用触发器Style
来根据 的值设置其Template
属性的值TabStripPlacement
,因此控件会根据选项卡是放在左侧、顶部、右侧还是底部来自动切换模板。
推荐阅读
- javascript - 如何处理将数据持久化到数据库中的表单提交中断?
- flutter - Flutter apk 无法在物理设备上创建本地文本文件
- typescript - 在vscode中使用oclif,如何解决“如果没有引用...就无法命名推断的'flags'类型”问题?
- powershell - Powershell:从程序的自定义输出中过滤掉特定值
- php - Laravel 存储嵌套的 json
- snowflake-cloud-data-platform - 雪花检查模式是否存在
- vba - 为什么我的代码在 VBA 模块中运行,但在我为其分配按钮单击时却没有?
- opengl - 将带有 STB 的纹理加载到 OpenGL 中显示不正确的 alpha 值
- r - 以列名中的字符串为条件,从现有列中变异一个新列并粘贴值
- reactjs - React 在使用 Context 时会抛出错误。错误:元素类型无效:需要一个字符串(对于内置组件)