在Aero主题下如何覆盖WPF中的按钮背景

9

因此,我的愿望很简单,将按钮的背景颜色在鼠标悬停时更改为浅绿色和绿色,在按下时更改为深绿色。以下XAML是我的第一次尝试:

<Window x:Class="ButtonMouseOver.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Button Background="LightGreen">
      Hello
      <Button.Style>
        <Style TargetType="Button">
          <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
              <Setter Property = "Background" Value="Green"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter Property = "Foreground" Value="DarkGreen"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </Button.Style>
    </Button>
  </Grid>
</Window>

但是,遗憾的是这并不起作用。我们只完成了目标的三分之一。是的,按钮变成了浅绿色,但是一旦你将鼠标悬停在它上面或按下它,你会得到相应按钮状态的标准Aero chrome。为了不放弃,我尝试了以下淫荡行为:

...
    <Button xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
            Background="LightGreen">
      Hello
      <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
          <Microsoft_Windows_Themes:ButtonChrome SnapsToDevicePixels="true" 
            x:Name="Chrome" Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" RenderDefaulted="{TemplateBinding IsDefaulted}" 
            RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}"
            >
            <ContentPresenter Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          RecognizesAccessKey="True"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
          </Microsoft_Windows_Themes:ButtonChrome>
        </ControlTemplate>
      </Button.Template>
      <Button.Style>
        <Style TargetType="Button">
          <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
              <Setter Property = "Background" Value="Green"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter Property = "Foreground" Value="DarkGreen"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </Button.Style>
    </Button>
...

现在我们已经把整个控件模板从 Aero.NormalColor.xaml 中提取出来了。但是,这并没有改变任何东西。然后我尝试从 Microsoft_Windows_Themes:ButtonChrome 元素中删除两个属性(RenderPressedRenderMouseOver),但仍然没有改变。最后,我删除了按钮的 Background 属性设置,只保留了 PresentationFramework.Aero 程序集的命名空间声明:

...
<Button xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
  Hello
...

终于有了进展。我们完成了 1/3 的需求,已经实现了 IsMouseOver 和 IsPressed,但是在按钮正常状态下(即没有鼠标悬停或按下),没有浅绿色的背景,因为我从按钮中移除了 Background 属性,以便让其他两个状态适用并可见。现在,在这个狂热的 XAML 无法为普通按钮状态获取 LightGreen 背景之后,我修改了样式,将背景颜色添加到其中:

  <Button xmlns...
    <ControlTemplate...
    ...
    </ControlTemplate>
    <Button.Style> 
      <Style TargetType="Button">
        <!-- ##### Normal state button background added ##### -->
        <Setter Property="Background" Value="LightGreen" />
        <!-- ################################################ -->
        <Style.Triggers>
          <Trigger Property="IsMouseOver" Value="true">
            <Setter Property = "Background" Value="Green"/>
          </Trigger>
          <Trigger Property="IsPressed" Value="true">
            <Setter Property = "Background" Value="DarkGreen"/>
          </Trigger>
        </Style.Triggers>
      </Style>
    </Button.Style> 
  </Button>

最终,它按照预期工作了。
我是疯了吗?还是这些(以及更多)是Aero主题中必须经过的步骤,只是为了改变按钮在其各种状态下(正常、悬停、按下)的背景颜色?也许,如果我不必在其中包含整个ButtonTemplate来删除它中的两个属性(RenderMouseOver和RenderPressed),生活就不会那么糟糕。
感谢任何帮助 - 我已经到了无法再忍受的地步。
更新:但是这还不是全部。与其从控件模板中删除RenderMouseOver和RenderPressed属性,不如将它们从以下内容更改:
<Microsoft_Windows_Themes:ButtonChrome SnapsToDevicePixels="true" ...
  RenderMouseOver="{TemplateBinding IsMouseOver}"  
  RenderPressed="{TemplateBinding IsPressed}" ...

to:

