问题描述
我是WPF的新手,我正在尝试为我的应用程序中的按钮定义按钮样式。我添加了以下ResourceDictionary
并将其添加到App.xaml
的应用程序资源中:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ReciepeFinder">
<Style x:Key="NavButton" targettype="Button">
<Setter Property="Width"
Value="120" />
<Setter Property="Height"
Value="120" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home.png" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate targettype="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouSEOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
基本上,没有悬停时,我希望按钮显示resources/images/home.png
而悬停时,我希望按钮resources/images/home_selected.png
。当我尝试按照上面的代码定义ControlTemplate
并运行该应用程序时,该按钮没有出现。但是,如果我完全删除ControlTemplate
标签,则按钮在那里带有正确的Background
,但显然没有悬停效果。
我在做什么错了?
奖励问题:如果我想使用绑定功能,以便可以在其他按钮的属性中设置无悬停图像和悬停图像的路径,并使其行为相同,我怎么能做到?
解决方法
因为设置ControlTemplate时,基本上必须重新定义控件的显示方式。您仅定义了触发器,没有任何内容表示。 ContentPresenter控件将为您提供帮助。只需在ControlTemplate.Triggers和ControlTemplate之后添加一些“边框和内容”演示者。这应该可以解决问题:
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
,
如果要重新设置控件的外观及其视觉状态,则必须编辑其ControlTemplate
。但是,控件模板包含控件必需部分的定义,以及它在不同状态下的外观以及它们之间的过渡。要使ControlTemplate
正常工作,必须实现所有这些部分和状态。您可以在e.g. for Button
上找到有关MSDN上每个内置控件的必需部件和状态的信息。
您可以复制默认模板并对其进行调整,而不必从头开始创建ControlTemplate
,这更加容易。您可以使用Blend或Visual Studio提取它。
您的控制模板无效,因为它实际上是空的。没有什么可以显示背景或任何内容。它不包含任何视觉效果或控件。我为您创建了一个示例控件模板,该模板应符合您的要求。
<Style x:Key="NavButton" TargetType="{x:Type Button}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home.png" />
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="true"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="border" Property="Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Border
显示图像背景。通常,您将ContentPresenter
放在Border
内,因为它会显示您分配给Content
的{{1}}属性的内容,但是由于显示的是图像而不是内容,我已经省略了。
请注意,此模板中有Button
个。这些用于绑定应用模板的控件中的指定属性。换句话说,如果直接在按钮上设置TemplateBinding
或在设置位置创建Background
派生的Style
,则将使用此值。如果只是将值硬编码到控制模板中,则它不会更改。
关于您的奖励问题:当您使用这些NavButton
时,可以如上所述直接设置TemplateBinding
或在派生的Background
中进行设置,因此可以重复使用。由于没有Style
属性,您要么必须创建一个派生按钮,然后向其添加依赖项属性,要么创建一个附加属性,要么使用例如为此,您可以使用未使用的HoverBackground
属性,尽管这感觉不合适,因为这是一种肮脏的解决方法。这是Foreground
的示例,只是因为它很容易为您测试,但也适用于上述其他选项。
Foreground
<Setter Property="Foreground">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
请注意,要在<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Foreground}"/>
</Trigger>
中访问模板化父级的属性,必须使用与Setter
相同的不同绑定语法。