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:
- If the implementation of a vector image is changed from
Path
to a (for in开发者_高级运维stance)Canvas
, I'll need to replace it not only in the style, but everywhere in the source code which uses it. - Definition of a path using a style seems to be too verbose.
- I see no way to generalize this approach for using
Canvas
or arbitrary XAML code. - 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
精彩评论