tags:

views:

100

answers:

2

Hi,

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.

A: 

Steve,

while this might not answer your whole question or solve your problem directly, it might help you with "storing and reusing xaml": You can dynamically load XAML or write objects to XAML using the XamlReader and XamlWriter Classes. I can't estimate if you would actually gain a performance advantage but maybe it's worth a try.

Example from MSDN:

// Create the Button.
Button origianlButton = new Button();
origianlButton.Height = 50;
origianlButton.Width = 100;
origianlButton.Background = Brushes.AliceBlue;
origianlButton.Content = "Click Me";

// Save the Button to a string.
string savedButton = XamlWriter.Save(origianlButton);

// Load the button
StringReader stringReader = new StringReader(savedButton);
XmlReader xmlReader = XmlReader.Create(stringReader);
Button readerLoadButton = (Button)XamlReader.Load(xmlReader);

Best regards

moonground.de
A: 

What you're fundamentally doing, when you wrap your Canvas in a UserControl, is creating a FrameworkTemplate. A FrameworkTemplate is an abstract class that, saith the documentation, "enables the instantiation of a tree of FrameworkElement and/or FrameworkContentElement objects," which is your real objective here.

There are two concrete subclasses of FrameworkTemplate: ControlTemplate and DataTemplate. Creating a UserControl in XAML sets its Content, which is used to construct its ControlTemplate, so that every time you instantiate the UserControl it instantiates all of the objects in that template.

You could instead create a ControlTemplate and then use it when creating some other kind of control. Or - and this is probably the best approach - you could create a DataTemplate.

For instance, consider this XAML:

<DataTemplate TargetType="Symbol">
   <Canvas Canvas.Top="{Binding Top}" Canvas.Left="{Binding Left}">
      <!-- XAML to construct the symbol goes here -->
   </Canvas>
</DataTemplate>

Now you can do this:

<ItemsControl ItemsSource="{StaticResource SomeCollectionOfSymbolObjects}">
   <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
         <Canvas/>
      </ItemsPanelTemplate>
   </ItemsControl.ItemsPanel>
</ItemsControl>

This will create a Canvas (your symbol) in the ItemsControl's Canvas for every Symbol in the collection, positioned where the Symbol.Top and Symbol.Left properties say it should be positioned.

With a little monkeying around with template selectors and the proper design of the Symbol class, you can probably use data-binding to construct the entirety of your diagram.

Edit

There are other subclasses of FrameworkTemplate besides ControlTemplate and DataTemplate. One appears in this very post.

Robert Rossney