【问题标题】:Overriding button background in WPF on Aero在 Aero 上的 WPF 中覆盖按钮背景
【发布时间】:2011-05-24 09:46:13
【问题描述】:

因此,愿望很简单,将按钮的背景更改为浅绿色,当鼠标光标悬停在按钮上时为绿色,按下时为深绿色。以下 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>

但是,唉,这行不通。我们距离目标只有 1/3。是的,按钮变成浅绿色,但只要将鼠标悬停在它上面或按下它,您就会获得相应按钮状态的标准 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)。不过,没有任何变化。然后我删除 Button 的 Background 属性的设置,只留下 PresentationFramework.Aero 程序集的命名空间声明:

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

终于,我们取得了进展。我们已经从 1/3 的需求减少到了 2/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 包含在其中,只是为了从中删除两个属性(RenderMouseOverRenderPressed),生活不会那么糟糕。

感谢您的帮助 - 我无计可施。

更新:但是等等,还有更多。而不是从控件模板中删除 RenderMouseOver 和 RenderPressed 属性,只需将它们更改为:

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

到:

<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 with Binding 仅针对控件模板中需要更改的两个属性有任何建议,而无需从 aero xaml 批发中重复整个 chrome 定义,我我全神贯注。

【问题讨论】:

    标签: c# wpf c#-4.0 wpf-controls wpf-4.0


    【解决方案1】:

    Button 的默认模板使用ButtonChrome,它执行自己的绘图。如果你想完全摆脱默认外观,你需要定义自己的模板,没有ButtonChrome。我知道这听起来很乏味,但您只需要做一次:您可以将自定义样式放入资源字典中,然后使用StaticResource 引用它。请注意,您还可以使用 {x:Type Button} 作为样式的键(x:Key 属性)将样式应用于应用中的所有按钮

    【讨论】:

    • 问题是我只想改变一个按钮的背景。 Aero chrome 使用按钮正常状态的背景按钮属性正确处理此问题。但是,这种处理是错误的,因为背景会在鼠标悬停和按下按钮时立即改变。似乎没有简单的方法可以在这两种状态下更改按钮的背景。这违背了在 Button(可能还有其他视觉元素)中具有 Background 属性的全部目的。
    • Background 属性对所有控件都是通用的,所以无论如何它都会存在,但不能保证模板会考虑到它。我可以重现您的 Aero 主题问题,以及 Luna 主题的类似问题。似乎 ButtonChrome 没有考虑某些状态的背景...无论如何,我不认为标准按钮镀铬是为了定制其颜色而设计的。如果您想这样做,请不要使用 chrome...无论如何,如果没有它,创建自己的按钮模板非常容易。
    • 来自 MSDN(Control.Background 属性):Background 属性仅适用于控件的静止状态。控件的默认样式指定控件状态更改时的外观。例如,如果您在 Button 上设置 Background 属性,则该按钮仅在未按下或禁用时才具有该值。如果要创建具有更高级自定义背景的控件,则必须定义控件的样式。
    • 我了解 Background 属性仅适用于正常状态。按钮 chrome 的严重错误在于没有考虑按钮样式触发器,迫使您重新定义整个控件模板。要么,要么我对 Style 属性做错了,所以它不会覆盖控件模板样式和触发器。
    猜你喜欢
    • 2021-09-20
    • 2021-02-27
    • 2021-03-30
    • 2013-10-30
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 2023-02-20
    • 1970-01-01
    相关资源
    最近更新 更多