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); }
CopyGraphToBitmap ( Int32 bitmapWidthPx, Int32 bitmapHeightPx ) { const String MethodName = "CopyGraphToBitmap"; this.ArgumentChecker.CheckArgumentPositive(MethodName, "bitmapWidthPx", bitmapWidthPx); this.ArgumentChecker.CheckArgumentPositive(MethodName, "bitmapHeightPx", bitmapHeightPx); CheckIfLayingOutGraph(MethodName); // Save the current vertex locations. LayoutSaver oLayoutSaver = new LayoutSaver(this.Graph); // Adjust the control's graph scale so that the graph's vertices and // edges will be the same relative size in the image that they are in // the control. GraphImageScaler oGraphImageScaler = new GraphImageScaler(this); oGraphImageScaler.SetGraphScale(bitmapWidthPx, bitmapHeightPx, WpfGraphicsUtil.GetScreenDpi(this).Width); // Adjust the control's transforms so that the image will be centered // on the same point on the graph that the control is centered on. GraphImageCenterer oGraphImageCenterer = new GraphImageCenterer(this); oGraphImageCenterer.CenterGraphImage( new Size(bitmapWidthPx, bitmapHeightPx) ); // Transform the graph's layout to the specified size. Double dOriginalActualWidth = this.ActualWidth; Double dOriginalActualHeight = this.ActualHeight; Rect oBitmapRectangle = new Rect(0, 0, (Double)bitmapWidthPx, (Double)bitmapHeightPx); TransformLayout(oBitmapRectangle); Debug.Assert(m_eLayoutState == LayoutState.Stable); DrawGraph(oBitmapRectangle); System.Drawing.Bitmap oBitmap = WpfGraphicsUtil.VisualToBitmap(this, bitmapWidthPx, bitmapHeightPx); // Restore the original layout. // // NOTE: // // Don't try calling TransformLayout() again using the original // rectangle. The first call to TransformLayout() lost "resolution" if // the layout was transformed to a smaller rectangle, and attempting to // reverse the transform will yield poor results. oLayoutSaver.RestoreLayout(); oGraphImageScaler.RestoreGraphScale(); oBitmapRectangle = new Rect(0, 0, dOriginalActualWidth, dOriginalActualHeight); Debug.Assert(m_eLayoutState == LayoutState.Stable); DrawGraph(oBitmapRectangle); oGraphImageCenterer.RestoreCenter(); return (oBitmap); }