<Microsoft_Windows_Themes:ButtonChrome SnapsToDevicePixels="true" ...
  RenderMouseOver="{Binding IsMouseOver}"  
  RenderPressed="{Binding IsPressed}" ...

...,还修复了忽略按钮样式触发器的问题。我认为问题的根源在于模板绑定是在编译时应用的(出于性能考虑),而常规绑定是在运行时应用的。因此,如果属性使用模板绑定(即,原始模板中的IsMouseOver和IsPressed被使用),则来自各个按钮的样式触发器的绑定将不会被使用。

以上所有内容说完了,这里是在保持原始控件模板的情况下更改Aero按钮背景的典型示例:

<Window x:Class="ButtonMouseOver.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Button>
      Hello
      <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
          <Microsoft_Windows_Themes:ButtonChrome SnapsToDevicePixels="true" 
            x:Name="Chrome" Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" RenderDefaulted="{TemplateBinding IsDefaulted}" 
            RenderMouseOver="{Binding IsMouseOver}" RenderPressed="{Binding IsPressed}"
            >
            <ContentPresenter Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          RecognizesAccessKey="True"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
          </Microsoft_Windows_Themes:ButtonChrome>
        </ControlTemplate>
      </Button.Template>
      <Button.Style>
        <Style TargetType="Button">
          <Setter Property="Background" Value="LightGreen"/>
          <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
              <Setter Property = "Background" Value="Green"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter Property = "Foreground" Value="DarkGreen"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </Button.Style>
    </Button>
  </Grid>
</Window>

当然,这是在只有一个按钮被这样设计的情况下,否则模板和样式可以移动到某个资源部分。此外,按钮的默认状态没有触发器,但可以很容易地添加到上面的示例中(不要忘记将控件模板中的RenderDefaulted属性更改为使用Binding而不是TemplateBinding)。

如果有人对如何仅覆盖控件模板中需要更改的两个属性的TemplateBinding而无需整个从aero xaml批量重复chrome定义的方法有任何建议,我会认真听取。

1个回答

2
Button的默认模板使用ButtonChrome进行绘制。如果您想完全摆脱默认外观,则需要定义自己的模板,不含ButtonChrome。我知道这听起来很繁琐,但您只需要做一次:可以将您的自定义样式放入资源字典中,并使用StaticResource引用它。请注意,您还可以通过将{x:Type Button}用作样式的键(x:Key属性),将该样式应用于应用程序中所有按钮。

问题在于我只想改变按钮的背景。Aero chrome使用按钮的正常状态下的Background Button属性来正确处理此操作。但是,这种处理方式存在缺陷,因为当鼠标悬停或按下按钮时,背景会立即改变回去。似乎没有简单的方法可以在这两种状态下更改按钮的背景。这使得Button(以及其他视觉元素)中具有Background属性的整个目的都失去了意义。 - Michael Goldshteyn
Background属性对于所有控件都是通用的,因此它总会存在,但不能保证模板会考虑它。我可以在Aero主题下重现您的问题,并在Luna主题下遇到类似的问题。似乎ButtonChrome在某些状态下不考虑背景...无论如何,我认为标准按钮chrome并不是设计用于自定义其颜色的。如果您想这样做,就不要使用chrome...而且即使没有它,创建自己的按钮模板也很容易。 - Thomas Levesque
来自 MSDN(Control.Background 属性):Background 属性仅适用于控件的静止状态。控件的默认样式指定了其在控件状态更改时的外观。例如,如果您在 Button 上设置 Background 属性,则该按钮仅在未被按下或禁用时具有该值。如果您想创建一个具有更高级背景自定义的控件,则必须定义控件的样式。 - Thomas Levesque
我知道Background属性只适用于正常状态。按钮样式触发器没有被考虑进去是按钮外观设计的一个严重问题,这迫使你重新定义整个控件模板。或者,我在Style属性上做错了什么,以至于它不能覆盖控件模板的样式和触发器。 - Michael Goldshteyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接