在 ControlTemplate 上重写 IsMouseOver 会使 Button 消失
Overriding IsMouseOver on ControlTemplate makes Button disappear
我是 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 之后添加一些 Border 和 Content presenter。这应该可以解决问题:
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
如果要重新设置控件的外观及其视觉状态,则必须编辑其 ControlTemplate
。但是,控件模板包含控件的必需部分的定义,以及它在不同状态下的外观和它们之间的转换。要使 ControlTemplate
按预期工作,您必须实现所有这些部分和状态。您可以在 MSDN e.g. for Button
.
上找到有关每个 built-in 控件所需部件和状态的信息
无需从头开始创建 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
显示图片背景。通常,您会在 Border
中放置一个 ContentPresenter
,因为它会显示您分配给 Button
的 Content
属性 的内容,但是因为你显示的是图片而不是内容,所以我省略了。
请注意,此模板中有 TemplateBinding
。这些用于绑定应用模板的控件的指定属性。换句话说,如果您直接在按钮上设置 Background
或从您设置的 NavButton
派生 Style
,将使用该值。如果您只是在控件模板中硬编码该值,它不会改变。
对于您的奖励问题:当您使用这些 TemplateBinding
时,直接或在派生的 Style
中设置 Background
将如上所述工作,因此您可以重复使用它。由于没有 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
中模板化父项的 属性,您必须使用不同的绑定语法,其效果与 TemplateBinding
相同。
我是 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 之后添加一些 Border 和 Content presenter。这应该可以解决问题:
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
如果要重新设置控件的外观及其视觉状态,则必须编辑其 ControlTemplate
。但是,控件模板包含控件的必需部分的定义,以及它在不同状态下的外观和它们之间的转换。要使 ControlTemplate
按预期工作,您必须实现所有这些部分和状态。您可以在 MSDN e.g. for Button
.
无需从头开始创建 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
显示图片背景。通常,您会在 Border
中放置一个 ContentPresenter
,因为它会显示您分配给 Button
的 Content
属性 的内容,但是因为你显示的是图片而不是内容,所以我省略了。
请注意,此模板中有 TemplateBinding
。这些用于绑定应用模板的控件的指定属性。换句话说,如果您直接在按钮上设置 Background
或从您设置的 NavButton
派生 Style
,将使用该值。如果您只是在控件模板中硬编码该值,它不会改变。
对于您的奖励问题:当您使用这些 TemplateBinding
时,直接或在派生的 Style
中设置 Background
将如上所述工作,因此您可以重复使用它。由于没有 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
中模板化父项的 属性,您必须使用不同的绑定语法,其效果与 TemplateBinding
相同。