简体   繁体   中英

Vector image as reusable XAML fragment

I want to reuse some XAML fragment as image in some WPF application/library.

The background of the problem is following:

It's easy to reuse a bitmap image in a WPF application. The image can be added as a resource, I can use <Image Source="packURI"/> at many places in XAML so the image will be the same.

But I'd like to have a possibility to do the same for a vector image. The image itself can be represented as Path , but I cannot reuse the same Path as a resource, because simply using it at several different places (and possibly from several UI threads) is prohibited (UI element can have only one logical parent).

Moreover, the question gets more complicated if I'd like to build the "image" from several Path s, use a Canvas for it. Or some arbitrary XAML code.

I tried using a Style for the Path , so the image is represented in such a way:

<Path Style={StaticResource VectorImage1}/>

This seems to be a reusable way, but I am concerned with two problems:

  1. If the implementation of a vector image is changed from Path to a (for instance) Canvas , I'll need to replace it not only in the style, but everywhere in the source code which uses it.
  2. Definition of a path using a style seems to be too verbose.
  3. I see no way to generalize this approach for using Canvas or arbitrary XAML code.
  4. The syntax seems to be quite unnatural.

There is other way to have a reusable XAML fragment, through defining a UserControl , but defining a separate user control for each vector image seems to be an overkill.

Is there a better, nice, right way to define a reusable XAML fragment?

You can add the x:Shared attribute to the Path Resource and use it as a StaticResource. This will work if "MyVectorImage" changes to something else

Update
Probably better to use a ContentControl or similar to be able to add Properties, such as Margin etc.

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

Example. You replace "MyVectorImage" with a StackPanel containing two Paths.

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

After some research, there is one more option: using a DrawingImage as Source for an image. The customary image source is a BitmapSource , however it can be "vector graphics" as well.

Here is an example :

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

produces such a nice vector image:

来自上面来源的矢量图像

Yet another option might be using a DrawingBrush , like in this SO question: How to store and retrieve multiple shapes in XAML/WPF? .

You can store the path in a resource dictionary and set x:Shared to false:

<Path x:Key="CrossPath"
      x:Shared="false"
      ...
      />

This will tell WPF to create a new instance every time it is requested. http://msdn.microsoft.com/en-us/library/aa970778.aspx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM