WPF What is the correct way of using SVG files as icons in WPF
Asked Answered
P

10

96

Can someone describe a recommended Step by Step procedure for doing this?

Step1. Convert SVG to XAML... thats easy

Step2. Now what?

Pyriphlegethon answered 19/8, 2010 at 21:23 Comment(1)
Sorry to resurrect this post, but I think this information has value: an SVG is essentially a one-to-one match with a WPF Path. So other than some superficial markup adjustments, you should be able to just bring the SVG right into your WPF application. At most you may have to host the Path into a Canvas, but that's about all, IMHO.Provencal
H
184

Your technique will depend on what XAML object your SVG to XAML converter produces. Does it produce a Drawing? An Image? A Grid? A Canvas? A Path? A Geometry? In each case your technique will be different.

In the examples below I will assume you are using your icon on a button, which is the most common scenario, but note that the same techniques will work for any ContentControl.

Using a Drawing as an icon

To use a Drawing, paint an approriately-sized rectangle with a DrawingBrush:

<Button>
  <Rectangle Width="100" Height="100">
    <Rectangle.Fill>
      <DrawingBrush>
        <DrawingBrush.Drawing>

          <Drawing ... /> <!-- Converted from SVG -->

        </DrawingBrush.Drawing>
      </DrawingBrush>
    </Rectangle.Fill>
  </Rectangle>
</Button>

Using an Image as an icon

An image can be used directly:

<Button>
  <Image ... />  <!-- Converted from SVG -->
</Button>

Using a Grid as an icon

A grid can be used directly:

<Button>
  <Grid ... />  <!-- Converted from SVG -->
</Button>

Or you can include it in a Viewbox if you need to control the size:

<Button>
  <Viewbox ...>
    <Grid ... />  <!-- Converted from SVG -->
  </Viewbox>
</Button>

Using a Canvas as an icon

This is like using an image or grid, but since a canvas has no fixed size you need to specify the height and width (unless these are already set by the SVG converter):

<Button>
  <Canvas Height="100" Width="100">  <!-- Converted from SVG, with additions -->
  </Canvas>
</Button>

Using a Path as an icon

You can use a Path, but you must set the stroke or fill explicitly:

<Button>
  <Path Stroke="Red" Data="..." /> <!-- Converted from SVG, with additions -->
</Button>

or

<Button>
  <Path Fill="Blue" Data="..." /> <!-- Converted from SVG, with additions -->
</Button>

Using a Geometry as an icon

You can use a Path to draw your geometry. If it should be stroked, set the Stroke:

<Button>
  <Path Stroke="Red" Width="100" Height="100">
    <Path.Data>
      <Geometry ... /> <!-- Converted from SVG -->
    </Path.Data>
  </Path>
</Button>

or if it should be filled, set the Fill:

<Button>
  <Path Fill="Blue" Width="100" Height="100">
    <Path.Data>
      <Geometry ... /> <!-- Converted from SVG -->
    </Path.Data>
  </Path>
</Button>

How to data bind

If you're doing the SVG -> XAML conversion in code and want the resulting XAML to appear using data binding, use one of the following:

Binding a Drawing:

<Button>
  <Rectangle Width="100" Height="100">
    <Rectangle.Fill>
      <DrawingBrush Drawing="{Binding Drawing, Source={StaticResource ...}}" />
    </Rectangle.Fill>
  </Rectangle>
</Button>

Binding an Image:

<Button Content="{Binding Image}" />

Binding a Grid:

<Button Content="{Binding Grid}" />

Binding a Grid in a Viewbox:

<Button>
  <Viewbox ...>
    <ContentPresenter Content="{Binding Grid}" />
  </Viewbox>
</Button>

Binding a Canvas:

<Button>
  <ContentPresenter Height="100" Width="100" Content="{Binding Canvas}" />
</Button>

Binding a Path:

<Button Content="{Binding Path}" />  <!-- Fill or stroke must be set in code unless set by the SVG converter -->

Binding a Geometry:

<Button>
  <Path Width="100" Height="100" Data="{Binding Geometry}" />
</Button>
Hardner answered 20/8, 2010 at 5:56 Comment(5)
+10 simply taking the time for examples to all cases In my case I have a canvas so I assume following applies <Button> <Canvas Height="100" Width="100"> <!-- Converted from SVG, with additions --> </Canvas> </Button> But how do I reuse this? I cant do copy/pasting for every button I want to use the svg image in. I would sort of like to define it as resources in a dictionary and use as Static/DynamicResource.Pyriphlegethon
You cannot use a single Canvas in multiple places in your UI because a Visual can only have one parent. So you would typicaly use a template for this. A template allows you to create a separate instance of the Canvas each place you need it: <ResourceDictionary><DataTemplate x:Key="MyIconTemplate"><Canvas ... /></DataTemplate></ResourceDictionary> ... <Button><ContentPresenter ContentTemplate="{StaticResource MyIconTemplate}" /></Button>.Hardner
A more efficient but more complex approach is to use a VisualBrush to create a picture of the Canvas to paint with: <ResourceDictionary><VisualBrush x:Key="MyBrush"><VisualBrush.Visual><Canvas x:Key="MyCanvas" ... /></VisualBrush.Visual></VisualBrush></ResourceDictionary> ... <Button><Rectangle Fill="{StaticResource MyBrush}" Width="..." Height="..." /></Button>. This is harder because you must also make sure the Canvas gets measured/arranged and you must hard-code the Width/Height (or use a template for the rectangle).Hardner
Aha!!! I knew I couldn't use the same canvas twice directly which is why the answer was not yet accepted. Many thanks.Pyriphlegethon
So basically, for each of those elements - e.g. the Canvas object, where do I put the SVG? This is important because my SVG's are all in static resources, and I need to embed them...Provencal
E
64

