【问题标题】:Overriding IsMouseOver on ControlTemplate makes Button disappear在 ControlTemplate 上覆盖 IsMouseOver 使 Button 消失
【发布时间】:2020-08-27 18:39:11
【问题描述】:

我是 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,但显然没有悬停效果。 我做错了什么?

额外问题:如果我想使用绑定,以便我可以在其他按钮的属性中设置无悬停图像和悬停图像的路径并使其行为方式相同,我怎样才能做到这一点?

【问题讨论】:

    标签: c# wpf xaml controltemplate


    【解决方案1】:

    如果要重新设置控件的外观及其视觉状态,则必须编辑其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,因为它会显示您分配给ButtonContent 属性的内容,但是由于您显示的是图像而不是内容,所以我省略了.

    请注意,此模板中有TemplateBindings。这些用于绑定应用模板的控件的指定属性。换句话说,如果您直接在按钮上设置Background 或从您设置它的NavButton 派生Style,则将使用此值。如果您只是硬编码控件模板中的值,它不会改变。

    对于您的附加问题:当您使用这些TemplateBindings 时,直接设置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 中模板化父级的属性,您必须使用与TemplateBinding 具有相同效果的不同绑定语法。

    【讨论】:

    • 非常感谢!我创建了一个 HoverableButton 类,它添加了 OnHover 和 NoHover 属性并将它们用于绑定!
    【解决方案2】:

    因为当您设置 ControlTemplate 时,您基本上必须重新定义控件的显示方式。而且您只定义了没有任何内容表示的触发器。 ContentPresenter 控件将帮助您。只需在 ControlTemplate.Triggers 和 ControlTemplate 之后添加一些 Border 和 Content Presenter。这应该可以解决问题:

    </ControlTemplate.Triggers>
    <Border Background="{TemplateBinding Background}">
    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Border>
    </ControlTemplate>
    

    【讨论】:

      猜你喜欢
      • 2016-08-27
      • 1970-01-01
      • 1970-01-01
      • 2014-07-05
      • 2014-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多