You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF按钮鼠标悬停时修改DrawingImage画笔颜色的问题

问题描述

我在WPF视图中使用如下按钮:

MyView.xaml:

<Button Grid.Column="1"
        Style="{StaticResource HoverOnCloseButton}"
        Background="Transparent"
        BorderBrush="Transparent"
        Command="{Binding CancelBtnCommand}">
    <Button.Content>
        <Image Width="10"
               Height="10"
               MinWidth="10"
               MinHeight="10"
               Stretch="Uniform"
               UseLayoutRounding="True"
               SnapsToDevicePixels="True"
               RenderOptions.BitmapScalingMode="HighQuality"
               Source="{Binding MySettings.CrossIcon, UpdateSourceTrigger=PropertyChanged}"/>
    </Button.Content>
</Button>

按钮内容的图片加载自实现INotifyPropertyChanged接口的MySettings类的CrossIcon属性:

public class MySettings: INotifyPropertyChanged
{
    public ImageSource CrossIcon { get; private set; }

    public ResourceDictionary ResourceDict { get; set; }

    public MySettings()
    {
        InitializeDictionary();
        this.CrossIcon = ResourceDict["crossDrawingImage"] as DrawingImage;
    }

    private void InitializeDictionary()
    {
            String url = String.Format("pack://application:,,,/myApp;component/Resources/Themes/Dark/MyDictionary.xaml");

            // Load resource dictionary
            ResourceDict = new ResourceDictionary
            {
                Source = new Uri(url, UriKind.RelativeOrAbsolute)
            };

    }
}

crossDrawingImage在MyDictionary.xaml中定义如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:core="clr-namespace:System;assembly=mscorlib">

    <DrawingImage x:Key="crossDrawingImage">
        <DrawingImage.Drawing>
            <DrawingGroup ClipGeometry="M0,0 V10 H10 V0 H0 Z">
                <GeometryDrawing Brush="#FF000000" Geometry="F0 M10,10z M0,0z M4.33959,5L0,0.660409 0.660409,0 5,4.33959 9.33959,0 10,0.660409 5.66041,5 10,9.33959 9.33959,10 5,5.66041 0.660409,10 0,9.33959 4.33959,5z" />
            </DrawingGroup>
        </DrawingImage.Drawing>
    </DrawingImage>
</ResourceDictionary>

