如何在 MouseOver 上交换 SVG 填充颜色?

How to swap SVG Fill Color on MouseOver?

我正在使用 SharpVectors 在我的 XAML 中嵌入 svg 文件。

这是我的 Button ControlTemplate:

<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
        BorderBrush="{TemplateBinding BorderBrush}"
        Padding="{TemplateBinding Padding}"
        Background="{TemplateBinding Background}"
        SnapsToDevicePixels="True">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <svgc:SvgViewbox x:Name="svgIcon" Grid.Column="0" Margin="{TemplateBinding Padding}" Height="20" Width="20" Stretch="Uniform" />
        <customUserControl:ExtendedBinding Grid.Column="0"
                                           Source="{Binding ElementName=svgIcon,Path=Source,Mode=TwoWay}"
                                           Target="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
        <ContentPresenter x:Name="contentPresenter"
                          Grid.Column="1"
                          Focusable="False"
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                          Margin="{TemplateBinding Padding}"
                          RecognizesAccessKey="True"
                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
    </Grid>
</Border>
</ControlTemplate>

下面是创建按钮的示例:

<Button Content="Text" Tag="../../../../Assets/Icons/cross.svg" Click="OnClick"/>

我得到的是一个 SVG 图标,按钮内有一个文本。

现在我想在 MouseOver 上更改我的 SVG 的背景颜色(要求 here 的行为完全相同,区别在于我没有 "Fill"在我的 svgc:SvgViewbox.

中标记

有办法解决我的问题吗?

更新: 我的 SVG 文件是标准的 SVG,像这样:

<g>
    <g>
        <path d="M311.517,14.053c-99.265,0-180.024,80.66-180.024,179.805c0,41.538,14.182,79.827,37.955,110.303L0,451.176l22.889,26.313    l170.577-147.994c31.628,27.496,72.926,44.168,118.051,44.168c99.266,0,180.025-80.661,180.025-179.805    C491.542,94.713,410.784,14.053,311.517,14.053z M311.517,353.663c-88.237,0-160.024-71.688-160.024-159.805    S223.279,34.053,311.517,34.053s160.025,71.688,160.025,159.805S399.755,353.663,311.517,353.663z" fill="#FFFFFF"/>
        <polygon points="322.447,122.812 300.587,122.812 300.587,182.928 240.471,182.928 240.471,204.788 300.587,204.788     300.587,264.904 322.447,264.904 322.447,204.788 382.563,204.788 382.563,182.928 322.447,182.928   " fill="#000000"/>
    </g>
</g>

您的 svg XAML 中必须有多个 GeomtryDrawing ,如下所示:

<DrawingGroup>
   <GeometryDrawing Brush="#FF71BF22">
        <GeometryDrawing.Geometry>
          <PathGeometry FillRule="Nonzero" Figures="M546.394,327.725L536.856,272.111 507.584,214.272C507.584,214.272,493.855,193.075,471.075,197.47L498.963,334.022 546.394,327.725z" />
        </GeometryDrawing.Geometry>
   </GeometryDrawing>

在您的代码中搜索所有此类 Brush 并更改它们的值,从而创建您的 SVG XAML 的两个副本,然后使用 Trigger 将它们交换到 MouseOver

编辑 #1(基于 OP 的第一条评论)

您可以使用 AttachedProperty 来处理 PathMouseEnterMouseLeave 事件。此 AP 允许您设置透明度级别。 您的 Path 必须低于 Grid.

public static byte GetSvgProp(DependencyObject obj)
{
    return (byte)obj.GetValue(SvgPropProperty);
}

public static void SetSvgProp(DependencyObject obj, byte value)
{
    obj.SetValue(SvgPropProperty, value);
}

// Using a DependencyProperty as the backing store for SvgProp.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty SvgPropProperty =
    DependencyProperty.RegisterAttached("SvgProp", typeof(byte), typeof(Window2), new PropertyMetadata((byte)0xFF, new PropertyChangedCallback(SvgPropChanged)));

private static void SvgPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{       
    ((Grid)d).Loaded += (s, args) => {

        var paths = ((Grid)s).Children.OfType<Path>();
        foreach (Path p in paths)
        {
            p.MouseEnter += (s1, args1) => {
                Path p1 = (Path)s1;
                p1.Tag = p1.Fill; // save old value
                var c = ((SolidColorBrush)p1.Fill).Color;
                Color newColor = new Color() { A=(byte)e.NewValue, R = c.R, G = c.G, B = c.B };
                p1.Fill = new SolidColorBrush() { Color = newColor };
            };
            p.MouseLeave += (s2, args2) => {
                Path p2 = (Path)s2;
                //p2.Fill.Opacity = 1.0;
                p2.Fill = (Brush)p2.Tag; // revert back to old value
            };
        }

    };
}

假设您的 SVG 代码在 Grid 中:

<Grid local:Window2.SvgProp="0x23">
            <Path Fill="Red" Data="M311.517,14.053c-99.265,0-180.024,80.66-180.024,179.805c0,41.538,14.182,79.827,37.955,110.303L0,451.176l22.889,26.313    l170.577-147.994c31.628,27.496,72.926,44.168,118.051,44.168c99.266,0,180.025-80.661,180.025-179.805    C491.542,94.713,410.784,14.053,311.517,14.053z M311.517,353.663c-88.237,0-160.024-71.688-160.024-159.805    S223.279,34.053,311.517,34.053s160.025,71.688,160.025,159.805S399.755,353.663,311.517,353.663z"/>                    
            <Polygon Points="322.447,122.812 300.587,122.812 300.587,182.928 240.471,182.928 240.471,204.788 300.587,204.788     300.587,264.904 322.447,264.904 322.447,204.788 382.563,204.788 382.563,182.928 322.447,182.928   " Fill="#000000"/>
 </Grid>

我找到了一个通过反射让它工作的解决方案。

还在 github (https://github.com/ElinamLLC/SharpVectors/issues/88) 回购中提出了一个问题来制作属性 public 并轻松访问它们。

SvgDrawingCanvas canvas = (SvgDrawingCanvas) fooSvgViewbox.Child;
List<Drawing> drawings = (List<Drawing>)typeof(SvgDrawingCanvas).GetField("_drawObjects", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(canvas);

foreach (Drawing drawing in drawings)
{
    if (drawing is GeometryDrawing geometryDrawing)
    {
        geometryDrawing.Brush = Brushes.BlueViolet;
    }
}

如果您查看 class 代码 (https://github.com/ElinamLLC/SharpVectors/blob/master/Source/SharpVectorRuntimeWpf/SvgDrawingCanvas.cs#L49),您将看到绘制对象的私有修饰符。通过反射可以访问和修改项目。现在您可以创建自己的 AttachedBehavior 或自定义绑定!