依赖项属性值优先的解决方法

时间:2019-05-21 22:42:50

标签: wpf

我正在构建如下所示的自定义控件:

<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>

我非常感谢您的投入。

1 个答案:

答案 0 :(得分:1)

我阅读了您的问题,如果我理解正确,则在控件上设置Foreground会覆盖您通过控件的Style触发器对该属性进行的更改。

我不建议您使用触发器来更改控件的属性,尤其是BackgroundForeground之类的常见属性。控件的属性旨在由控件的使用者从代码外部设置。如果您使用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} 上进行更改。但是,我建议您反对这种做法,因为它往往会产生更多无法解决的问题。


ControlTemplateStyle的名称有些令人误解。基本上,它们都旨在以可重用的方式在您的应用程序中对控件做一些事情。简而言之,当您要设置与控件外观相关的并且可重用的内容时,应使用Style

ControlTemplate用于设置控件属性的默认值。 ControlTemplate的触发器也不例外。它们旨在将默认值设置为控件的属性,但是它们可以根据某些条件进行设置。例如,Style可能需要在其Style上使用触发器,以根据TabControl的值来设置其Style属性的值,因此控件会自动切换模板取决于选项卡是放在左侧,顶部,右侧还是底部。