我想通过按钮绑定的HoverOnCloseButton样式,利用IsMouseOver触发器修改DrawingImage的画笔颜色(原本是#FF000000),于是写了如下样式:

<Style x:Key="HoverOnCloseButton" TargetType="{x:Type Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border Background="{TemplateBinding Panel.Background}"
                        BorderBrush="Transparent"  
                        Name="border"
                        SnapsToDevicePixels="True">
                    <ContentPresenter Name="contentPresenter"
                                      HorizontalAlignment="Center" 
                                      VerticalAlignment="Center"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="UIElement.IsMouseOver" Value="True">
                        <Setter Property="Panel.Background" TargetName="border" Value="Red"/>
                        <Setter Property="TextElement.Foreground"
                                TargetName="contentPresenter">
                            <Setter.Value>
                                <SolidColorBrush>#FFFFFF</SolidColorBrush>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

但设置TextElement.Foreground的Setter完全没效果。我还试过替换整个DrawingImage:在资源字典里创建用白色画笔的crossOnMouseHoverDrawingImage,在MySettings里加对应属性,修改样式触发器设置ContentSource,但还是没生效。请问我哪里错了?怎么实现按钮鼠标悬停时修改DrawingImage的画笔颜色?


解决方案

为什么之前的方法失效?

  1. TextElement.Foreground不作用于DrawingImage:TextElement.Foreground是针对文本元素的属性,你的按钮内容是Image控件,绑定的DrawingImage里的Brush是硬编码在资源中的静态值,不会继承Foreground属性,所以这个Setter根本影响不到画笔颜色。
  2. 替换DrawingImage的方案错误:你修改样式触发器设置ContentSource的方式不对,ContentSource不是用来直接切换Content的属性,而且如果Image的Source是绑定到ViewModel的属性,触发器无法直接修改这个绑定值。

可行实现方法

方法一:将画笔提取为资源,通过触发器修改画笔

  1. 先修改资源字典,把画笔提取为可复用的资源:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!-- 定义默认和悬停的画笔 -->
    <SolidColorBrush x:Key="CrossIconDefaultBrush" Color="#FF000000"/>
    <SolidColorBrush x:Key="CrossIconHoverBrush" Color="#FFFFFF"/>

    <DrawingImage x:Key="crossDrawingImage">
        <DrawingImage.Drawing>
            <DrawingGroup ClipGeometry="M0,0 V10 H10 V0 H0 Z">
                <!-- 绑定画笔到动态资源 -->
                <GeometryDrawing Brush="{DynamicResource CrossIconDefaultBrush}" 
                                 Geometry="F0 M10,10z M0,0z M4.33959,5L0,0.660409 0.660409,0 5,4.33959 9.33959,0 10,0.660409 5.66041,5 10,9.33959 9.33959,10 5,5.66041 0.660409,10 0,9.33959 4.33959,5z" />
            </DrawingGroup>
        </DrawingImage.Drawing>
    </DrawingImage>
</ResourceDictionary>
  1. 修改按钮样式,在IsMouseOver触发器中替换动态资源的画笔:
<Style x:Key="HoverOnCloseButton" TargetType="{x:Type Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border x:Name="border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="Transparent"
                        SnapsToDevicePixels="True">
                    <ContentPresenter HorizontalAlignment="Center" 
                                      VerticalAlignment="Center"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="border" Property="Background" Value="Red"/>
                        <!-- 在Border级别覆盖动态资源,影响内部DrawingImage的画笔 -->
                        <Setter Property="Resources" TargetName="border">
                            <Setter.Value>
                                <ResourceDictionary>
                                    <SolidColorBrush x:Key="CrossIconDefaultBrush" Color="#FFFFFF"/>
                                </ResourceDictionary>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这种方法利用DynamicResource的动态替换特性,在按钮的Border级别覆盖资源,从而影响内部DrawingImage的画笔。

方法二:在按钮模板中直接定义两个Image,通过触发器切换可见性

这种方法不需要修改ViewModel和资源字典,直接在样式里处理:

<Style x:Key="HoverOnCloseButton" TargetType="{x:Type Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border x:Name="border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="Transparent"
                        SnapsToDevicePixels="True">
                    <!-- 默认图标 -->
                    <Image x:Name="defaultIcon"
                           Width="10" Height="10"
                           Stretch="Uniform"
                           Source="{DynamicResource crossDrawingImage}"/>
                    <!-- 悬停图标 -->
                    <Image x:Name="hoverIcon"
                           Width="10" Height="10"
                           Stretch="Uniform"
                           Visibility="Collapsed">
                        <Image.Source>
                            <DrawingImage>
                                <DrawingImage.Drawing>
                                    <DrawingGroup ClipGeometry="M0,0 V10 H10 V0 H0 Z">
                                        <GeometryDrawing Brush="#FFFFFF" 
                                                         Geometry="F0 M10,10z M0,0z M4.33959,5L0,0.660409 0.660409,0 5,4.33959 9.33959,0 10,0.660409 5.66041,5 10,9.33959 9.33959,10 5,5.66041 0.660409,10 0,9.33959 4.33959,5z" />
                                    </DrawingGroup>
                                </DrawingImage.Drawing>
                            </DrawingImage>
                        </Image.Source>
                    </Image>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="border" Property="Background" Value="Red"/>
                        <Setter TargetName="defaultIcon" Property="Visibility" Value="Collapsed"/>
                        <Setter TargetName="hoverIcon" Property="Visibility" Value="Visible"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

注意这里要把原来Button里的Image内容去掉,因为模板里已经包含了图标,按钮直接用这个样式即可。

方法三:通过ViewModel绑定切换ImageSource

如果坚持要通过ViewModel控制,可以给MySettings添加CrossHoverIcon属性,然后在按钮样式里通过触发器修改Image的Source绑定:

  1. 修改MySettings:
public class MySettings: INotifyPropertyChanged
{
    public ImageSource CrossIcon { get; private set; }
    public ImageSource CrossHoverIcon { get; private set; }

    public ResourceDictionary ResourceDict { get; set; }

    public MySettings()
    {
        InitializeDictionary();
        this.CrossIcon = ResourceDict["crossDrawingImage"] as DrawingImage;
        this.CrossHoverIcon = ResourceDict["crossOnMouseHoverDrawingImage"] as DrawingImage;
    }

    // INotifyPropertyChanged接口实现代码省略
}
  1. 修改按钮样式,用触发器修改Image的Source:
<Style x:Key="HoverOnCloseButton" TargetType="{x:Type Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border x:Name="border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="Transparent"
                        SnapsToDevicePixels="True">
                    <Image x:Name="iconImage"
                           Width="10" Height="10"
                           Stretch="Uniform"
                           Source="{Binding MySettings.CrossIcon}"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="border" Property="Background" Value="Red"/>
                        <Setter TargetName="iconImage" Property="Source" Value="{Binding MySettings.CrossHoverIcon}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

同样,按钮里不需要再定义Content,模板已经包含了Image。


内容的提问来源于stack exchange,提问作者Willy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 03:32:00