在 Aero 上的 WPF 中覆盖按钮背景

发布于 2024-10-07 22:31:25 字数 7527 浏览 1 评论 0原文

所以,愿望很简单,将按钮的背景更改为浅绿色,当鼠标光标悬停在其上时将其背景更改为绿色,当按下按钮时将其背景更改为深绿色。以下 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 镀铬效果。不想放弃,我尝试了以下放荡的行为:

...
    <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 可以工作,但在按钮的正常状态(没有鼠标悬停或按下)期间没有浅绿色背景,因为我已经删除了按钮的背景属性,以应用其他两个状态并使其可见。现在,在这个疯狂的 XAML 无法为我们提供正常按钮状态的浅绿色背景之后,我修改了样式以将背景颜色也放入其中:

  <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 主题,才能改变按钮在某些不同状态下的背景颜色(正常、悬停、按下)?也许,如果我不必将整个 dang 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 有任何建议,而不重复 aero xaml 批发中的整个 chrome 定义,我会洗耳恭听。

So, the desire is simple, change a button's background to LightGreen, Green when the mouse cursor hovers over it and DarkGreen when it is pressed. The following XAML is my first attempt:

<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>

But, alas this doesn't work. We're only 1/3 of the way towards the goal. Yes, the button becomes light green, but as soon as you hover over it or press it you get standard Aero chrome for the respective button states. Not wanting to give up, I attempt the following debauchery:

...
    <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>
...

Now we've got the whole friggin' control template thrown in there lifted from Aero.NormalColor.xaml. Alas, this changes nothing. I then go about removing two attributes (RenderPressed and RenderMouseOver) from the Microsoft_Windows_Themes:ButtonChrome element. Still, there is no change. I then remove the setting of the Button's Background attribute, leaving just the namespace declaration for the PresentationFramework.Aero assembly:

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

Finally, we have progress. We've gone from 1/3 of the requirements to 2/3 of the way, with IsMouseOver and IsPressed working, but no light green background during the button's normal state (of not being moused over or pressed), since I've removed the Background property from the button, to get the other two states to apply and be visible. Now, after this maniacal XAML fails to get us the LightGreen background for the normal button state, I modify the style to throw the background color in there as well:

  <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>

Finally, it works as intended.

Am I crazy, or are these (and more) the hoops you have to go through with an Aero theme to just change a button's background color in some of its various states (normal, hovered over, pressed)? Maybe, life wouldn't be so bad if I didn't have to include the whole dang ButtonTemplate in there just to remove the two attributes (RenderMouseOver and RenderPressed) from it.

Thank you for any help - I am at wit's end.

Update: But wait there's more to this. Instead of removing the RenderMouseOver and RenderPressed attributes from the control template, simply changing them from:

<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}" ...

..., also fixes the problem (of ignoring the button's style triggers). I think the root of the issue is that the template bindings are applied at compile time (for performance reasons), while regular bindings are applied at run time. Because of this, the bindings from the style triggers from the individual buttons are not used if attributes use template bindings (i.e., the IsMouseOver and IsPressed from the original template are used instead).

Having said all of the above, here is the canonical example for changing a button's background in Aero while keeping its original control template:

<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>

This is of course assuming that only one button is so styled, otherwise the template and style can be moved out into a resources section somewhere. Also, there is no trigger for the default state of a button, but it is easily added to the above example (don't forget to change the RenderDefaulted attribute in the control template to use Binding instead of TemplateBinding).

If anyone has any suggestions on how to override the TemplateBinding with Binding for only the two attributes in the control template that need to change, without repeating the entire chrome definition from the aero xaml wholesale, I am all ears.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

栩栩如生 2024-10-14 22:31:25

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

The default template for Button uses a ButtonChrome, which performs its own drawing. If you want to get completely rid of the default appearance, you need to define your own template, without the ButtonChrome. I know it sounds tedious, but you only have to do it once: you can put your custom style in a resource dictionary, and just refer to it with StaticResource. Note that you can also apply the style to all buttons in your app by using {x:Type Button} as the style's key (x:Key attribute)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文