为什么WPF样式标签会覆盖按钮的背景颜色和其他属性?

问题描述

假设我在WPF应用程序的灰色背景网格中有一个简单的按钮:

enter image description here

<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Hello" Width="100" Height="50" Background="#333333" BorderThickness="0"/> 

我想通过添加以下代码来更改按钮的MouSEOver行为:

<Button.Style>
    <Style targettype="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate targettype="{x:Type Button}">
                    <Border>
                        <Border.Style>
                            <Style targettype="{x:Type Border}">
                                <Style.Triggers>
                                    <Trigger Property="IsMouSEOver" Value="True">
                                        <Setter Property="Background" Value="#404040"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Border.Style>
                        <Grid Background="Transparent">
                            <ContentPresenter></ContentPresenter>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Button.Style>

问题是:为什么通过添加此样式标签来改变颜色并移动文本?

enter image description here

解决方法

WPF中的ControlTemplate定义控件的外观和视觉状态,例如鼠标悬停或聚焦。如果覆盖控件模板,则将从头开始。您不能仅覆盖特定的部分,例如鼠标悬停状态,而忽略其余部分。该控件将无法正常工作。您必须重新创建整个控制模板或采用默认模板并对其进行调整。

每个控件都有一个默认模板,通常会根据给定的要求进行提取和修改。您可以在MSDN e.g. for Button上找到所有部分,状态和控制模板的一些示例。如果您未实现任何已定义的状态或部分,则您的按钮可能无法像预期的那样工作。

MSDN上的控制模板示例可能不完整或已过时。您可以使用Blend或Visual Studio提取默认控件模板。有关说明,请参见此related post

问题是:为什么通过添加此样式标签来改变颜色并移动文本?

模板中任何控件的Background都会获得其默认值,您显式分配的值或标记扩展或绑定提供的值。在您的示例中,所有背景值都丢失了(因此是默认值)或硬编码。

如果要使用模板化Button上定义的背景色,则必须使用TemplateBindingRelativeSource binding on TemplatedParent,例如在您的Border上:

<Border Background="{TemplateBinding Background">

这会将分配给模板按钮(直接或通过Background的{​​{1}}的值应用于您的Style。本质上,模板绑定使您可以设置控件的样式。

为什么要移动文本?那是因为您没有定义它。请记住,您从头开始。尝试像这样设置Border,您会看到ContentPresenter居中:

Content

关于鼠标悬停样式的另一句话。在控制模板中,您可以使用VisualStateManager或通过定义triggers on your control template来定义状态。

例如,对于鼠标悬停状态,您首先要为<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> 分配一个名称:

Border

然后,您可以将引用该名称的触发器添加到控制模板以设置<Border x:Name="ButtonBorder" ... >

Background

控制模板不是很简单并且很容易正确。我建议您阅读introduction to creating control templates,以更好地了解它们的工作原理。