可重用的XAML碎片作为矢量图像

9

我想在一些WPF应用程序/库中将一些XAML片段重复使用为图像。

问题的背景如下:

在WPF应用程序中,重用位图图像很容易。可以将图像添加为资源,我可以在许多XAML中使用<Image Source="packURI"/>,这样图像将是相同的。

但我希望有可能对矢量图像执行相同操作。图像本身可以表示为Path,但我无法将相同的Path重用为资源,因为禁止仅仅在几个不同地方(可能来自几个UI线程)使用它(UI元素只能有一个逻辑父级)。

此外,如果我想从几个Path构建“图像”,则问题变得更加复杂,需要使用Canvas。或者使用任意的XAML代码。

我尝试使用PathStyle,使图像以以下方式表示:

<Path Style={StaticResource VectorImage1}/>

这似乎是一种可重用的方式,但我担心有两个问题:

  1. 如果矢量图像的实现从Path更改为(例如)Canvas,我不仅需要在样式中替换它,还需要在使用它的源代码中到处替换。
  2. 使用样式定义路径似乎太冗长。
  3. 我看不到将此方法泛化以使用Canvas或任意XAML代码的方法。
  4. 语法似乎相当不自然。

还有另一种通过定义UserControl来获得可重用XAML片段的方法,但为每个矢量图像定义单独的用户控件似乎过度了。

是否有更好、更好的方法来定义可重用的XAML片段呢?

3个回答

15

你可以向路径资源添加x:Shared属性并将其用作静态资源。如果"MyVectorImage"更改为其他内容,则仍将起作用。

更新
可能最好使用ContentControl或类似控件,以便添加属性,例如边距等。

<Window.Resources>
    <Path x:Key="MyVectorImage"
          x:Shared="False"
          Stroke="DarkGoldenRod"
          StrokeThickness="3"
          Data="M 10,20 C 10,25 40,35 40,17 H 28"
          Stretch="Fill"
          Width="100"
          Height="40"/>
</Window.Resources>
<StackPanel>
    <ContentControl Margin="10" Content="{StaticResource MyVectorImage}"/>
    <ContentControl Margin="10" Content="{StaticResource MyVectorImage}"/>
</StackPanel>

例如,您可以用包含两个路径的StackPanel替换"MyVectorImage"。

<Window.Resources>
    <StackPanel x:Key="MyVectorImage"
                x:Shared="False">
        <Path Stroke="DarkGoldenRod"
              StrokeThickness="3"
              Data="M 10,20 C 10,25 40,35 40,17 H 28"
              Stretch="Fill"
              Width="100"
              Height="40"/>
        <Path Stroke="DarkGoldenRod"
              StrokeThickness="3"
              Data="M 10,20 C 10,25 40,35 40,17 H 28"
              Stretch="Fill"
              Width="100"
              Height="40"/>
    </StackPanel>
</Window.Resources>

1
我尝试使用您的建议,但在运行时出现异常:“命名空间'http://schemas.microsoft.com/winfx/2006/xaml'中的共享属性只能在已编译的资源字典中使用。” 我将MyVectorImage移动到单独的XAML资源字典中,但是当我将该字典包含到窗口的MergedDictionaries中时,仍然会收到相同的错误消息。 - Vlad
@Vlad@ 我更新了我的示例。如果您尝试使用 StackPanel 示例,我忘记将 x:Shared 属性移动到 StackPanel 中。 - Fredrik Hedblad
我明白了,我的错,我应该尝试理解并阅读消息的确切内容。只有在顶层使用x:Shared时它才起作用。 - Vlad
@Vlad:是的,当我尝试运行它时,我也遇到了相同的异常。在设计器中它显示得很好,所以我没有费心去实际尝试运行它 :) - Fredrik Hedblad
很棒,这对我非常有用。现在我可以将所有的矢量图形保存在单独的资源字典中! - EdwardM

5
经过一些调研,还有一种选项:使用DrawingImage作为图像的Source。常规的图像来源是BitmapSource,但它也可以是“矢量图形”。
这里有一个示例
<Image>
  <Image.Source>
    <DrawingImage PresentationOptions:Freeze="True">
      <DrawingImage.Drawing>
        <GeometryDrawing>
          <GeometryDrawing.Geometry>
            <GeometryGroup>
              <EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" />
              <EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" />
            </GeometryGroup>
          </GeometryDrawing.Geometry>
          <GeometryDrawing.Brush>
            <LinearGradientBrush>
              <GradientStop Offset="0.0" Color="Blue" />
              <GradientStop Offset="1.0" Color="#CCCCFF" />
            </LinearGradientBrush>
          </GeometryDrawing.Brush>
          <GeometryDrawing.Pen>
            <Pen Thickness="10" Brush="Black" />
          </GeometryDrawing.Pen>
        </GeometryDrawing>
      </DrawingImage.Drawing>
    </DrawingImage>
  </Image.Source>
</Image>

生成这样一个漂亮的矢量图像:

来自上述源的矢量图像

另一种选择可能是使用DrawingBrush,就像在这个SO问题中所示:如何在XAML/WPF中存储和检索多个形状?


1
我更喜欢这种方法,但我认为答案会受益于一些内联代码示例。使用DrawingImage允许您将多个矢量图形(和多个颜色)组合成单个的、连贯的图标,通过使用<DrawingGroup/>元素作为DrawingImage的内容属性。它还允许您在UI代码中始终使用<Image/>元素来表示您的图标,无论您是引用矢量图像还是位图图像;"Source"属性将引用DrawingImage矢量资源或BitmapSource图像资源。 - BTownTKD
@BTownTKD:你说得对,我会尝试构建一些例子。 - Vlad
示例链接已经失效且无法恢复。(该成员的MSDN内容没有示例。) 有人在这个网站上有可以分享的内容吗? - ygoe
1
@LonelyPixel:我已经更正了链接并嵌入了示例。 - Vlad

3

这对任意的 WPF 片段都适用吗?例如,带有子元素的 Canvas - Vlad
非常感谢,您的建议基本上与被接受的答案相同。 - Vlad
谢谢你解释 shared=false 的作用,它解决了我的问题。 - EdwardM

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