HOWTO: Create Vector-based backgrounds in WPF
I am currently developing a kiosk application for a client. The project is still in POC stage and we do not have a budget for a graphics designer yet… We decided to create a very simple background using some vector graphics!
Using the new XBOX 360 NXE interface as inspiration, I started to create this background!
Introducing Shape
WPF provides us with some basic shapes that we can use to draw vector graphics (Ellipse, Polygon, and Rectangle)
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Opacity="0.6">
<Ellipse Fill="#7FFFFFFF" Stroke="{x:Null}" Width="40" Height="40" Canvas.Left="117.666" Canvas.Top="34.852"/>
<Ellipse Fill="#4CFFFFFF" Stroke="{x:Null}" Width="132" Height="132" Canvas.Left="5.666" Canvas.Top="93.852"/>
<Ellipse Fill="#33FFFFFF" Stroke="{x:Null}" Width="196" Height="196" Canvas.Left="794.246" Canvas.Top="541.697"/>
<Ellipse Fill="#BFFFFFFF" Stroke="{x:Null}" Width="23" Height="23" Canvas.Left="964.198" Canvas.Top="716.459"/>
<Ellipse Fill="#66FFFFFF" Stroke="{x:Null}" Width="23" Height="23" Canvas.Left="987.198" Canvas.Top="702.459"/>
<Ellipse Fill="#BFFFFFFF" Stroke="{x:Null}" Width="122" Height="122" Canvas.Left="964.198" Canvas.Top="478.697"/>
<Ellipse Fill="{x:Null}" Stroke="#7FFFFFFF" Width="100" Height="100" Canvas.Left="87.666" Canvas.Top="4.852" StrokeThickness="10"/>
<Ellipse Fill="{x:Null}" Stroke="#7FFFFFFF" Width="70" Height="70" Canvas.Left="102.666" Canvas.Top="19.852" StrokeThickness="10"/>
<Ellipse Fill="{x:Null}" Stroke="#BFFFFFFF" Width="107" Height="107" Canvas.Left="146.952" Canvas.Top="96.042" StrokeThickness="10"/>
<Ellipse Fill="{x:Null}" Stroke="#A5FFFFFF" Width="100" Height="100" Canvas.Left="709.21" Canvas.Top="677.459" StrokeThickness="35"/>
<Ellipse Fill="{x:Null}" Stroke="#BFFFFFFF" Width="180" Height="180" Canvas.Left="-99.632" Canvas.Top="-78.445" StrokeThickness="24"/>
<Ellipse Fill="{x:Null}" Stroke="#FFFFFFFF" Width="23" Height="23" Canvas.Left="987.198" Canvas.Top="729.459" StrokeThickness="4"/>
<Ellipse Fill="{x:Null}" Stroke="#BFFFFFFF" Width="850" Height="850" Canvas.Top="425" Canvas.Left="-425" StrokeThickness="45"/>
<Ellipse Fill="{x:Null}" Stroke="#BFFFFFFF" Width="750" Height="750" Canvas.Left="-375" Canvas.Top="475" StrokeThickness="45"/>
<Ellipse Fill="{x:Null}" Stroke="#BFFFFFFF" Width="650" Height="650" Canvas.Left="-325" Canvas.Top="525" StrokeThickness="45"/>
<Ellipse Fill="{x:Null}" Stroke="#66FFFFFF" Width="300" Height="300" Canvas.Left="859.711" Canvas.Top="-140.999" StrokeThickness="100"/>
</Canvas>
Extremely easy to create and it looks… well, OK-ish!
“With great power comes great responsibility” – Peter Parker (Spider-man)
Although the shape classes is extremely easy to use, it has overhead. Shape derive from FrameworkElement (which derive from UIElement).
Before we can optimize our background, we have to quickly look at one more of the shape classes (Path)
Path, the swiss army knife of shapes
The Path class has the ability to draw curves and complex shapes.
<Ellipse Fill="#7FFFFFFF"Stroke="{x:Null}"Width="40"Height="40"Canvas.Left="117.666"Canvas.Top="34.852"/>
The Ellipse used earlier can be represented using a Path as follows
<Path Fill="#7FFFFFFF" Stroke="{x:Null}">
<Path.Data>
<EllipseGeometry RadiusX="20" RadiusY="20" Center="137.666 54.852"/>
</Path.Data>
</Path>
Now, this doesn’t really increase the performance, so why do it?
Geometries can be grouped, allowing you render multiple geometries using a single path! This reduces the amount of full UIElements needed to render the background! The general rule of thumb is that less UIElements with more complex geometries performs better that more UIElements with simpler geometries!!!
Just keep in mind that the path does the rendering and it uses it’s Fill & Stroke properties to render all the geometries! This isn’t ideal for my background because I actually want to have different opacities per shape/geometry…
DrawingImage
The Path is not the only item that can be used to draw geometries… The DrawingImage can actually render multiple GeometryDrawing (Each with it’s own Brush & Pen)
<Image Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top" Canvas.Left="-425" Canvas.Top="-140">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#7FFFFFFF">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="20" RadiusY="20" Center="137.666 54.852"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="#4CFFFFFF">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="66" RadiusY="66" Center="71.666 159.852"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<!-- Removed for brevity -->
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
Now I have one UIElement rendering my complete background!!!
If you found this article interesting or useful, please 