WPF中使用SVG文件作为图标的正确方式是什么?

94

有人能描述一下做这件事的推荐逐步程序吗?

步骤1:将SVG转换为XAML...那很容易

步骤2:现在该怎么做呢?


13
抱歉重新激活这篇文章,但我认为这些信息非常有价值:SVG基本上与WPF Path一一对应。 因此,除了一些表面的标记调整外,您应该能够将SVG直接引入您的WPF应用程序中。 最多可能需要将Path托管到Canvas中,但在我看来就是这样。 - code4life
10个回答

181

你的技术取决于SVG到XAML转换器生成的XAML对象是什么。它会产生绘图、图像、网格、画布、路径或几何图形吗?在每种情况下,你的技术都会有所不同。

在下面的示例中,我将假设你正在使用一个按钮来显示你的图标,这是最常见的场景,但请注意,相同的技术也适用于任何ContentControl。

使用绘图作为图标

要使用绘图,可以用DrawingBrush绘制一个适当大小的矩形:

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

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

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

使用图片作为图标

可以直接使用图片:

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

使用网格作为图标

可以直接使用网格:

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

如果你需要控制大小,你可以把它包含在一个Viewbox中:

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

使用Canvas作为图标

这就像使用图像或网格一样,但由于画布没有固定的大小,您需要指定高度和宽度(除非这些已经被SVG转换器设置):

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

如何使用路径作为图标

您可以使用路径,但必须明确设置描边或填充:

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

如何将几何图形用作图标

您可以使用路径(Path)绘制您的几何图形。如果需要描边,请设置 Stroke 属性:

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

或者如果它应该被填充,设置 Fill:

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

如何进行数据绑定

如果您正在使用代码进行SVG -> XAML转换,并希望生成的XAML使用数据绑定,请使用以下之一:

绑定绘图:

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

绑定图像:

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

绑定网格:

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

在Viewbox中绑定网格:

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

绑定画布:

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

绑定路径:

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

绑定几何形状:

<Button>
  <Path Width="100" Height="100" Data="{Binding Geometry}" />
</Button>

13
在我的情况下,我有一个画布,因此我认为以下适用:<Button> <Canvas Height="100" Width="100"> <!--Converted from SVG, with additions --> </Canvas> </Button> 但是我该如何重复使用它呢?我不能为每个我想在其中使用SVG图像的按钮都进行复制/粘贴。我希望将其定义为字典中的资源,并使用Static/DynamicResource。 - NVM
4
在UI中,您不能在多个位置使用单个画布(Canvas),因为一个可视对象(Visual)只能有一个父级。因此,您通常会使用模板(template)来解决这个问题。模板允许您在需要的每个位置创建Canvas的单独实例: <ResourceDictionary><DataTemplate x:Key="MyIconTemplate"><Canvas ... /></DataTemplate></ResourceDictionary> ... <Button><ContentPresenter ContentTemplate="{StaticResource MyIconTemplate}" /></Button>. - Ray Burns
1
一种更高效但更复杂的方法是使用VisualBrush创建Canvas的图片来进行绘制:<ResourceDictionary><VisualBrush x:Key="MyBrush"><VisualBrush.Visual><Canvas x:Key="MyCanvas" ... /></VisualBrush.Visual></VisualBrush></ResourceDictionary> ... <Button><Rectangle Fill="{StaticResource MyBrush}" Width="..." Height="..." /></Button>。这种方法更难,因为您还必须确保Canvas被测量/排列,并且必须硬编码Width/Height(或者为矩形使用模板)。 - Ray Burns
1
啊哈!我知道不能直接两次使用同一画布,这就是为什么答案还没有被接受的原因。非常感谢。 - NVM
1
所以基本上,对于这些元素中的每一个 - 例如Canvas对象,我应该把SVG放在哪里?这很重要,因为我的SVG都在静态资源中,我需要将它们嵌入... - code4life
很棒的例子。谢谢。 - Maciej S.