Install the SharpVectors library

Install-Package SharpVectors

Add the following in XAML

<UserControl xmlns:svgc="http://sharpvectors.codeplex.com/svgc">
    <svgc:SvgViewbox Source="/Icons/icon.svg"/>
</UserControl>
Eyesight answered 28/2, 2014 at 22:53 Comment(5)
Is it better to embed the XAML Imges on the app or to use this approach?Rodriques
Works very well for me... with nugets and all. I had to make the svg with color information correctly, but once that was solved they dont appear black, but looks nice.Method
Here is how you can bind to an Source AttachedProperty with sharpvectors: https://mcmap.net/q/219174/-how-to-bind-to-an-unbindable-property-without-violating-mvvmMoidore
Update for 2020 There is a new version use SharpVectors.ReloadedCreaky
November 2020: 1.7.0 threw NullReference exception for Source property of SvgViewbox no matter the content. Downgraded to 1.6.0 (SharpVectors.Reloaded), it works there.Selfsown
C
7

Option 1: Use SVG icons directly using "SharpVectors" nuget package

  1. Add SharpVectors nuget package to your project.
  2. Add SVG files to your project, for example, in a "Icons" subfolder and set their Build Action property to Resource
  3. Use it in your code:
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <Button Height="100">
                <svgc:SvgViewbox Source="/Icons/Checkmark_16x.svg"/>
            </Button>
            <ContentControl Height="100">
                <svgc:SvgViewbox Source="/Icons/CollapseAll_16x.svg"/>
            </ContentControl>
            <Label Height="100">
                <svgc:SvgViewbox Source="/Icons/Refresh_16x.svg"/>
            </Label>
        </StackPanel>
    </Grid>
</Window>

Option 2: Convert SVG to XAML using "SvgToXaml" tool

  1. SvgToXaml. Download the latest release (this answer was tested with the "Ver_1.3.0")
  2. Place all your SVG icons into a folder and execute the following command:
SvgToXaml.exe BuildDict /inputdir "c:\Icons" /outputdir "c:\MyWpfApp" /outputname IconsDictionary
  1. Add generated IconsDictionary.xaml file to your project and use it in your code:
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="IconsDictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Height="100">
                <Image Source="{StaticResource Refresh_16xDrawingImage}"/>
            </Button>
            <ContentControl Height="100">
                <Image Source="{StaticResource CollapseAll_16xDrawingImage}"/>
            </ContentControl>
            <Label Height="100">
                <Image Source="{StaticResource Checkmark_16xDrawingImage}"/>
            </Label>
        </StackPanel>
    </Grid>
</Window>

Option 3: Use IValueConverter for some already generated XAML files

If you already have generated XAML files and you want to use them, for some types of them it is possible to create a custom ValueConverter class. Please refer to the following answers for more information:

Callous answered 19/12, 2021 at 18:36 Comment(1)
I like very much the second option. Thanks a lot!Undertenant
S
6

Windows 10 build 15063 "Creators Update" natively supports SVG images (though with some gotchas) to UWP/UAP applications targeting Windows 10.

If your application is a WPF app rather than a UWP/UAP, you can still use this API (after jumping through quite a number of hoops): Windows 10 build 17763 "October 2018 Update" introduced the concept of XAML islands (as a "preview" technology but I believe allowed in the app store; in all cases, with Windows 10 build 18362 "May 2019 Update" XAML islands are no longer a preview feature and are fully supported) allowing you to use UWP APIs and controls in your WPF applications.

You need to first add the references to the WinRT APIs, and to use certain Windows 10 APIs that interact with user data or the system (e.g. loading images from disk in a Windows 10 UWP webview or using the toast notification API to show toasts), you also need to associate your WPF application with a package identity, as shown here (immensely easier in Visual Studio 2019). This shouldn't be necessary to use the Windows.UI.Xaml.Media.Imaging.SvgImageSource class, though.

Usage (if you're on UWP or you've followed the directions above and added XAML island support under WPF) is as simple as setting the Source for an <Image /> to the path to the SVG. That is equivalent to using SvgImageSource, as follows:

<Image>
    <Image.Source>
        <SvgImageSource UriSource="Assets/svg/icon.svg" />
    </Image.Source>
</Image>

However, SVG images loaded in this way (via XAML) may load jagged/aliased. One workaround is to specify a RasterizePixelHeight or RasterizePixelWidth value that is double+ your actual height/width:

