UndrawVertex ( IVertex vertex, GraphDrawingContext graphDrawingContext ) { Debug.Assert(vertex != null); Debug.Assert(graphDrawingContext != null); AssertValid(); // Retrieve the VertexDrawingHistory object for the vertex, if one // exists. (If the vertex was previously hidden, there won't be a // VertexDrawingHistory object for it.) Dictionary <Int32, VertexDrawingHistory> oVertexDrawingHistories = graphDrawingContext.VertexDrawingHistories; Int32 iVertexID = vertex.ID; VertexDrawingHistory oVertexDrawingHistory; if (oVertexDrawingHistories.TryGetValue( iVertexID, out oVertexDrawingHistory)) { // Remove the VertexDrawingHistory object from the dictionary. oVertexDrawingHistories.Remove(iVertexID); // Remove the vertex's DrawingVisual object, which will cause the // vertex to disappear. m_oAllVertexDrawingVisuals.Children.Remove( oVertexDrawingHistory.DrawingVisual); } }
DrawEdge ( IEdge oEdge, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oEdge != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); EdgeDrawingHistory oEdgeDrawingHistory; Dictionary <Int32, EdgeDrawingHistory> oEdgeDrawingHistories = oGraphDrawingContext.EdgeDrawingHistories; if (m_oEdgeDrawer.TryDrawEdge(oEdge, oGraphDrawingContext, out oEdgeDrawingHistory)) { Debug.Assert(oEdgeDrawingHistory != null); GetEdgeDrawingVisuals(oEdgeDrawingHistory).Children.Add( oEdgeDrawingHistory.DrawingVisual); // Save the EdgeDrawingHistory object. oEdgeDrawingHistories.Add(oEdge.ID, oEdgeDrawingHistory); } }
UndrawEdge ( IEdge edge, GraphDrawingContext graphDrawingContext ) { Debug.Assert(edge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); // Retrieve the EdgeDrawingHistory object for the edge, if one exists. // (If the edge was previously hidden, there won't be an // EdgeDrawingHistory object for it.) Dictionary <Int32, EdgeDrawingHistory> oEdgeDrawingHistories = graphDrawingContext.EdgeDrawingHistories; Int32 iEdgeID = edge.ID; EdgeDrawingHistory oEdgeDrawingHistory; if (oEdgeDrawingHistories.TryGetValue( iEdgeID, out oEdgeDrawingHistory)) { // Remove the EdgeDrawingHistory object from the dictionary. oEdgeDrawingHistories.Remove(iEdgeID); // Remove the edge's DrawingVisual object, which will cause the // edge to disappear. GetEdgeDrawingVisuals(oEdgeDrawingHistory).Children.Remove( oEdgeDrawingHistory.DrawingVisual); } }
DrawNewVertex ( IVertex newVertex, GraphDrawingContext graphDrawingContext ) { Debug.Assert(newVertex != null); Debug.Assert(graphDrawingContext != null); AssertValid(); DrawVertex(newVertex, graphDrawingContext); }
DrawNewEdge ( IEdge newEdge, GraphDrawingContext graphDrawingContext ) { Debug.Assert(newEdge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); DrawEdge(newEdge, graphDrawingContext); }
RedrawEdge ( IEdge edge, GraphDrawingContext graphDrawingContext ) { Debug.Assert(edge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); UndrawEdge(edge, graphDrawingContext); DrawEdge(edge, graphDrawingContext); }
RedrawVertex ( IVertex vertex, GraphDrawingContext graphDrawingContext ) { Debug.Assert(vertex != null); Debug.Assert(graphDrawingContext != null); AssertValid(); UndrawVertex(vertex, graphDrawingContext); DrawVertex(vertex, graphDrawingContext); }
TryGetEdgeEndpoints ( IVertex oVertex1, IVertex oVertex2, GraphDrawingContext oGraphDrawingContext, out Point oEdgeEndpoint1, out Point oEdgeEndpoint2 ) { Debug.Assert(oVertex1 != null); Debug.Assert(oVertex2 != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); oEdgeEndpoint1 = new Point(); oEdgeEndpoint2 = new Point(); // Retrieve the information about how the vertices were drawn. Dictionary <Int32, VertexDrawingHistory> oVertexDrawingHistories = oGraphDrawingContext.VertexDrawingHistories; VertexDrawingHistory oVertex1DrawingHistory, oVertex2DrawingHistory; if ( !oVertexDrawingHistories.TryGetValue(oVertex1.ID, out oVertex1DrawingHistory) || !oVertexDrawingHistories.TryGetValue(oVertex2.ID, out oVertex2DrawingHistory) ) { // One of the edge's vertices is hidden. return(false); } // The drawing histories determine the edge endpoints. For example, if // oVertex1 was drawn as a circle, then oVertex1DrawingHistory is a // CircleVertexDrawingHistory that knows to put its endpoint on the // circle itself and not at the circle's center. oVertex1DrawingHistory.GetEdgeEndpoint(oVertex2DrawingHistory, out oEdgeEndpoint1); oVertex2DrawingHistory.GetEdgeEndpoint(oVertex1DrawingHistory, out oEdgeEndpoint2); return(true); }
CreateVisual ( Point currentMouseLocation, Color backColor, VertexDrawer vertexDrawer ) { Debug.Assert(vertexDrawer != null); Debug.Assert(m_bDragIsInProgress); AssertValid(); // This method redraws the dragged vertices at an offset location, and // adds the resulting Visuals to a ContainerVisual. // // Figure out the offset. Double dOffsetX = currentMouseLocation.X - m_oMouseDownLocation.X; Double dOffsetY = currentMouseLocation.Y - m_oMouseDownLocation.Y; GraphDrawingContext oGraphDrawingContext = new GraphDrawingContext( m_oGraphRectangle, m_iMargin, backColor); ContainerVisual oContainerVisual = new ContainerVisual(); foreach (IVertex oVertex in m_aoVertices) { System.Drawing.PointF oOriginalLocation = GetOriginalVertexLocation(oVertex); oVertex.Location = new System.Drawing.PointF( oOriginalLocation.X + (Single)dOffsetX, oOriginalLocation.Y + (Single)dOffsetY ); VertexDrawingHistory oVertexDrawingHistory; if (vertexDrawer.TryDrawVertex(oVertex, oGraphDrawingContext, out oVertexDrawingHistory)) { oContainerVisual.Children.Add( oVertexDrawingHistory.DrawingVisual); } } m_oVisual = oContainerVisual; return(m_oVisual); }
TryDrawSelfLoop ( IVertex oVertex, DrawingContext oDrawingContext, GraphDrawingContext oGraphDrawingContext, Color oColor, Double dWidth, Boolean bDrawArrow ) { Debug.Assert(oVertex != null); Debug.Assert(oDrawingContext != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(dWidth >= 0); AssertValid(); // Retrieve the information about how the vertex was drawn. VertexDrawingHistory oVertexDrawingHistory; if (!oGraphDrawingContext.VertexDrawingHistories.TryGetValue( oVertex.ID, out oVertexDrawingHistory)) { // The edge's vertex is hidden, so the edge should be hidden also. return(false); } // Determine the edge of the graph rectangle that is farthest from the // vertex. Point oVertexLocation = oVertexDrawingHistory.VertexLocation; RectangleEdge eFarthestGraphRectangleEdge = WpfGraphicsUtil.GetFarthestRectangleEdge(oVertexLocation, oGraphDrawingContext.GraphRectangle); // Get the point on the vertex at which to draw the self-loop. Point oSelfLoopEndpoint = oVertexDrawingHistory.GetSelfLoopEndpoint( eFarthestGraphRectangleEdge); DrawSelfLoopAt(oDrawingContext, oGraphDrawingContext, oColor, dWidth, oSelfLoopEndpoint, eFarthestGraphRectangleEdge, bDrawArrow); return(true); }
DrawBackground ( IGraph oGraph, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oGraph != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); // Draw the background color, followed by the background image if one // was specified. Color oBackColor; if (!TryGetColorValue(oGraph, ReservedMetadataKeys.GraphBackColor, out oBackColor)) { oBackColor = m_oBackColor; } DrawingVisual oBackgroundDrawingVisual = new DrawingVisual(); using (DrawingContext oDrawingContext = oBackgroundDrawingVisual.RenderOpen()) { oDrawingContext.DrawRectangle( CreateFrozenSolidColorBrush(oBackColor), null, oGraphDrawingContext.GraphRectangle); Object oImageSourceAsObject; if (oGraph.TryGetValue(ReservedMetadataKeys.GraphBackgroundImage, typeof(ImageSource), out oImageSourceAsObject)) { ImageSource oImageSource = (ImageSource)oImageSourceAsObject; oDrawingContext.DrawImage(oImageSource, new Rect(new Size(oImageSource.Width, oImageSource.Height))); } } m_oVisualCollection.Add(oBackgroundDrawingVisual); }
DrawLabel ( DrawingContext drawingContext, GraphDrawingContext graphDrawingContext, VertexDrawingHistory vertexDrawingHistory, Rect vertexBounds, FormattedText formattedText ) { Debug.Assert(drawingContext != null); Debug.Assert(graphDrawingContext != null); Debug.Assert(vertexDrawingHistory != null); Debug.Assert(formattedText != null); AssertValid(); DrawLabel(drawingContext, graphDrawingContext, vertexDrawingHistory, vertexBounds, GetLabelPosition(vertexDrawingHistory.Vertex), formattedText); }
DrawVertex ( IVertex oVertex, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oVertex != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); VertexDrawingHistory oVertexDrawingHistory; Dictionary <Int32, VertexDrawingHistory> oVertexDrawingHistories = oGraphDrawingContext.VertexDrawingHistories; if (m_oVertexDrawer.TryDrawVertex(oVertex, oGraphDrawingContext, out oVertexDrawingHistory)) { Debug.Assert(oVertexDrawingHistory != null); // Add the DrawingVisual for the vertex to // m_oAllVertexDrawingVisuals, and add the VertexDrawingHistory to // the oGraphDrawingContext.VertexDrawingHistories dictionary. DrawingVisual oVertexChildDrawingVisual = oVertexDrawingHistory.DrawingVisual; m_oAllVertexDrawingVisuals.Children.Add(oVertexChildDrawingVisual); oVertexDrawingHistories.Add(oVertex.ID, oVertexDrawingHistory); // Save the vertex on the DrawingVisual for later retrieval. SaveVertexOnDrawingVisual(oVertex, oVertexChildDrawingVisual); } }
//************************************************************************* // Method: DrawImageShape() // /// <summary> /// Draws a vertex as a specified image. /// </summary> /// /// <param name="oVertex"> /// The vertex to draw. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="oDrawingContext"> /// The DrawingContext to use. /// </param> /// /// <param name="oDrawingVisual"> /// The <see cref="DrawingVisual" /> object from which <paramref /// name="oDrawingContext" /> was obtained. /// </param> /// /// <param name="eVisibility"> /// The visibility of the vertex. /// </param> /// /// <param name="bDrawAsSelected"> /// true to draw the vertex as selected. /// </param> /// /// <param name="sAnnotation"> /// The annotation to draw next to the image, or null if there is no /// annotation. /// </param> /// /// <param name="oImageSource"> /// The image to draw. /// </param> /// /// <param name="oVertexLabelDrawer"> /// Object that draws a vertex label as an annotation. /// </param> /// /// <returns> /// A VertexDrawingHistory object that retains information about how the /// vertex was drawn. /// </returns> //************************************************************************* protected VertexDrawingHistory DrawImageShape( IVertex oVertex, GraphDrawingContext oGraphDrawingContext, DrawingContext oDrawingContext, DrawingVisual oDrawingVisual, VisibilityKeyValue eVisibility, Boolean bDrawAsSelected, String sAnnotation, ImageSource oImageSource, VertexLabelDrawer oVertexLabelDrawer ) { Debug.Assert(oVertex != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(oDrawingContext != null); Debug.Assert(oDrawingVisual != null); Debug.Assert(oImageSource != null); Debug.Assert(oVertexLabelDrawer != null); AssertValid(); // Move the vertex if it falls outside the graph rectangle. Rect oVertexRectangle = GetVertexRectangle( GetVertexLocation(oVertex), oImageSource.Width * m_dGraphScale, oImageSource.Height * m_dGraphScale); MoveVertexIfNecessary(oVertex, ref oVertexRectangle, oGraphDrawingContext); Byte btAlpha = 255; if (!bDrawAsSelected) { // Check for a non-opaque alpha value. btAlpha = GetAlpha(oVertex, eVisibility, btAlpha); } VertexDrawingHistory oVertexDrawingHistory = new ImageVertexDrawingHistory(oVertex, oDrawingVisual, bDrawAsSelected, oVertexRectangle); if (btAlpha > 0) { oDrawingContext.DrawImage(oImageSource, oVertexRectangle); Color oColor = GetColor(oVertex, eVisibility, bDrawAsSelected); // Draw an outline rectangle. oDrawingContext.DrawRectangle(null, GetPen(oColor, DefaultPenThickness), oVertexRectangle); if (btAlpha < 255) { // Real transparency can't be achieved with arbitrary images, // so simulate transparency by drawing on top of the image with // a translucent brush the same color as the graph's // background. // // This really isn't a good solution. Is there are better way // to simulate transparency? Color oTranslucentColor = oGraphDrawingContext.BackColor; oTranslucentColor.A = (Byte)( (Byte)255 - btAlpha ); oDrawingContext.DrawRectangle( CreateFrozenSolidColorBrush(oTranslucentColor), null, oVertexRectangle); } if (sAnnotation != null) { oVertexLabelDrawer.DrawLabel(oDrawingContext, oGraphDrawingContext, oVertexDrawingHistory, oVertexRectangle, CreateFormattedText(sAnnotation, oColor) ); } } return (oVertexDrawingHistory); }
//************************************************************************* // Method: DrawLabelShape() // /// <summary> /// Draws a vertex as a label. /// </summary> /// /// <param name="oVertex"> /// The vertex to draw. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="oDrawingContext"> /// The DrawingContext to use. /// </param> /// /// <param name="oDrawingVisual"> /// The <see cref="DrawingVisual" /> object from which <paramref /// name="oDrawingContext" /> was obtained. /// </param> /// /// <param name="eVisibility"> /// The visibility of the vertex. /// </param> /// /// <param name="bDrawAsSelected"> /// true to draw the vertex as selected. /// </param> /// /// <param name="sLabel"> /// The label to draw. Can't be null or empty. /// </param> /// /// <returns> /// A VertexDrawingHistory object that retains information about how the /// vertex was drawn. /// </returns> //************************************************************************* protected VertexDrawingHistory DrawLabelShape( IVertex oVertex, GraphDrawingContext oGraphDrawingContext, DrawingContext oDrawingContext, DrawingVisual oDrawingVisual, VisibilityKeyValue eVisibility, Boolean bDrawAsSelected, String sLabel ) { Debug.Assert(oVertex != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(oDrawingContext != null); Debug.Assert(oDrawingVisual != null); Debug.Assert( !String.IsNullOrEmpty(sLabel) ); AssertValid(); // Figure out what colors to use. Color oOutlineColor; Color oTextColor = GetColor(oVertex, eVisibility, false); Color oFillColor = GetColor(oVertex, eVisibility, ReservedMetadataKeys.PerVertexLabelFillColor, m_oLabelFillColor, true); if (bDrawAsSelected) { // The outline color is always the selected color. oOutlineColor = m_oSelectedColor; // The text color is the default or per-vertex color with no alpha. oTextColor.A = 255; // The fill color is the default or per-vertex fill color with no // alpha. oFillColor.A = 255; } else { // The outline color is the default or per-vertex color with alpha. oOutlineColor = oTextColor; // The text color is the default or per-vertex color with alpha. // The fill color is the default or per-vertex fill color with // alpha. } Double dLabelFontSize = GetLabelFontSize(oVertex); // Format the text, subject to a maximum label size. If a maximum // label size isn't specified, the label would be as wide as necessary // to accommodate the text length. Specifying a maximum size forces // the text to wrap at word breaks. // // Side effect: If the font size is large enough, not a single // character of the text will fit within the specified width and no // text will be drawn. FormattedText oFormattedText = CreateFormattedText(sLabel, oTextColor, dLabelFontSize); oFormattedText.MaxTextWidth = MaximumLabelWidth * m_dGraphScale; oFormattedText.MaxTextHeight = MaximumLabelHeight * m_dGraphScale; Rect oVertexRectangle = GetVertexRectangle( GetVertexLocation(oVertex), oFormattedText.Width, oFormattedText.Height); // Pad the text. Rect oVertexRectangleWithPadding = oVertexRectangle; Double dLabelPadding = GetLabelPadding(dLabelFontSize); oVertexRectangleWithPadding.Inflate(dLabelPadding, dLabelPadding * 0.7); if (m_oTypeface.Style != FontStyles.Normal) { // This is a hack to move the right edge of the padded rectangle // to the right to adjust for wider italic text, which // FormattedText.Width does not account for. What is the correct // way to do this? It might involve the FormattedText.Overhang* // properties, but I'll be darned if I can understand how those // properties work. Double dItalicCompensation = dLabelFontSize / 7.0; oVertexRectangleWithPadding.Inflate(dItalicCompensation, 0); oVertexRectangleWithPadding.Offset(dItalicCompensation, 0); } // Move the vertex if it falls outside the graph rectangle. Double dOriginalVertexRectangleWithPaddingX = oVertexRectangleWithPadding.X; Double dOriginalVertexRectangleWithPaddingY = oVertexRectangleWithPadding.Y; MoveVertexIfNecessary(oVertex, ref oVertexRectangleWithPadding, oGraphDrawingContext); oVertexRectangle.Offset( oVertexRectangleWithPadding.X - dOriginalVertexRectangleWithPaddingX, oVertexRectangleWithPadding.Y - dOriginalVertexRectangleWithPaddingY ); // Draw the padded rectangle, then the text. oDrawingContext.DrawRectangle( GetBrush(oFillColor), GetPen(oOutlineColor, DefaultPenThickness), oVertexRectangleWithPadding); oDrawingContext.DrawText(oFormattedText, oVertexRectangle.Location); // Return information about how the vertex was drawn. return ( new LabelVertexDrawingHistory(oVertex, oDrawingVisual, bDrawAsSelected, oVertexRectangleWithPadding) ); }
DrawLabel ( DrawingContext oDrawingContext, GraphDrawingContext oGraphDrawingContext, Point oEdgeEndpoint1, Point oEdgeEndpoint2, String sLabel, Color oColor ) { Debug.Assert(oDrawingContext != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(sLabel != null); AssertValid(); if (sLabel.Length == 0) { return; } sLabel = TruncateLabel(sLabel); if (oEdgeEndpoint2.X < oEdgeEndpoint1.X) { // Don't let text be drawn upside-down. Point oTemp = oEdgeEndpoint2; oEdgeEndpoint2 = oEdgeEndpoint1; oEdgeEndpoint1 = oTemp; } Double dEdgeAngleDegrees = MathUtil.RadiansToDegrees( WpfGraphicsUtil.GetAngleBetweenPoints( oEdgeEndpoint1, oEdgeEndpoint2)); Double dEdgeLength = WpfGraphicsUtil.GetDistanceBetweenPoints( oEdgeEndpoint1, oEdgeEndpoint2); // To avoid trigonometric calculations, use a RotateTransform to make // the edge look as if it is horizontal, with oEdgeEndpoint2 to the // right of oEdgeEndpoint1. RotateTransform oRotateTransform = new RotateTransform( dEdgeAngleDegrees, oEdgeEndpoint1.X, oEdgeEndpoint1.Y); oEdgeEndpoint2 = oRotateTransform.Transform(oEdgeEndpoint2); oRotateTransform.Angle = -dEdgeAngleDegrees; oDrawingContext.PushTransform(oRotateTransform); FormattedText oFormattedText = CreateFormattedText(sLabel, oColor); oFormattedText.Trimming = TextTrimming.CharacterEllipsis; if (sLabel.IndexOf('\n') == -1) { // Unless the label includes line breaks, don't allow the // FormattedText class to break the text into multiple lines. oFormattedText.MaxLineCount = 1; } Double dTextWidth = oFormattedText.Width; // The ends of the label text are between one and two "buffer units" // from the ends of the edge. The buffer unit is the width of an // arbitrary character. Double dBufferUnit = CreateFormattedText("i", oColor).Width; Double dEdgeLengthMinusBuffers = dEdgeLength - 2 * dBufferUnit; if (dEdgeLengthMinusBuffers <= 0) { return; } // Determine where to draw the label text, and where to draw a // translucent rectangle behind the text. The translucent rectangle // serves to obscure, but not completely hide, the underlying edge. Point oLabelOrigin = oEdgeEndpoint1; Rect oTranslucentRectangle; if (dTextWidth > dEdgeLengthMinusBuffers) { // The label text should start one buffer unit from the first // endpoint, and terminate with ellipses approximately one buffer // length from the second endpoint. oLabelOrigin.Offset(dBufferUnit, 0); oFormattedText.MaxTextWidth = dEdgeLengthMinusBuffers; // The translucent rectangle should be the same width as the text. oTranslucentRectangle = new Rect(oLabelOrigin, new Size(dEdgeLengthMinusBuffers, oFormattedText.Height)); } else { // The label should be centered along the edge's length. oLabelOrigin.Offset(dEdgeLength / 2.0, 0); oFormattedText.TextAlignment = TextAlignment.Center; // The translucent rectangle should extend between zero and one // buffer units beyond the ends of the text. This provides a // margin between the text and the unobscured edge, if there is // enough space. oTranslucentRectangle = new Rect(oLabelOrigin, new Size( Math.Min(dTextWidth + 2 * dBufferUnit, dEdgeLengthMinusBuffers), oFormattedText.Height) ); oTranslucentRectangle.Offset( -oTranslucentRectangle.Width / 2.0, 0); } // The text and rectangle should be vertically centered on the edge. oDrawingContext.PushTransform( new TranslateTransform(0, -oFormattedText.Height / 2.0)); // Draw the translucent rectangle, then the text. // // Note: Don't make the rectangle any more opaque than the edge, which // might be translucent itself. oDrawingContext.DrawRectangle(GetBrush( WpfGraphicsUtil.SetWpfColorAlpha(oGraphDrawingContext.BackColor, Math.Min(LabelBackgroundAlpha, oColor.A)) ), null, oTranslucentRectangle); oDrawingContext.DrawText(oFormattedText, oLabelOrigin); oDrawingContext.Pop(); oDrawingContext.Pop(); }
//************************************************************************* // Method: CheckDrawVertexArguments() // /// <summary> /// Checks the arguments to <see cref="TryDrawVertex" />. /// </summary> /// /// <param name="oVertex"> /// The vertex that will eventually be drawn. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <remarks> /// An exception is thrown if one of the arguments is invalid. /// </remarks> //************************************************************************* protected void CheckDrawVertexArguments( IVertex oVertex, GraphDrawingContext oGraphDrawingContext ) { AssertValid(); const String MethodName = "TryDrawVertex"; const String VertexArgumentName = "vertex"; ArgumentChecker oArgumentChecker = this.ArgumentChecker; oArgumentChecker.CheckArgumentNotNull(MethodName, VertexArgumentName, oVertex); oArgumentChecker.CheckArgumentNotNull(MethodName, "graphDrawingContext", oGraphDrawingContext); if (oVertex.ParentGraph == null) { oArgumentChecker.ThrowArgumentException( MethodName, VertexArgumentName, "The vertex doesn't belong to a graph. It can't be drawn." ); } }
//************************************************************************* // Method: DrawGraph() // /// <summary> /// Draws a laid-out graph onto the contained collection of Visual objects. /// </summary> /// /// <param name="graph"> /// The graph to draw onto the contained collection of Visual objects. The /// graph should have already been laid out. You can use one of the /// supplied layout classes to do this. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <remarks> /// If you want to add a Visual on top of the graph, call <see /// cref="AddVisualOnTopOfGraph" /> after this method returns. /// /// <para> /// The collection of Visual objects is accessible via the <see /// cref="GraphDrawer.VisualCollection" /> property. /// </para> /// /// </remarks> //************************************************************************* public void DrawGraph( IGraph graph, GraphDrawingContext graphDrawingContext ) { Debug.Assert(graph != null); Debug.Assert(graphDrawingContext != null); AssertValid(); // Implementation note: // // In a previous GDI+ implementation of this graph drawer, the edges // had to be drawn first to allow the vertices to cover the ends of the // edges. That required a complex three-step drawing process: 1) allow // the vertex drawer to move each vertex if necessary to prevent the // vertex from falling outside the graph rectangle; 2) draw the edges // using the moved vertex locations; and 3) draw the vertices. // // This WPF implementation is simpler, for two reasons: // // 1. WPF uses retained-mode graphics, so covering the ends of the // edges can be accomplished simply by adding // m_oUnselectedEdgeDrawingVisuals to m_oVisualCollection before // adding m_oAllVertexDrawingVisuals. That means that the vertices // can be drawn onto m_oAllVertexDrawingVisuals first, and the // vertex drawer can move the vertices as necessary before drawing // them. A three-step process is no longer required. // // 2. The edges in this implementation don't actually need to be // covered, because they terminate at the vertex boundaries instead // of the vertex centers, as in the GDI+ implementation. m_oVisualCollection.Clear(); DrawBackground(graph, graphDrawingContext); m_oAllVertexDrawingVisuals = new DrawingVisual(); m_oUnselectedEdgeDrawingVisuals = new DrawingVisual(); m_oSelectedEdgeDrawingVisuals = new DrawingVisual(); // Draw the vertices after moving them if necessary. Each vertex needs // to be individually hit-tested and possibly redrawn by // RedrawVertex(), so each vertex is put into its own DrawingVisual // that becomes a child of m_oAllVertexDrawingVisuals. foreach (IVertex oVertex in graph.Vertices) { DrawVertex(oVertex, graphDrawingContext); } // Draw the edges. The edges don't need to be hit-tested, but they // might need to be redrawn by RedrawEdge(), so each edge is put into // its own DrawingVisual that becomes a child of either // m_oUnselectedEdgeDrawingVisuals or m_oSelectedEdgeDrawingVisuals. foreach (IEdge oEdge in graph.Edges) { DrawEdge(oEdge, graphDrawingContext); } // Selected edges need to be drawn on top of all the vertices // (including selected vertices) to guarantee that they will be // visible; hence the addition order seen here. m_oVisualCollection.Add(m_oUnselectedEdgeDrawingVisuals); m_oVisualCollection.Add(m_oAllVertexDrawingVisuals); m_oVisualCollection.Add(m_oSelectedEdgeDrawingVisuals); }
//************************************************************************* // Method: DrawNewVertex() // /// <summary> /// Draws a vertex that has been added to the graph but not yet drawn by /// <see cref="DrawGraph" />. /// </summary> /// /// <param name="newVertex"> /// The new vertex to draw onto the contained collection of Visual objects. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. This /// must be the same object that was passed to <see cref="DrawGraph" /> the /// last time the entire graph was drawn. /// </param> /// /// <remarks> /// Use this method to draw a new vertex without incurring the overhead of /// redrawing the entire graph. /// </remarks> //************************************************************************* public void DrawNewVertex( IVertex newVertex, GraphDrawingContext graphDrawingContext ) { Debug.Assert(newVertex != null); Debug.Assert(graphDrawingContext != null); AssertValid(); DrawVertex(newVertex, graphDrawingContext); }
//************************************************************************* // Method: DrawLabel() // /// <summary> /// Draws a vertex label as an annotation. /// </summary> /// /// <summary> /// Draws a vertex label as an annotation at a position determined by the /// vertex's metadata. /// </summary> /// /// <param name="drawingContext"> /// The DrawingContext to use. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="vertexDrawingHistory"> /// Describes how the vertex was drawn. /// </param> /// /// <param name="vertexBounds"> /// The vertex's bounding rectangle. /// </param> /// /// <param name="formattedText"> /// The FormattedText object to use. Several properties get changed by /// this method. /// </param> //************************************************************************* public void DrawLabel( DrawingContext drawingContext, GraphDrawingContext graphDrawingContext, VertexDrawingHistory vertexDrawingHistory, Rect vertexBounds, FormattedText formattedText ) { Debug.Assert(drawingContext != null); Debug.Assert(graphDrawingContext != null); Debug.Assert(vertexDrawingHistory != null); Debug.Assert(formattedText != null); AssertValid(); DrawLabel(drawingContext, graphDrawingContext, vertexDrawingHistory, vertexBounds, GetLabelPosition(vertexDrawingHistory.Vertex), formattedText); }
//************************************************************************* // Method: DrawEdge() // /// <summary> /// Draws an edge onto the contained collection of Visual objects. /// </summary> /// /// <param name="oEdge"> /// The edge to draw onto the contained collection of Visual objects. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <remarks> /// This method adds a DrawingVisual for the edge to either /// m_oUnselectedEdgeDrawingVisuals or m_oSelectedEdgeDrawingVisuals, and /// adds an EdgeDrawingHistory to /// oGraphDrawingContext.EdgeDrawingHistories. /// </remarks> //************************************************************************* protected void DrawEdge( IEdge oEdge, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oEdge != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); EdgeDrawingHistory oEdgeDrawingHistory; Dictionary<Int32, EdgeDrawingHistory> oEdgeDrawingHistories = oGraphDrawingContext.EdgeDrawingHistories; if ( m_oEdgeDrawer.TryDrawEdge(oEdge, oGraphDrawingContext, out oEdgeDrawingHistory) ) { Debug.Assert(oEdgeDrawingHistory != null); GetEdgeDrawingVisuals(oEdgeDrawingHistory).Children.Add( oEdgeDrawingHistory.DrawingVisual); // Save the EdgeDrawingHistory object. oEdgeDrawingHistories.Add(oEdge.ID, oEdgeDrawingHistory); } }
//************************************************************************* // Method: RedrawVertex() // /// <summary> /// Redraws a vertex that was drawn by <see cref="DrawGraph" />. /// </summary> /// /// <param name="vertex"> /// The vertex to redraw onto the contained collection of Visual objects. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. This /// must be the same object that was passed to <see cref="DrawGraph" /> the /// last time the entire graph was drawn. /// </param> /// /// <remarks> /// Use this method to redraw a vertex whose attributes (such as its /// selected state) have changed without incurring the overhead of /// redrawing the entire graph. /// </remarks> //************************************************************************* public void RedrawVertex( IVertex vertex, GraphDrawingContext graphDrawingContext ) { Debug.Assert(vertex != null); Debug.Assert(graphDrawingContext != null); AssertValid(); UndrawVertex(vertex, graphDrawingContext); DrawVertex(vertex, graphDrawingContext); }
//************************************************************************* // Method: UndrawVertex() // /// <summary> /// "Undraws" a vertex that was drawn by <see cref="DrawGraph" />. /// </summary> /// /// <param name="vertex"> /// The vertex to remove from the contained collection of Visual objects. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. This /// must be the same object that was passed to <see cref="DrawGraph" /> the /// last time the entire graph was drawn. /// </param> /// /// <remarks> /// Use this method to undraw a vertex without incurring the overhead of /// redrawing the entire graph. /// </remarks> //************************************************************************* public void UndrawVertex( IVertex vertex, GraphDrawingContext graphDrawingContext ) { Debug.Assert(vertex != null); Debug.Assert(graphDrawingContext != null); AssertValid(); // Retrieve the VertexDrawingHistory object for the vertex, if one // exists. (If the vertex was previously hidden, there won't be a // VertexDrawingHistory object for it.) Dictionary<Int32, VertexDrawingHistory> oVertexDrawingHistories = graphDrawingContext.VertexDrawingHistories; Int32 iVertexID = vertex.ID; VertexDrawingHistory oVertexDrawingHistory; if ( oVertexDrawingHistories.TryGetValue( iVertexID, out oVertexDrawingHistory) ) { // Remove the VertexDrawingHistory object from the dictionary. oVertexDrawingHistories.Remove(iVertexID); // Remove the vertex's DrawingVisual object, which will cause the // vertex to disappear. m_oAllVertexDrawingVisuals.Children.Remove( oVertexDrawingHistory.DrawingVisual); } }
//************************************************************************* // Method: CreateVisual() // /// <summary> /// Creates the Visual that should be used to represent the dragged /// vertices. /// </summary> /// /// <param name="currentMouseLocation"> /// The current mouse location. /// </param> /// /// <param name="backColor"> /// The graph's background color. /// </param> /// /// <param name="vertexDrawer"> /// The VertexDrawer that should be used to draw the dragged vertices. /// </param> /// /// <returns> /// The Visual that should be used to represent the dragged vertices. /// </returns> /// /// <remarks> /// The returned Visual can be retrieved later via the <see /// cref="MouseDragWithVisual.Visual" /> property. /// </remarks> //************************************************************************* public Visual CreateVisual( Point currentMouseLocation, Color backColor, VertexDrawer vertexDrawer ) { Debug.Assert(vertexDrawer != null); Debug.Assert(m_bDragIsInProgress); AssertValid(); // This method redraws the dragged vertices at an offset location, and // adds the resulting Visuals to a ContainerVisual. // // Figure out the offset. Double dOffsetX = currentMouseLocation.X - m_oMouseDownLocation.X; Double dOffsetY = currentMouseLocation.Y - m_oMouseDownLocation.Y; GraphDrawingContext oGraphDrawingContext = new GraphDrawingContext( m_oGraphRectangle, m_iMargin, backColor); ContainerVisual oContainerVisual = new ContainerVisual(); foreach (IVertex oVertex in m_aoVertices) { System.Drawing.PointF oOriginalLocation = GetOriginalVertexLocation(oVertex); oVertex.Location = new System.Drawing.PointF( oOriginalLocation.X + (Single)dOffsetX, oOriginalLocation.Y + (Single)dOffsetY ); VertexDrawingHistory oVertexDrawingHistory; if ( vertexDrawer.TryDrawVertex(oVertex, oGraphDrawingContext, out oVertexDrawingHistory) ) { oContainerVisual.Children.Add( oVertexDrawingHistory.DrawingVisual); } } m_oVisual = oContainerVisual; return (m_oVisual); }
//************************************************************************* // Method: DrawLabel() // /// <summary> /// Draws a vertex label as an annotation at a specified position. /// </summary> /// /// <param name="drawingContext"> /// The DrawingContext to use. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="vertexDrawingHistory"> /// Describes how the vertex was drawn. /// </param> /// /// <param name="vertexBounds"> /// The vertex's bounding rectangle. /// </param> /// /// <param name="labelPosition"> /// The label's position. /// </param> /// /// <param name="formattedText"> /// The FormattedText object to use. Several properties get changed by /// this method. /// </param> //************************************************************************* public void DrawLabel( DrawingContext drawingContext, GraphDrawingContext graphDrawingContext, VertexDrawingHistory vertexDrawingHistory, Rect vertexBounds, VertexLabelPosition labelPosition, FormattedText formattedText ) { Debug.Assert(drawingContext != null); Debug.Assert(graphDrawingContext != null); Debug.Assert(vertexDrawingHistory != null); Debug.Assert(formattedText != null); AssertValid(); Double dHalfVertexBoundsWidth = vertexBounds.Width / 2.0; Double dHalfVertexBoundsHeight = vertexBounds.Height / 2.0; Double dLabelHeight = formattedText.Height; Double dHalfLabelHeight = dLabelHeight / 2.0; Double dLabelWidth = formattedText.Width; Double dHalfLabelWidth = dLabelWidth / 2.0; TextAlignment eTextAlignment = TextAlignment.Left; formattedText.MaxLineCount = 1; // This is the point where the label will be drawn. It initially // assumes a text height of zero with no margin, but that will be // adjusted within the switch statement below. Point oDraw = vertexDrawingHistory.GetLabelLocation(labelPosition); Double dDrawX = oDraw.X; Double dDrawY = oDraw.Y; // These are the bounds of the label text. Double dLabelBoundsLeft = 0; Double dLabelBoundsRight = 0; switch (labelPosition) { case VertexLabelPosition.TopLeft: eTextAlignment = TextAlignment.Right; dDrawY -= (dLabelHeight + VerticalMargin); dLabelBoundsLeft = dDrawX - dLabelWidth; dLabelBoundsRight = dDrawX; break; case VertexLabelPosition.TopCenter: eTextAlignment = TextAlignment.Center; dDrawY -= (dLabelHeight + VerticalMargin); dLabelBoundsLeft = dDrawX - dHalfLabelWidth; dLabelBoundsRight = dDrawX + dHalfLabelWidth; break; case VertexLabelPosition.TopRight: // eTextAlignment = TextAlignment.Left; dDrawY -= (dLabelHeight + VerticalMargin); dLabelBoundsLeft = dDrawX; dLabelBoundsRight = dDrawX + dLabelWidth; break; case VertexLabelPosition.MiddleLeft: eTextAlignment = TextAlignment.Right; dDrawX -= HorizontalMargin; dDrawY -= dHalfLabelHeight; dLabelBoundsLeft = dDrawX - dLabelWidth; dLabelBoundsRight = dDrawX; break; case VertexLabelPosition.MiddleCenter: eTextAlignment = TextAlignment.Center; dDrawY -= dHalfLabelHeight; dLabelBoundsLeft = dDrawX - dHalfLabelWidth; dLabelBoundsRight = dDrawX + dHalfLabelWidth; break; case VertexLabelPosition.MiddleRight: // eTextAlignment = TextAlignment.Left; dDrawX += HorizontalMargin; dDrawY -= dHalfLabelHeight; dLabelBoundsLeft = dDrawX; dLabelBoundsRight = dDrawX + dLabelWidth; break; case VertexLabelPosition.BottomLeft: eTextAlignment = TextAlignment.Right; dDrawY += VerticalMargin; dLabelBoundsLeft = dDrawX - dLabelWidth; dLabelBoundsRight = dDrawX; break; case VertexLabelPosition.BottomCenter: eTextAlignment = TextAlignment.Center; dDrawY += VerticalMargin; dLabelBoundsLeft = dDrawX - dHalfLabelWidth; dLabelBoundsRight = dDrawX + dHalfLabelWidth; break; case VertexLabelPosition.BottomRight: // eTextAlignment = TextAlignment.Left; dDrawY += VerticalMargin; dLabelBoundsLeft = dDrawX; dLabelBoundsRight = dDrawX + dLabelWidth; break; default: Debug.Assert(false); break; } // Don't let the text exceed the bounds of the graph rectangle. Double dLabelBoundsTop = dDrawY; Double dLabelBoundsBottom = dDrawY + dLabelHeight; Rect oGraphRectangleMinusMargin = graphDrawingContext.GraphRectangleMinusMargin; dDrawX += Math.Max(0, oGraphRectangleMinusMargin.Left - dLabelBoundsLeft); dDrawX -= Math.Max(0, dLabelBoundsRight - oGraphRectangleMinusMargin.Right); dDrawY += Math.Max(0, oGraphRectangleMinusMargin.Top - dLabelBoundsTop); dDrawY -= Math.Max(0, dLabelBoundsBottom - oGraphRectangleMinusMargin.Bottom); formattedText.TextAlignment = eTextAlignment; drawingContext.DrawText( formattedText, new Point(dDrawX, dDrawY) ); }
DrawSelfLoopAt ( DrawingContext oDrawingContext, GraphDrawingContext oGraphDrawingContext, Color oColor, Double dWidth, Point oSelfLoopEndpoint, RectangleEdge eFarthestGraphRectangleEdge, Boolean bDrawArrow ) { Debug.Assert(oDrawingContext != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(dWidth >= 0); AssertValid(); // The self-loop is drawn as a circle. Figure out the location of the // circle's center and the tip of the arrow, if there is an arrow. Double dCircleX, dCircleY, dArrowTipX, dArrowTipY, dArrowAngle; dCircleX = dArrowTipX = oSelfLoopEndpoint.X; dCircleY = dArrowTipY = oSelfLoopEndpoint.Y; Double dSelfLoopCircleDiameter = 2.0 * SelfLoopCircleRadius; dArrowAngle = 0; switch (eFarthestGraphRectangleEdge) { case RectangleEdge.Top: dCircleY -= SelfLoopCircleRadius; dArrowTipY -= dSelfLoopCircleDiameter; break; case RectangleEdge.Left: dCircleX -= SelfLoopCircleRadius; dArrowTipX -= dSelfLoopCircleDiameter; dArrowAngle = Math.PI / 2.0; // (90 degrees.) break; case RectangleEdge.Right: dCircleX += SelfLoopCircleRadius; dArrowTipX += dSelfLoopCircleDiameter; dArrowAngle = -Math.PI / 2.0; // (-90 degrees.) break; case RectangleEdge.Bottom: dCircleY += SelfLoopCircleRadius; dArrowTipY += dSelfLoopCircleDiameter; dArrowAngle = Math.PI; // (180 degrees.) break; default: Debug.Assert(false); break; } oDrawingContext.DrawEllipse(null, GetPen(oColor, dWidth), new Point(dCircleX, dCircleY), SelfLoopCircleRadius, SelfLoopCircleRadius); if (bDrawArrow) { // Rotate the arrow slightly to adjust to the circular shape of the // edge connected to it. dArrowAngle += Math.PI / 13.0; DrawArrow(oDrawingContext, new Point(dArrowTipX, dArrowTipY), dArrowAngle, oColor, dWidth); } }
DrawLabel ( DrawingContext drawingContext, GraphDrawingContext graphDrawingContext, VertexDrawingHistory vertexDrawingHistory, Rect vertexBounds, VertexLabelPosition labelPosition, FormattedText formattedText ) { Debug.Assert(drawingContext != null); Debug.Assert(graphDrawingContext != null); Debug.Assert(vertexDrawingHistory != null); Debug.Assert(formattedText != null); AssertValid(); Double dHalfVertexBoundsWidth = vertexBounds.Width / 2.0; Double dHalfVertexBoundsHeight = vertexBounds.Height / 2.0; Double dLabelHeight = formattedText.Height; Double dHalfLabelHeight = dLabelHeight / 2.0; Double dLabelWidth = formattedText.Width; Double dHalfLabelWidth = dLabelWidth / 2.0; TextAlignment eTextAlignment = TextAlignment.Left; formattedText.MaxLineCount = 1; // This is the point where the label will be drawn. It initially // assumes a text height of zero with no margin, but that will be // adjusted within the switch statement below. Point oDraw = vertexDrawingHistory.GetLabelLocation(labelPosition); Double dDrawX = oDraw.X; Double dDrawY = oDraw.Y; // These are the bounds of the label text. Double dLabelBoundsLeft = 0; Double dLabelBoundsRight = 0; switch (labelPosition) { case VertexLabelPosition.TopLeft: eTextAlignment = TextAlignment.Right; dDrawY -= (dLabelHeight + VerticalMargin); dLabelBoundsLeft = dDrawX - dLabelWidth; dLabelBoundsRight = dDrawX; break; case VertexLabelPosition.TopCenter: eTextAlignment = TextAlignment.Center; dDrawY -= (dLabelHeight + VerticalMargin); dLabelBoundsLeft = dDrawX - dHalfLabelWidth; dLabelBoundsRight = dDrawX + dHalfLabelWidth; break; case VertexLabelPosition.TopRight: // eTextAlignment = TextAlignment.Left; dDrawY -= (dLabelHeight + VerticalMargin); dLabelBoundsLeft = dDrawX; dLabelBoundsRight = dDrawX + dLabelWidth; break; case VertexLabelPosition.MiddleLeft: eTextAlignment = TextAlignment.Right; dDrawX -= HorizontalMargin; dDrawY -= dHalfLabelHeight; dLabelBoundsLeft = dDrawX - dLabelWidth; dLabelBoundsRight = dDrawX; break; case VertexLabelPosition.MiddleCenter: eTextAlignment = TextAlignment.Center; dDrawY -= dHalfLabelHeight; dLabelBoundsLeft = dDrawX - dHalfLabelWidth; dLabelBoundsRight = dDrawX + dHalfLabelWidth; break; case VertexLabelPosition.MiddleRight: // eTextAlignment = TextAlignment.Left; dDrawX += HorizontalMargin; dDrawY -= dHalfLabelHeight; dLabelBoundsLeft = dDrawX; dLabelBoundsRight = dDrawX + dLabelWidth; break; case VertexLabelPosition.BottomLeft: eTextAlignment = TextAlignment.Right; dDrawY += VerticalMargin; dLabelBoundsLeft = dDrawX - dLabelWidth; dLabelBoundsRight = dDrawX; break; case VertexLabelPosition.BottomCenter: eTextAlignment = TextAlignment.Center; dDrawY += VerticalMargin; dLabelBoundsLeft = dDrawX - dHalfLabelWidth; dLabelBoundsRight = dDrawX + dHalfLabelWidth; break; case VertexLabelPosition.BottomRight: // eTextAlignment = TextAlignment.Left; dDrawY += VerticalMargin; dLabelBoundsLeft = dDrawX; dLabelBoundsRight = dDrawX + dLabelWidth; break; default: Debug.Assert(false); break; } // Don't let the text exceed the bounds of the graph rectangle. Double dLabelBoundsTop = dDrawY; Double dLabelBoundsBottom = dDrawY + dLabelHeight; Rect oGraphRectangleMinusMargin = graphDrawingContext.GraphRectangleMinusMargin; dDrawX += Math.Max(0, oGraphRectangleMinusMargin.Left - dLabelBoundsLeft); dDrawX -= Math.Max(0, dLabelBoundsRight - oGraphRectangleMinusMargin.Right); dDrawY += Math.Max(0, oGraphRectangleMinusMargin.Top - dLabelBoundsTop); dDrawY -= Math.Max(0, dLabelBoundsBottom - oGraphRectangleMinusMargin.Bottom); formattedText.TextAlignment = eTextAlignment; drawingContext.DrawText(formattedText, new Point(dDrawX, dDrawY)); }
//************************************************************************* // Method: DrawPlusSign() // /// <summary> /// Draws a plus sign on top of the vertex. /// </summary> /// /// <param name="eShape"> /// The simple vertex shape. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="oVertexLocation"> /// The location of the vertex. /// </param> /// /// <param name="oDrawingContext"> /// The DrawingContext to use. /// </param> /// /// <param name="oVertexColor"> /// The color of the vertex. /// </param> /// /// <param name="oVertexLabelDrawer"> /// Object that draws a vertex label as an annotation. /// </param> /// /// <param name="oVertexDrawingHistory"> /// A <see cref="VertexDrawingHistory" /> object that retains information /// about how the vertex was drawn. /// </param> /// /// <param name="oVertexBounds"> /// The rectangle defining the bounds of the vertex. /// </param> //************************************************************************* protected void DrawPlusSign( VertexShape eShape, Point oVertexLocation, Rect oVertexBounds, Color oVertexColor, GraphDrawingContext oGraphDrawingContext, DrawingContext oDrawingContext, VertexLabelDrawer oVertexLabelDrawer, VertexDrawingHistory oVertexDrawingHistory ) { Debug.Assert(oGraphDrawingContext != null); Debug.Assert(oDrawingContext != null); Debug.Assert(oVertexLabelDrawer != null); Debug.Assert(oVertexDrawingHistory != null); AssertValid(); Color oFillColor; switch (eShape) { case VertexShape.Circle: case VertexShape.Square: case VertexShape.Diamond: case VertexShape.Triangle: // The fill color is the color of the background. Adjust the // fill color for the opacity of the vertex. oFillColor = WpfGraphicsUtil.SetWpfColorAlpha( oGraphDrawingContext.BackColor, oVertexColor.A); break; default: oFillColor = oVertexColor; break; } Color oContrastingColor = WpfGraphicsUtil.GetContrastingColor(oFillColor); // The font size used below was chosen so that it is large enough to be // easily readable, but small enough to fit within the smallest // collapsed group vertex created by the NodeXLControl. oVertexLabelDrawer.DrawLabel( oDrawingContext, oGraphDrawingContext, oVertexDrawingHistory, oVertexBounds, VertexLabelPosition.MiddleCenter, CreateFormattedText("+", oContrastingColor, 15.0) ); }
DrawGraph ( IGraph graph, GraphDrawingContext graphDrawingContext ) { Debug.Assert(graph != null); Debug.Assert(graphDrawingContext != null); AssertValid(); // Implementation note: // // In a previous GDI+ implementation of this graph drawer, the edges // had to be drawn first to allow the vertices to cover the ends of the // edges. That required a complex three-step drawing process: 1) allow // the vertex drawer to move each vertex if necessary to prevent the // vertex from falling outside the graph rectangle; 2) draw the edges // using the moved vertex locations; and 3) draw the vertices. // // This WPF implementation is simpler, for two reasons: // // 1. WPF uses retained-mode graphics, so covering the ends of the // edges can be accomplished simply by adding // m_oUnselectedEdgeDrawingVisuals to m_oVisualCollection before // adding m_oAllVertexDrawingVisuals. That means that the vertices // can be drawn onto m_oAllVertexDrawingVisuals first, and the // vertex drawer can move the vertices as necessary before drawing // them. A three-step process is no longer required. // // 2. The edges in this implementation don't actually need to be // covered, because they terminate at the vertex boundaries instead // of the vertex centers, as in the GDI+ implementation. m_oVisualCollection.Clear(); DrawBackground(graph, graphDrawingContext); m_oAllVertexDrawingVisuals = new DrawingVisual(); m_oUnselectedEdgeDrawingVisuals = new DrawingVisual(); m_oSelectedEdgeDrawingVisuals = new DrawingVisual(); // Draw the vertices after moving them if necessary. Each vertex needs // to be individually hit-tested and possibly redrawn by // RedrawVertex(), so each vertex is put into its own DrawingVisual // that becomes a child of m_oAllVertexDrawingVisuals. foreach (IVertex oVertex in graph.Vertices) { DrawVertex(oVertex, graphDrawingContext); } // Draw the edges. The edges don't need to be hit-tested, but they // might need to be redrawn by RedrawEdge(), so each edge is put into // its own DrawingVisual that becomes a child of either // m_oUnselectedEdgeDrawingVisuals or m_oSelectedEdgeDrawingVisuals. foreach (IEdge oEdge in graph.Edges) { DrawEdge(oEdge, graphDrawingContext); } // Selected edges need to be drawn on top of all the vertices // (including selected vertices) to guarantee that they will be // visible; hence the addition order seen here. m_oVisualCollection.Add(m_oUnselectedEdgeDrawingVisuals); m_oVisualCollection.Add(m_oAllVertexDrawingVisuals); m_oVisualCollection.Add(m_oSelectedEdgeDrawingVisuals); }
//************************************************************************* // Method: DrawSimpleShape() // /// <summary> /// Draws a vertex as a simple shape. /// </summary> /// /// <param name="oVertex"> /// The vertex to draw. /// </param> /// /// <param name="eShape"> /// The vertex shape to use. Can't be <see cref="VertexShape.Image" /> or /// <see cref="VertexShape.Label" />. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="oDrawingContext"> /// The DrawingContext to use. /// </param> /// /// <param name="oDrawingVisual"> /// The <see cref="DrawingVisual" /> object from which <paramref /// name="oDrawingContext" /> was obtained. /// </param> /// /// <param name="eVisibility"> /// The visibility of the vertex. /// </param> /// /// <param name="bDrawAsSelected"> /// true to draw the vertex as selected. /// </param> /// /// <param name="sAnnotation"> /// The annotation to draw next to the shape, or null if there is no /// annotation. /// </param> /// /// <param name="oVertexLabelDrawer"> /// Object that draws a vertex label as an annotation. /// </param> /// /// <returns> /// A VertexDrawingHistory object that retains information about how the /// vertex was drawn. /// </returns> /// /// <remarks> /// "Simple" means "not <see cref="VertexShape.Image" /> and not <see /// cref="VertexShape.Label" />." /// </remarks> //************************************************************************* protected VertexDrawingHistory DrawSimpleShape( IVertex oVertex, VertexShape eShape, GraphDrawingContext oGraphDrawingContext, DrawingContext oDrawingContext, DrawingVisual oDrawingVisual, VisibilityKeyValue eVisibility, Boolean bDrawAsSelected, String sAnnotation, VertexLabelDrawer oVertexLabelDrawer ) { Debug.Assert(oVertex != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(oDrawingContext != null); Debug.Assert(oDrawingVisual != null); Debug.Assert(oVertexLabelDrawer != null); AssertValid(); Double dRadius = GetRadius(oVertex); Color oColor = GetColor(oVertex, eVisibility, bDrawAsSelected); Point oVertexLocation = GetVertexLocation(oVertex); Rect oVertexBounds; if (eShape == VertexShape.Triangle || eShape == VertexShape.SolidTriangle) { oVertexBounds = WpfGraphicsUtil.TriangleBoundsFromCenterAndHalfWidth( oVertexLocation, dRadius); } else { oVertexBounds = WpfGraphicsUtil.SquareFromCenterAndHalfWidth( oVertexLocation, dRadius); } // Move the vertex if it falls outside the graph rectangle. MoveVertexIfNecessary(oVertex, ref oVertexBounds, oGraphDrawingContext); Point oLocation = GetVertexLocation(oVertex); VertexDrawingHistory oVertexDrawingHistory = null; // Note that for the "hollow" shapes -- Circle, Square, Diamond, and // Triangle -- Brushes.Transparent is used instead of a null brush. // This allows the entire area of these shapes to be hit-tested. Using // a null brush would cause hit-testing to fail if the shapes' // interiors were clicked. switch (eShape) { case VertexShape.Circle: case VertexShape.Disk: Boolean bIsDisk = (eShape == VertexShape.Disk); oDrawingContext.DrawEllipse( bIsDisk ? GetBrush(oColor) : Brushes.Transparent, bIsDisk ? null : GetPen(oColor, DefaultPenThickness), oLocation, dRadius, dRadius ); oVertexDrawingHistory = bIsDisk ? new DiskVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius) : new CircleVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius); break; case VertexShape.Sphere: RadialGradientBrush oRadialGradientBrush = new RadialGradientBrush(); oRadialGradientBrush.GradientOrigin = oRadialGradientBrush.Center = new Point(0.3, 0.3); GradientStopCollection oGradientStops = oRadialGradientBrush.GradientStops; oGradientStops.Add( new GradientStop(Colors.White, 0.0) ); oGradientStops.Add( new GradientStop(oColor, 1.0) ); WpfGraphicsUtil.FreezeIfFreezable(oRadialGradientBrush); oDrawingContext.DrawEllipse(oRadialGradientBrush, null, oLocation, dRadius, dRadius); oVertexDrawingHistory = new SphereVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius); break; case VertexShape.Square: case VertexShape.SolidSquare: Boolean bIsSolidSquare = (eShape == VertexShape.SolidSquare); oDrawingContext.DrawRectangle( bIsSolidSquare ? GetBrush(oColor) : Brushes.Transparent, bIsSolidSquare? null : GetPen(oColor, DefaultPenThickness), oVertexBounds ); oVertexDrawingHistory = bIsSolidSquare ? new SolidSquareVertexDrawingHistory(oVertex, oDrawingVisual, bDrawAsSelected, oVertexBounds) : new SquareVertexDrawingHistory(oVertex, oDrawingVisual, bDrawAsSelected, oVertexBounds); break; case VertexShape.Diamond: case VertexShape.SolidDiamond: Boolean bIsSolidDiamond = (eShape == VertexShape.SolidDiamond); PathGeometry oDiamond = WpfGraphicsUtil.DiamondFromCenterAndHalfWidth( oLocation, dRadius); oDrawingContext.DrawGeometry( bIsSolidDiamond ? GetBrush(oColor) : Brushes.Transparent, bIsSolidDiamond ? null : GetPen(oColor, DefaultPenThickness), oDiamond ); oVertexDrawingHistory = bIsSolidDiamond ? new SolidDiamondVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius) : new DiamondVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius); break; case VertexShape.Triangle: case VertexShape.SolidTriangle: Boolean bIsSolidTriangle = (eShape == VertexShape.SolidTriangle); PathGeometry oTriangle = WpfGraphicsUtil.TriangleFromCenterAndHalfWidth( oLocation, dRadius); oDrawingContext.DrawGeometry( bIsSolidTriangle ? GetBrush(oColor) : Brushes.Transparent, bIsSolidTriangle ? null : GetPen(oColor, DefaultPenThickness), oTriangle ); oVertexDrawingHistory = bIsSolidTriangle ? new SolidTriangleVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius) : new TriangleVertexDrawingHistory( oVertex, oDrawingVisual, bDrawAsSelected, dRadius); break; default: Debug.Assert(false); break; } if (sAnnotation != null) { oVertexLabelDrawer.DrawLabel( oDrawingContext, oGraphDrawingContext, oVertexDrawingHistory, oVertexBounds, CreateFormattedText(sAnnotation, oColor) ); } if ( oVertex.ContainsKey(ReservedMetadataKeys.PerVertexDrawPlusSign) ) { DrawPlusSign(eShape, oVertexLocation, oVertexBounds, oColor, oGraphDrawingContext, oDrawingContext, oVertexLabelDrawer, oVertexDrawingHistory); } Debug.Assert(oVertexDrawingHistory != null); return (oVertexDrawingHistory); }
//************************************************************************* // Method: DrawVertex() // /// <summary> /// Draws a vertex onto the contained collection of Visual objects. /// </summary> /// /// <param name="oVertex"> /// The vertex to draw onto the contained collection of Visual objects. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <remarks> /// This method adds a DrawingVisual for the vertex to /// m_oAllVertexDrawingVisuals and adds a VertexDrawingHistory to /// oGraphDrawingContext.VertexDrawingHistories. /// </remarks> //************************************************************************* protected void DrawVertex( IVertex oVertex, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oVertex != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); VertexDrawingHistory oVertexDrawingHistory; Dictionary<Int32, VertexDrawingHistory> oVertexDrawingHistories = oGraphDrawingContext.VertexDrawingHistories; if ( m_oVertexDrawer.TryDrawVertex(oVertex, oGraphDrawingContext, out oVertexDrawingHistory) ) { Debug.Assert(oVertexDrawingHistory != null); // Add the DrawingVisual for the vertex to // m_oAllVertexDrawingVisuals, and add the VertexDrawingHistory to // the oGraphDrawingContext.VertexDrawingHistories dictionary. DrawingVisual oVertexChildDrawingVisual = oVertexDrawingHistory.DrawingVisual; m_oAllVertexDrawingVisuals.Children.Add(oVertexChildDrawingVisual); oVertexDrawingHistories.Add(oVertex.ID, oVertexDrawingHistory); // Save the vertex on the DrawingVisual for later retrieval. SaveVertexOnDrawingVisual(oVertex, oVertexChildDrawingVisual); } }
//************************************************************************* // Method: MoveVertexIfNecessary() // /// <summary> /// Moves a vertex if it falls within the graph rectangle's margin or /// outside the graph rectangle. /// </summary> /// /// <param name="oVertex"> /// The vertex to move. /// </param> /// /// <param name="oVertexBounds"> /// The rectangle defining the bounds of the vertex. This gets updated if /// the vertex is moved. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <remarks> /// If the vertex falls within the margin or outside the graph rectangle, /// the IVertex.Location property and <paramref name="oVertexBounds" /> get /// updated. /// </remarks> //************************************************************************* protected void MoveVertexIfNecessary( IVertex oVertex, ref Rect oVertexBounds, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oVertex != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); if (!m_bLimitVerticesToBounds) { // The vertex shouldn't be moved. return; } Rect oGraphRectangleMinusMargin = oGraphDrawingContext.GraphRectangleMinusMargin; if (oGraphRectangleMinusMargin.IsEmpty) { return; } Rect oMovedVertexBounds = WpfGraphicsUtil.MoveRectangleWithinBounds( oVertexBounds, oGraphRectangleMinusMargin, false); oVertex.Location = System.Drawing.PointF.Add( oVertex.Location, new System.Drawing.SizeF( (Single)(oMovedVertexBounds.X - oVertexBounds.X), (Single)(oMovedVertexBounds.Y - oVertexBounds.Y) ) ); oVertexBounds = oMovedVertexBounds; }
//************************************************************************* // Method: DrawNewEdge() // /// <summary> /// Draws an edge that has been added to the graph but not yet drawn by /// <see cref="DrawGraph" />. /// </summary> /// /// <param name="newEdge"> /// The new edge to draw onto the contained collection of Visual objects. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. This /// must be the same object that was passed to <see cref="DrawGraph" /> the /// last time the entire graph was drawn. /// </param> /// /// <remarks> /// Use this method to draw a new edge without incurring the overhead of /// redrawing the entire graph. /// </remarks> //************************************************************************* public void DrawNewEdge( IEdge newEdge, GraphDrawingContext graphDrawingContext ) { Debug.Assert(newEdge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); DrawEdge(newEdge, graphDrawingContext); }
//************************************************************************* // Method: TryDrawVertex() // /// <summary> /// Draws a vertex after moving it if necessary. /// </summary> /// /// <param name="vertex"> /// The vertex to draw. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> /// /// <param name="vertexDrawingHistory"> /// Where a <see cref="VertexDrawingHistory" /> object that retains /// information about how the vertex was drawn gets stored if true is /// returned. /// </param> /// /// <returns> /// true if the vertex was drawn, false if the vertex is hidden. /// </returns> /// /// <remarks> /// This method should be called repeatedly while a graph is being drawn, /// once for each of the graph's vertices. The <see /// cref="IVertex.Location" /> property on all of the graph's vertices must /// be set by ILayout.LayOutGraph before this method is called. /// /// <para> /// If the vertex falls outside the graph rectangle, it gets moved before /// being drawn. /// </para> /// /// </remarks> //************************************************************************* public Boolean TryDrawVertex( IVertex vertex, GraphDrawingContext graphDrawingContext, out VertexDrawingHistory vertexDrawingHistory ) { AssertValid(); vertexDrawingHistory = null; CheckDrawVertexArguments(vertex, graphDrawingContext); // If the vertex is hidden, do nothing. VisibilityKeyValue eVisibility = GetVisibility(vertex); if (eVisibility == VisibilityKeyValue.Hidden) { return (false); } // Check for a per-vertex label. Object oLabelAsObject; String sLabel = null; if ( vertex.TryGetValue(ReservedMetadataKeys.PerVertexLabel, typeof(String), out oLabelAsObject) ) { sLabel = (String)oLabelAsObject; if ( String.IsNullOrEmpty(sLabel) ) { sLabel = null; } else { sLabel = TruncateLabel(sLabel); } } Boolean bDrawAsSelected = GetDrawAsSelected(vertex); Point oLocation = WpfGraphicsUtil.PointFToWpfPoint(vertex.Location); DrawingVisual oDrawingVisual = new DrawingVisual(); VertexShape eShape = GetShape(vertex); VertexLabelDrawer oVertexLabelDrawer = new VertexLabelDrawer(m_eLabelPosition); using ( DrawingContext oDrawingContext = oDrawingVisual.RenderOpen() ) { if (eShape == VertexShape.Label) { if (sLabel != null) { // Draw the vertex as a label. vertexDrawingHistory = DrawLabelShape(vertex, graphDrawingContext, oDrawingContext, oDrawingVisual, eVisibility, bDrawAsSelected, sLabel); return (true); } // Default to something usable. eShape = VertexShape.Disk; } else if (eShape == VertexShape.Image) { Object oImageSourceAsObject; if (vertex.TryGetValue(ReservedMetadataKeys.PerVertexImage, typeof(ImageSource), out oImageSourceAsObject) ) { // Draw the vertex as an image. vertexDrawingHistory = DrawImageShape(vertex, graphDrawingContext, oDrawingContext, oDrawingVisual, eVisibility, bDrawAsSelected, sLabel, (ImageSource)oImageSourceAsObject, oVertexLabelDrawer); return (true); } // Default to something usable. eShape = VertexShape.Disk; } // Draw the vertex as a simple shape. vertexDrawingHistory = DrawSimpleShape(vertex, eShape, graphDrawingContext, oDrawingContext, oDrawingVisual, eVisibility, bDrawAsSelected, sLabel, oVertexLabelDrawer); } return (true); }
//************************************************************************* // Method: RedrawEdge() // /// <summary> /// Redraws an edge that was drawn by <see cref="DrawGraph" />. /// </summary> /// /// <param name="edge"> /// The edge to redraw onto the contained collection of Visual objects. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. This /// must be the same object that was passed to <see cref="DrawGraph" /> the /// last time the entire graph was drawn. /// </param> /// /// <remarks> /// Use this method to redraw an edge whose attributes (such as its /// selected state) have changed without incurring the overhead of /// redrawing the entire graph. /// </remarks> //************************************************************************* public void RedrawEdge( IEdge edge, GraphDrawingContext graphDrawingContext ) { Debug.Assert(edge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); UndrawEdge(edge, graphDrawingContext); DrawEdge(edge, graphDrawingContext); }
TryDrawEdge ( IEdge edge, GraphDrawingContext graphDrawingContext, out EdgeDrawingHistory edgeDrawingHistory ) { Debug.Assert(edge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); edgeDrawingHistory = null; // If the edge is hidden, do nothing. VisibilityKeyValue eVisibility = GetVisibility(edge); if (eVisibility == VisibilityKeyValue.Hidden) { return(false); } Boolean bDrawAsSelected = GetDrawAsSelected(edge); Color oColor = GetColor(edge, eVisibility, bDrawAsSelected); Double dWidth = GetWidth(edge, bDrawAsSelected); DashStyle oDashStyle = GetDashStyle(edge, dWidth, bDrawAsSelected); Boolean bDrawArrow = (m_bDrawArrowOnDirectedEdge && edge.IsDirected); IVertex [] aoVertices = edge.Vertices; DrawingVisual oDrawingVisual = new DrawingVisual(); using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen()) { if (edge.IsSelfLoop) { if (!TryDrawSelfLoop(aoVertices[0], oDrawingContext, graphDrawingContext, oColor, dWidth, bDrawArrow)) { // The edge's vertex is hidden, so the edge should be // hidden also. return(false); } } else { Point oEdgeEndpoint1, oEdgeEndpoint2; if (!TryGetEdgeEndpoints(aoVertices[0], aoVertices[1], graphDrawingContext, out oEdgeEndpoint1, out oEdgeEndpoint2)) { // One of the edge's vertices is hidden, so the edge should // be hidden also. return(false); } if (bDrawArrow) { // Draw the arrow and set the second endpoint to the center // of the flat end of the arrow. Double dArrowAngle = WpfGraphicsUtil.GetAngleBetweenPoints( oEdgeEndpoint1, oEdgeEndpoint2); oEdgeEndpoint2 = DrawArrow(oDrawingContext, oEdgeEndpoint2, dArrowAngle, oColor, dWidth); } // Draw the edge. oDrawingContext.DrawLine(GetPen(oColor, dWidth, oDashStyle), oEdgeEndpoint1, oEdgeEndpoint2); // Draw the edge's label, if there is one. Object oLabelAsObject; if (edge.TryGetValue(ReservedMetadataKeys.PerEdgeLabel, typeof(String), out oLabelAsObject) && oLabelAsObject != null) { DrawLabel(oDrawingContext, graphDrawingContext, oEdgeEndpoint1, oEdgeEndpoint2, (String)oLabelAsObject, oColor); } } // Retain information about the edge that was drawn. edgeDrawingHistory = new EdgeDrawingHistory( edge, oDrawingVisual, bDrawAsSelected); return(true); } }
//************************************************************************* // Method: UndrawEdge() // /// <summary> /// "Undraws" an edge that was drawn by <see cref="DrawGraph" />. /// </summary> /// /// <param name="edge"> /// The edge to remove from the contained collection of Visual objects. /// </param> /// /// <param name="graphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. This /// must be the same object that was passed to <see cref="DrawGraph" /> the /// last time the entire graph was drawn. /// </param> /// /// <remarks> /// Use this method to undraw an edge without incurring the overhead of /// redrawing the entire graph. /// </remarks> //************************************************************************* public void UndrawEdge( IEdge edge, GraphDrawingContext graphDrawingContext ) { Debug.Assert(edge != null); Debug.Assert(graphDrawingContext != null); AssertValid(); // Retrieve the EdgeDrawingHistory object for the edge, if one exists. // (If the edge was previously hidden, there won't be an // EdgeDrawingHistory object for it.) Dictionary<Int32, EdgeDrawingHistory> oEdgeDrawingHistories = graphDrawingContext.EdgeDrawingHistories; Int32 iEdgeID = edge.ID; EdgeDrawingHistory oEdgeDrawingHistory; if ( oEdgeDrawingHistories.TryGetValue( iEdgeID, out oEdgeDrawingHistory) ) { // Remove the EdgeDrawingHistory object from the dictionary. oEdgeDrawingHistories.Remove(iEdgeID); // Remove the edge's DrawingVisual object, which will cause the // edge to disappear. GetEdgeDrawingVisuals(oEdgeDrawingHistory).Children.Remove( oEdgeDrawingHistory.DrawingVisual); } }
//************************************************************************* // Method: DrawGraph() // /// <summary> /// Draws the graph. /// </summary> /// /// <param name="oGraphRectangle"> /// Rectangle to draw the graph within. /// </param> //************************************************************************* protected void DrawGraph( Rect oGraphRectangle ) { AssertValid(); #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl: DrawGraph(), oGraphRectangle = " + oGraphRectangle); #endif m_oLastGraphDrawingContext = new GraphDrawingContext( oGraphRectangle, m_oAsyncLayout.Margin, m_oGraphDrawer.BackColor); m_oGraphDrawer.DrawGraph(m_oGraph, m_oLastGraphDrawingContext); }
//************************************************************************* // Method: DrawBackground() // /// <summary> /// Draws the graph's background. /// </summary> /// /// <param name="oGraph"> /// The graph being drawn. /// </param> /// /// <param name="oGraphDrawingContext"> /// Provides access to objects needed for graph-drawing operations. /// </param> //************************************************************************* protected void DrawBackground( IGraph oGraph, GraphDrawingContext oGraphDrawingContext ) { Debug.Assert(oGraph != null); Debug.Assert(oGraphDrawingContext != null); AssertValid(); // Draw the background color, followed by the background image if one // was specified. Color oBackColor; if ( !TryGetColorValue(oGraph, ReservedMetadataKeys.GraphBackColor, out oBackColor) ) { oBackColor = m_oBackColor; } DrawingVisual oBackgroundDrawingVisual = new DrawingVisual(); using ( DrawingContext oDrawingContext = oBackgroundDrawingVisual.RenderOpen() ) { oDrawingContext.DrawRectangle( CreateFrozenSolidColorBrush(oBackColor), null, oGraphDrawingContext.GraphRectangle); Object oImageSourceAsObject; if ( oGraph.TryGetValue(ReservedMetadataKeys.GraphBackgroundImage, typeof(ImageSource), out oImageSourceAsObject) ) { ImageSource oImageSource = (ImageSource)oImageSourceAsObject; oDrawingContext.DrawImage( oImageSource, new Rect( new Size(oImageSource.Width, oImageSource.Height) ) ); } } m_oVisualCollection.Add(oBackgroundDrawingVisual); }
//************************************************************************* // Constructor: NodeXLControl() // /// <summary> /// Initializes a new instance of the <see cref="NodeXLControl" /> class. /// </summary> //************************************************************************* public NodeXLControl() { m_oGraph = new Graph(); m_oGraphDrawer = new GraphDrawer(this); m_oAsyncLayout = new FruchtermanReingoldLayout(); OnNewLayout(m_oAsyncLayout); m_oLastLayoutContext = new LayoutContext(System.Drawing.Rectangle.Empty); m_oLastGraphDrawingContext = null; m_eLayoutState = LayoutState.Stable; m_eMouseMode = MouseMode.Select; m_bMouseAlsoSelectsIncidentEdges = true; m_bAllowVertexDrag = true; m_oVerticesBeingDragged = null; m_oMarqueeBeingDragged = null; m_oTranslationBeingDragged = null; m_oSelectedVertices = new HashSet<IVertex>(); m_oSelectedEdges = new HashSet<IEdge>(); m_oCollapsedGroups = new Dictionary<String, IVertex>(); m_oDoubleClickedVertexInfo = null; m_bShowVertexToolTips = false; m_oLastMouseMoveLocation = new Point(-1, -1); // Create a helper object for displaying vertex tooltips. CreateVertexToolTipTracker(); m_oVertexToolTip = null; m_bGraphZoomCentered = false; this.AddLogicalChild(m_oGraphDrawer.VisualCollection); CreateTransforms(); // Prevent a focus rectangle from being drawn around the control when // it captures keyboard focus. The focus rectangle does not behave // properly when the layout and render transforms are applied -- // sometimes the rectangle disappears, and sometimes it gets magnified // by the render layout. this.FocusVisualStyle = null; // AssertValid(); }