问题描述
假设我在WPF应用程序的灰色背景网格中有一个简单的按钮:
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Hello" Width="100" Height="50" Background="#333333" BorderThickness="0"/>
<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>
解决方法
WPF中的ControlTemplate
定义控件的外观和视觉状态,例如鼠标悬停或聚焦。如果覆盖控件模板,则将从头开始。您不能仅覆盖特定的部分,例如鼠标悬停状态,而忽略其余部分。该控件将无法正常工作。您必须重新创建整个控制模板或采用默认模板并对其进行调整。
每个控件都有一个默认模板,通常会根据给定的要求进行提取和修改。您可以在MSDN e.g. for Button
上找到所有部分,状态和控制模板的一些示例。如果您未实现任何已定义的状态或部分,则您的按钮可能无法像预期的那样工作。
MSDN上的控制模板示例可能不完整或已过时。您可以使用Blend或Visual Studio提取默认控件模板。有关说明,请参见此related post。
问题是:为什么通过添加此样式标签来改变颜色并移动文本?
模板中任何控件的Background
都会获得其默认值,您显式分配的值或标记扩展或绑定提供的值。在您的示例中,所有背景值都丢失了(因此是默认值)或硬编码。
如果要使用模板化Button
上定义的背景色,则必须使用TemplateBinding
或RelativeSource
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,以更好地了解它们的工作原理。