Rendering bitmap of Grid containing Diagram

I need to generate an image file containing a Grid with a Diagram object inside of it.
I’m using Silverlight 5 and GoSilverlight 1.3.3.5.

Basically I need my output to look like this (the Diagram is outlined in Red):

With the code that I’ve written so far, this is the result that I’m getting:

So it seems like maybe templates are not being applied or rendered before the bitmap is created.
I’ve tried Diagram.Panel.UpdateLayout() and Diagram.Panel.ApplyTemplate() before generating the bitmap object and these don’t seem to help.

My diagram object looks like this:


<ControlTemplate x:Key="WiringDiagramTemplate" TargetType="go:Diagram">
     <ScrollViewer HorizontalScrollBarVisibility="Auto"
          VerticalScrollBarVisibility="Auto"
          Padding="0"
          BorderThickness="0">
               <go:DiagramPanel x:Name="Panel"
                    Stretch="{TemplateBinding Stretch}"
                    Padding="{TemplateBinding Padding}"
                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
     </ScrollViewer>
</ControlTemplate>


<DataTemplate x:Key="WireTemplate">
     <go:LinkShape Stroke="{Binding Data.WireColor}" StrokeThickness="2"
          go:Part.SelectionAdorned="True"
          go:Part.Reshapable="True" Loaded="Wire_Loaded" >
          <go:Link.Route>
               <go:Route Routing="Orthogonal" RelinkableFrom="True" RelinkableTo="True" />
          </go:Link.Route>
     </go:LinkShape>
</DataTemplate>

....


<go:Diagram x:Name="myWiring" Padding="10"
     HorizontalContentAlignment="Center"
     VerticalContentAlignment="Center"
     Template="{StaticResource WiringDiagramTemplate}"
     LinkTemplate="{StaticResource WireTemplate}" 
     Margin="0" 
     Height="170"
/>

and I’m generating the image by:


foreach (Link link in myDiagram.Links)
{
    Cable cable = (Cable)link.Data;

    myDiagram.SelectedLink = null;

    var model = cable.Model;
    if (model == null)
    {
        model = new WireModel();
        model.NodesSource = cable.Pins;
        model.LinksSource = cable.Wires;
    }

    myWiring.Model = model;

    UIElement element = grdmyWiring;

    double width = ((FrameworkElement)element).ActualWidth;
    double height = ((FrameworkElement)element).ActualHeight;

    WriteableBitmap bitmap = new WriteableBitmap(Convert.ToInt32(Math.Round(width)), Convert.ToInt32(Math.Round(height)));
    bitmap.Render(element, null);
    bitmap.Invalidate();

    PngBitmapEncoder png = new PngBitmapEncoder();
    png.Frames.Add(BitmapFrame.Create(bitmap));

    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(cable.From + cable.FromPort + "_" + cable.To + cable.ToPort + ".png", FileMode.Create, isoFile))
        {
            png.Save(isoStream);
        }
    }
}

Is there a trick to updating the Diagram to prepare it for being rendered as a bitmap after I apply a model to it?

The rendering is happening asynchronously, so the parts have not yet had time to be arranged.

It might help to call Diagram.Panel.MakeBitmap. Call the overloaded method that takes an Action. In your case the Action would do the PNG encoding and storage.

The only overloaded method that I could find that sounded useful was Diagram.Panel.Loaded() but I’m still not getting anything. I don’t see any overloads for MakeBitmap. Which overloaded method do you suggest that I put my PNG encoding and storage in? I don’t see anything like Diagram.Panel.BitmapLoaded().

This was my unsuccessful attempt:


            foreach (Link link in myDiagram.Links)
            {
                Cable cable = (Cable)link.Data;

                myDiagram.SelectedLink = null;

                var model = cable.Model;
                if (model == null)
                {
                    model = new WireModel();
                    model.NodesSource = cable.Pins;
                    model.LinksSource = cable.Wires;
                }

                myWiring.Panel.Loaded += (s, args) =>
                {
                    Rect bounds = myWiring.Panel.DiagramBounds;
                    double w = bounds.Width;
                    double h = bounds.Height;

                    BitmapSource bitmap = myWiring.Panel.MakeBitmap(new Size(w, h), 96, new Point(bounds.X, bounds.Y), 1);

                    PngBitmapEncoder png = new PngBitmapEncoder();
                    png.Frames.Add(BitmapFrame.Create(bitmap));

                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(cable.From + cable.FromPort + "_" + cable.To + cable.ToPort + ".png", FileMode.Create, isoFile))
                        {
                            png.Save(isoStream);
                        }
                    }
                };

                myWiring.Model = model;

I meant that you should call:
DiagramPanel.MakeBitmap(Size, Double, Point, Double, Action)
rather than:
DiagramPanel.MakeBitmap()
or:
DiagramPanel.MakeBitmap(Size, Double, Point, Double)

Here’s the reference: MakeBitmap Method

Thanks for the suggestion. It helped with my rendering problem but now it seems that the bitmap only generates correctly if the Diagram is actually displaying on the screen.

Is there any way to assign a model to a diagram and make a bitmap from that diagram totally from code-behind without it having to be displayed on the screen? That’s ultimately what I’m trying to do.

This is the code that I’m using but it’s only working when the diagram is physically displayed, otherwise the bitmap still generates like this:


foreach (Link link in myDiagram.Links)
{
    Cable cable = (Cable)link.Data;

    var model = cable.Model;
    if (model == null)
    {
        model = new WireModel();
        model.NodesSource = cable.Pins;
        model.LinksSource = cable.Wires;
    }
                
    myWiring.Model = model;

    Rect bounds = myWiring.Panel.DiagramBounds;
    double w = bounds.Width;
    double h = bounds.Height;
    myWiring.Panel.MakeBitmap(new Size(w, h), 96, new Point(bounds.X, bounds.Y), 1,
        bmp =>
        {
            PngBitmapEncoder png = new PngBitmapEncoder();
            png.Frames.Add(BitmapFrame.Create(bmp));

            using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(cable.From + cable.FromPort + "_" + cable.To + cable.ToPort + ".png", FileMode.Create, isoFile))
                {
                    png.Save(isoStream);
                }
            }
        });
}

Try calling Measure and then Arrange on the Diagram before calling MakeBitmap.

If that doesn’t help, you could try adding the Diagram to your app’s visual tree in a manner that does not interfere.