Search code examples
wpfsvgbindingfillsharpvectors

How to swap SVG Fill Color on MouseOver?


I'm using SharpVectors to embed svg file in my XAML.

Here is my 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>

And here is an example of creating a button:

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

What I obtain is a SVG icon with a text inside my button.

Now I'd like to change the background color of my SVG on MouseOver (exactly the same behaviour asked here, the difference is that I do not have a "Fill" tag in my svgc:SvgViewbox.

Is there a way to solve my problem?

Update: My SVG file is a standard SVG, like this:

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

Solution

  • There must be multiple GeomtryDrawing 's in your svg XAML like below :

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

    Search for all such Brush in your code and change their values, thus creating two copies of your SVG XAML, then using Trigger swap these on MouseOver.

    EDIT #1 (based on OP's 1st comment)

    You can use an AttachedProperty to handle MouseEnter and MouseLeave events for your Path. This AP allows you to set the transparency level. Your Path must be under 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
                };
            }
    
        };
    }
    

    Assuming your SVG code is in a 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>