Centers a graph image for the NodeXLControl and later restores the original graph location.
This prepares the NodeXLControl for being saved as an image. It adjusts the control's transforms so that the image will be centered on the same point on the graph that the control is centered on. This prevents an unwanted graph shift when the image has dimensions different from those of the control.

Because the NodeXLControl's transforms are not public, this class is embedded within the control.

Pass an image size to CenterGraphImage, which immediately centers the graph within the image dimensions. Call to restore the graph to its original location.

Inheritance: Object
    Composite
    (
        Double compositeWidth,
        Double compositeHeight,
        String headerText,
        String footerText,
        System.Drawing.Font headerFooterFont,
        IEnumerable<LegendControlBase> legendControls
    )
    {
        Debug.Assert(compositeWidth > 0);
        Debug.Assert(compositeHeight > 0);
        Debug.Assert(headerFooterFont != null);
        Debug.Assert(legendControls != null);
        AssertValid();

        // Note:
        //
        // Don't try taking a shortcut by using
        // NodeXLControl.CopyGraphToBitmap() to get an image of the graph and
        // then compositing the image with the other elements.  That would work
        // if the caller were creating an image, but if it were creating an XPS
        // document, the graph would no longer be scalable.

        Double dScreenDpi =
            WpfGraphicsUtil.GetScreenDpi(m_oNodeXLControl).Width;

        // The NodeXLControl can't be a child of two logical trees, so
        // disconnect it from its parent after saving the current vertex
        // locations.

        m_oLayoutSaver = new LayoutSaver(m_oNodeXLControl.Graph);

        Debug.Assert(m_oNodeXLControl.Parent is Panel);
        m_oParentPanel = (Panel)m_oNodeXLControl.Parent;
        UIElementCollection oParentChildren = m_oParentPanel.Children;
        m_iChildIndex = oParentChildren.IndexOf(m_oNodeXLControl);
        oParentChildren.Remove(m_oNodeXLControl);

        m_oGraphImageScaler = new GraphImageScaler(m_oNodeXLControl);

        m_oGraphImageCenterer = new NodeXLControl.GraphImageCenterer(
            m_oNodeXLControl);

        // The header and footer are rendered as Label controls.  The legend is
        // rendered as a set of Image controls.

        Label oHeaderLabel, oFooterLabel;
        IEnumerable<Image> oLegendImages;
        Double dHeaderHeight, dTotalLegendHeight, dFooterHeight;

        CreateHeaderOrFooterLabel(headerText, compositeWidth, headerFooterFont,
            out oHeaderLabel, out dHeaderHeight);

        CreateLegendImages(legendControls, compositeWidth, out oLegendImages,
            out dTotalLegendHeight);

        CreateHeaderOrFooterLabel(footerText, compositeWidth, headerFooterFont,
            out oFooterLabel, out dFooterHeight);

        m_oNodeXLControl.Width = compositeWidth;

        m_oNodeXLControl.Height = Math.Max(10,
            compositeHeight - dHeaderHeight - dTotalLegendHeight
            - dFooterHeight);

        // Adjust the control's graph scale so that the graph's vertices and
        // edges will be the same relative size in the composite that they are
        // in the control.

        m_oGraphImageScaler.SetGraphScale(
            (Int32)WpfGraphicsUtil.WpfToPx(compositeWidth, dScreenDpi),
            (Int32)WpfGraphicsUtil.WpfToPx(m_oNodeXLControl.Height, dScreenDpi),
            dScreenDpi);

        // Adjust the NodeXLControl's translate transforms so that the
        // composite will be centered on the same point on the graph that the
        // NodeXLControl is centered on.

        m_oGraphImageCenterer.CenterGraphImage( new Size(compositeWidth,
            m_oNodeXLControl.Height) );

        StackPanel oStackPanel = new StackPanel();
        UIElementCollection oStackPanelChildren = oStackPanel.Children;

        // To avoid a solid black line at the bottom of the header or the top
        // of the footer, which is caused by rounding errors, make the
        // StackPanel background color the same as the header and footer.

        oStackPanel.Background = HeaderFooterBackgroundBrush;

        if (oHeaderLabel != null)
        {
            oStackPanelChildren.Add(oHeaderLabel);
        }

        // Wrap the NodeXLControl in a Grid to clip it.

        m_oGrid = new Grid();
        m_oGrid.Width = m_oNodeXLControl.Width;
        m_oGrid.Height = m_oNodeXLControl.Height;
        m_oGrid.ClipToBounds = true;
        m_oGrid.Children.Add(m_oNodeXLControl);

        oStackPanelChildren.Add(m_oGrid);

        foreach (Image oLegendImage in oLegendImages)
        {
            oStackPanelChildren.Add(oLegendImage);
        }

        if (oFooterLabel != null)
        {
            oStackPanelChildren.Add(oFooterLabel);
        }

        Size oCompositeSize = new Size(compositeWidth, compositeHeight);
        Rect oCompositeRectangle = new Rect(new Point(), oCompositeSize);

        oStackPanel.Measure(oCompositeSize);
        oStackPanel.Arrange(oCompositeRectangle);
        oStackPanel.UpdateLayout();

        return (oStackPanel);
    }