I’ve recently been working on a project using WPF to produce a diagram. In this I must show text alongside symbols that illustrate information associated with the text.
To draw the symbols I initially used some png images I had produced. Within my diagram these images appeared blurry and only looked worse when zoomed in on. To improve on this I decided I would use a vector rather than a rastor image format. Below is the method I used to get the rastor image from a file path:
protected Image GetSymbolImage(string symbolPath, int symbolHeight)
{
Image symbol = new Image();
symbol.Height = symbolHeight;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(symbolPath);
bitmapImage.DecodePixelHeight = symbolHeight;
bitmapImage.EndInit();
symbol.Source = bitmapImage;
return symbol;
}
Unfortunately this does not recognise vector image formats. So instead I used a method like the following, where “path” is the file path to a vector image of the format .xaml:
public static Canvas LoadXamlCanvas(string path)
{
//if a file exists at the specified path
if (File.Exists(path))
{
//store the text in the file
string text = File.ReadAllText(path);
//produce a canvas from the text
StringReader stringReader = new StringReader(text);
XmlReader xmlReader = XmlReader.Create(stringReader);
Canvas c = (Canvas)XamlReader.Load(xmlReader);
//return the canvas
return c;
}
return null;
}
This worked but drastically killed performance when called repeatedly.
I found the logic necessary for text to canvas conversion (see above) was the main cause of the performance problem therefore embedding the .xaml images would not alone resolve the performance issue.
I tried using this method only on the initial load of my application and storing the resulting canvases in a dictionary that could later be accessed much quicker but I later realised when using the canvases within the dictionary I would have to make copies of them. All the logic I found online associated with making copies used a XamlWriter and XamlReader which would again just introduce a performance problem.
The solution I used was to copy the contents of each .xaml image into its own user control and then make use of these user controls where appropriate. This means I now display vector graphics and performance is much better.
However this solution to me seems pretty clumsy. I’m new to WPF and wonder if there is some built in way of storing and reusing xaml throughout an application?
Apologies for the length of this question. I thought having a record of my attempts might help someone with any similar problem.
Thanks.
What you’re fundamentally doing, when you wrap your
Canvasin aUserControl, is creating aFrameworkTemplate. AFrameworkTemplateis an abstract class that, saith the documentation, “enables the instantiation of a tree ofFrameworkElementand/orFrameworkContentElementobjects,” which is your real objective here.There are two concrete subclasses of
FrameworkTemplate:ControlTemplateandDataTemplate. Creating aUserControlin XAML sets itsContent, which is used to construct itsControlTemplate, so that every time you instantiate theUserControlit instantiates all of the objects in that template.You could instead create a
ControlTemplateand then use it when creating some other kind of control. Or – and this is probably the best approach – you could create aDataTemplate.For instance, consider this XAML:
Now you can do this:
This will create a
Canvas(your symbol) in theItemsControl‘sCanvasfor everySymbolin the collection, positioned where theSymbol.TopandSymbol.Leftproperties say it should be positioned.With a little monkeying around with template selectors and the proper design of the
Symbolclass, you can probably use data-binding to construct the entirety of your diagram.Edit
There are other subclasses of
FrameworkTemplatebesidesControlTemplateandDataTemplate. One appears in this very post.