在ControlTemplate上覆盖IsMouseOver会使Button消失

问题描述

我是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相同的不同绑定语法。