64

安装SharpVectors

Install-Package SharpVectors

在XAML中添加以下内容

<UserControl xmlns:svgc="http://sharpvectors.codeplex.com/svgc">
    <svgc:SvgViewbox Source="/Icons/icon.svg"/>
</UserControl>

7
在应用程序中嵌入XAML图像还是使用这种方法更好? - MyOwnWay
对我来说非常有效...使用NuGet和所有其他工具。我必须正确地使用颜色信息创建SVG,但一旦解决了这个问题,它们就不会显示为黑色,而是看起来很好。 - kfn
1
这是如何使用sharpvectors绑定到Source AttachedProperty的方法:https://dev59.com/2ZPfa4cB1Zd3GeqPDXSi#35088090 - CodingYourLife
1
2020年更新:有一个新版本使用SharpVectors.Reloaded。 - Andy Braham
1
2020年11月:1.7.0版本在SvgViewbox的Source属性上无论内容如何都会抛出NullReference异常。降级到1.6.0(SharpVectors.Reloaded)后,问题得以解决。 - Volodymyr Kotylo

6

Windows 10 build 15063 "Creators Update"原生支持SVG图像(尽管存在一些注意事项),可用于针对Windows 10的UWP/UAP应用程序。

如果您的应用程序是WPF应用程序而不是UWP/UAP,则仍然可以使用此API(需要跳过很多障碍):Windows 10 build 17763“2018年10月更新”引入了XAML岛的概念(作为“预览”技术,但我相信允许在应用商店中使用;在所有情况下,随着Windows 10 build 18362“2019年5月更新”,XAML岛不再是预览功能并得到完全支持),允许您在WPF应用程序中使用UWP API和控件。

你需要首先添加对WinRT API的引用。如果要使用与用户数据或系统交互的某些Windows 10 API(例如,在Windows 10 UWP webview中从磁盘加载图像或使用toast通知API显示toast),您还需要将WPF应用程序与包标识相关联,如此处所示(在Visual Studio 2019中更加简便)。虽然这不是使用Windows.UI.Xaml.Media.Imaging.SvgImageSource类所必需的。
使用方法(如果您在UWP上或者按照上述说明在WPF下添加了XAML岛支持)非常简单,只需将<Image />Source设置为SVG路径即可。这相当于使用SvgImageSource,如下所示:
<Image>
    <Image.Source>
        <SvgImageSource UriSource="Assets/svg/icon.svg" />
    </Image.Source>
</Image>

然而,以这种方式(通过XAML)加载的SVG图像可能会出现锯齿/走样。一种解决方法是指定RasterizePixelHeightRasterizePixelWidth值,其值为您实际高度/宽度的两倍以上:
<SvgImageSource RasterizePixelHeight="300" RasterizePixelWidth="300" UriSource="Assets/svg/icon.svg" /> <!-- presuming actual height or width is under 150 -->

这可以通过在基本图像的 ImageOpened 事件中创建新的 SvgImageSource 来动态解决:
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;

这里是尝试说明锯齿效应的结果,对于非高dpi屏幕可能难以看到。

这是上面代码的结果:一个使用初始SvgImageSourceImage,以及下面的第二个Image,它使用在ImageOpened事件中创建的SvgImageSource:

enter image description here

这是顶部图像的放大视图:

enter image description here

这是底部图像的放大视图(抗锯齿,正确):

enter image description here

你需要在新标签页中打开图片并查看全尺寸才能体会差异。


19
SvgImageSource 是一个 UWP 库,而不是 WPF,遗憾的是。 - adam_0
6
这是UWP而不是WPF;您混淆了XAML。 - juFo

6

选项1:使用“SharpVectors”nuget包直接使用SVG图标

  1. SharpVectors nuget包添加到项目中。
  2. 将SVG文件添加到项目中,在“Icons”子文件夹中设置它们的生成操作属性为资源
  3. 在代码中使用它:
<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>

选项2:使用"SvgToXaml"工具将SVG转换为XAML

  1. SvgToXaml。下载最新版本(本回答是使用"Ver_1.3.0"测试的)
  2. 将所有SVG图标放入一个文件夹中,然后执行以下命令:
SvgToXaml.exe BuildDict /inputdir "c:\Icons" /outputdir "c:\MyWpfApp" /outputname IconsDictionary
  1. 将生成的 IconsDictionary.xaml 文件添加到您的项目中并在您的代码中使用它:
<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>

选项3:对于一些已生成的XAML文件,使用IValueConverter

如果你已经有了一些生成的XAML文件并想要使用它们,在某些类型的文件中可以创建自定义ValueConverter类。请参考以下答案获取更多信息:


我非常喜欢第二个选项。非常感谢! - Willy

5

经过多次搜索和尝试,我成功找到了不需要使用外部库的方法。首先,您需要使用Inkscape打开SVG文件进行准备,然后按照以下列表中的步骤进行操作:

  • 使用Inkscape打开SVG文件;
  • 按下Ctrl + A来选择所有内容;
  • 转到编辑 > 调整页面大小以适应选定内容;
  • 按下Ctrl + C;
  • 按下Ctrl + S,然后关闭Inkscape;
  • 在SVG文件编辑器中打开SVG文件,然后转到<path>,您可以查看到多个路径。这是一个示例:
<path d="..." fill="..." id="path2"/>
<path d="..." fill="..." id="path4"/>
<path d="..." fill="..." id="path6"/>

在您的XAML文件中,您需要创建一个ViewBox元素,然后插入一个Grid元素,然后为SVG文件中看到的路径次数插入Path元素。
<Viewbox Stretch="Fill">
    <Grid>
        <Path Fill="..." Data="..."/>
        <Path Fill="..." Data="..."/>
        <Path Fill="..." Data="..."/>
    </Grid>
</Viewbox>

在您的XAML中的Fill属性中,您需要插入SVG文件中的fill属性,在您的XAML中的Data属性中,您需要插入SVG文件中的d属性。您应该得到这样的结果: enter image description here

2
您可以将SVG生成的XAML作为矩形上的绘图刷。类似这样:

<Rectangle>
   <Rectangle.Fill>
      --- insert the converted xaml's geometry here ---
   </Rectangle.Fill>
</Rectangle>

3
和第一个答案一样的问题。我不想每次使用相同的SVG文件时都复制粘贴。 - NVM
也不谈论转换以及它是如何完成的。 - Martin Braun

1

1

另一个选择是dotnetprojects SVGImage

这允许在xaml中直接使用.svg文件。

好处是,它只有一个约100k的程序集。与sharpvectors相比,后者更大且包含许多文件。

用法:

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

就是这样。

请看:


我想使用这个解决方案,但它显示“无法定位资源”,即使将SVG图标的构建操作设置为资源。 - Martin Braun

1

我们可以直接从SVG代码中使用路径代码:

    <Path>
        <Path.Data>
            <PathGeometry Figures="M52.8,105l-1.9,4.1c ... 

这个可行。我只是将 path 更改为 Path,将 d 更改为 Data。可能有很多不同的元素我不知道如何移植,但一个简单的标志可以像这样使用。 - Boppity Bop

0

我发现这个教程非常有帮助: https://msadowski.github.io/WPF-vector-graphics-tutorial/

  1. 从github下载程序的zip文件。
  2. 使用程序将SVG转换为XAML。
  3. 将.xaml文件复制/粘贴到您项目中所选的文件夹中。
  4. 在App.xaml中添加应用程序资源以供您的文件使用。
  5. 在您的xaml页面中使用Source="{StaticResource }"引用您的矢量图像

该教程通过示例详细解释了所有步骤。我已经尝试过了,我的svg图像现在在我的应用程序中显示得很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接