ChatGPT解决这个技术问题 Extra ChatGPT

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

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

Step1. Convert SVG to XAML... thats easy

Step2. Now what?

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.

R
Ray Burns

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>

+10 simply taking the time for examples to all cases In my case I have a canvas so I assume following applies 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.
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>.
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).
Aha!!! I knew I couldn't use the same canvas twice directly which is why the answer was not yet accepted. Many thanks.
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...
I
Igor Popov

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>

Is it better to embed the XAML Imges on the app or to use this approach?
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.
Here is how you can bind to an Source AttachedProperty with sharpvectors: stackoverflow.com/a/35088090/828184
Update for 2020 There is a new version use SharpVectors.Reloaded
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.
M
Mahmoud Al-Qudsi

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:

https://i.stack.imgur.com/W8ZOr.png

This is a blown up view of the top image:

https://i.stack.imgur.com/WKfua.png

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

https://i.stack.imgur.com/97eiG.png

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


SvgImageSource is a UWP library, not WPF, sadly.
This is UWP not WPF; You are confusing XAML
M
Marco Concas

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

https://i.stack.imgur.com/L5yAM.png


P
PolarBear

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

Add SharpVectors nuget package to your project. Add SVG files to your project, for example, in a "Icons" subfolder and set their Build Action property to Resource 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

SvgToXaml. Download the latest release (this answer was tested with the "Ver_1.3.0") Place all your SVG icons into a folder and execute the following command:

SvgToXaml.exe BuildDict /inputdir "c:\Icons" /outputdir "c:\MyWpfApp" /outputname IconsDictionary

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:

Option 2: Use .xaml icon files directly

https://stackoverflow.com/a/21588195/7585517


G
Gus Cavalcanti

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>

Same problem as in first answer. I dont want to copy paste everytime I want to use the same svg.
Also not talking about the conversion and how it's done.
P
PaulusHub

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


S
Soleil

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

M
Markus

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:

https://www.nuget.org/packages/DotNetProjects.SVGImage/

https://github.com/dotnetprojects/SVGImage


I want to use this solution, but it says "Cannot locate resource", even when setting the build action of the SVG icon to Resource.