<SvgImageSource RasterizePixelHeight="300" RasterizePixelWidth="300" UriSource="Assets/svg/icon.svg" /> <!-- presuming actual height or width is under 150 -->

This can be worked around dynamically by creating a new SvgImageSource in the ImageOpened event for the base image:

var svgSource = new SvgImageSource(new Uri("ms-appx://" + Icon));
PrayerIcon.ImageOpened += (s, e) =>
{
    var newSource = new SvgImageSource(svgSource.UriSource);
    newSource.RasterizePixelHeight = PrayerIcon.DesiredSize.Height * 2;
    newSource.RasterizePixelWidth = PrayerIcon.DesiredSize.Width * 2;
    PrayerIcon2.Source = newSource;
};
PrayerIcon.Source = svgSource;

The aliasing may be hard to see on non high-dpi screens, but here's an attempt to illustrate it.

This is the result of the code above: an Image that uses the initial SvgImageSource, and a second Image below it that uses the SvgImageSource created in the ImageOpened event:

enter image description here

This is a blown up view of the top image:

enter image description here

Whereas this is a blown-up view of the bottom (antialiased, correct) image:

enter image description here

(you'll need to open the images in a new tab and view at full size to appreciate the difference)

Steapsin answered 5/7, 2017 at 21:12 Comment(2)
SvgImageSource is a UWP library, not WPF, sadly.Elemi
This is UWP not WPF; You are confusing XAMLTham
G
5

After various searches and attempts I managed to find the method without having to use external libraries. First you will need to use Inkscape to open the SVG file to prepare, then follow the procedure according to the following list:

  • Open the SVG file with Inkscape;
  • Press Ctrl + A to select everything;
  • Go to Edit > Resize page to selection;
  • Press Ctrl + C;
  • Press Ctrl + S then close Inkscape;
  • Open the SVG file a file editor then go to <path>, you could view several paths. This is an example:
<path d="..." fill="..." id="path2"/>
<path d="..." fill="..." id="path4"/>
<path d="..." fill="..." id="path6"/>
  • In your XAML file you have to create a ViewBox element, then insert a Grid element and then Path elements for the number of times when in the SVG file see the paths:
<Viewbox Stretch="Fill">
    <Grid>
        <Path Fill="..." Data="..."/>
        <Path Fill="..." Data="..."/>
        <Path Fill="..." Data="..."/>
    </Grid>
</Viewbox>

Where in Fill property on your XAML you have to insert the fill property in the SVG file and in Data property on your XAML you have to insert the d property in the SVG file.

You should get a result like this: enter image description here

Guthrie answered 29/10, 2021 at 9:8 Comment(0)
G
2

You can use the resulting xaml from the SVG as a drawing brush on a rectangle. Something like this:

<Rectangle>
   <Rectangle.Fill>
      --- insert the converted xaml's geometry here ---
   </Rectangle.Fill>
</Rectangle>
Gallium answered 20/8, 2010 at 2:31 Comment(2)
Same problem as in first answer. I dont want to copy paste everytime I want to use the same svg.Pyriphlegethon
Also not talking about the conversion and how it's done.Hermia
A
1

Use the SvgImage or the SvgImageConverter extensions, the SvgImageConverter supports binding. See the following link for samples demonstrating both extensions.

https://github.com/ElinamLLC/SharpVectors/tree/master/TutorialSamples/ControlSamplesWpf

Aureus answered 6/7, 2019 at 3:7 Comment(0)
H
1

We can use directly the path's code from the SVG's code:

    <Path>
        <Path.Data>
            <PathGeometry Figures="M52.8,105l-1.9,4.1c ... 
Hamburg answered 18/10, 2019 at 1:53 Comment(1)
this works. I simply changed path to Path and d to Data.. there could be a lot of different elements which I dont know how to port but a simple logo can be used like thisTague
U
1

Another alternative is dotnetprojects SVGImage

This allows native use of .svg files directly in xaml.

The nice part is, it is only one assembly which is about 100k. In comparision to sharpvectors which is much bigger any many files.

Usage:

...
xmlns:svg1="clr-namespace:SVGImage.SVG;assembly=DotNetProjects.SVGImage"
...
<svg1:SVGImage Name="mySVGImage" Source="/MyDemoApp;component/Resources/MyImage.svg"/>
...

That's all.

See:

Uralic answered 15/10, 2020 at 12:58 Comment(1)
I want to use this solution, but it says "Cannot locate resource", even when setting the build action of the SVG icon to Resource.Hermia
G
0

I found this tutorial extremely helpful: https://msadowski.github.io/WPF-vector-graphics-tutorial/

  1. Download the zip file for the program from github.
  2. Use program to convert the SVG to XAML.
  3. Copy/paste the .xaml file into your folder of choice in your project.
  4. Add the application resource to App.xaml for your file.
  5. Reference your vector image in your xaml page with Source="{StaticResource }"

The tutorial explains all steps very well with an example shown. I've tried it and my svg image is showing up great in my application now.

Gurrola answered 13/2, 2023 at 20:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.