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);
Visual oGroupVisual;
if ( m_oGroupDrawer.TryDrawGroupRectangles(graph, graphDrawingContext,
out oGroupVisual) )
{
m_oVisualCollection.Add(oGroupVisual);
}
if ( m_oGroupDrawer.TryDrawCombinedIntergroupEdges(
graph, graphDrawingContext, out oGroupVisual) )
{
m_oVisualCollection.Add(oGroupVisual);
}
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.
//
// Selected vertices should always be on top of unselected vertices, so
// draw them first. Note that this could also be accomplished by using
// two DrawingVisual objects, m_oUnselectedVertexDrawingVisuals and
// m_oSelectedVertexDrawingVisuals, in a manner similar to what is done
// for edges. However, vertices have to be hit-tested, and having two
// DrawingVisual objects for two sets of vertices would complicate and
// slow down hit-testing, so this solution is the simpler one.
LinkedList<IVertex> oSelectedVertices = new LinkedList<IVertex>();
foreach ( IVertex oVertex in GetVerticesToDraw(graph) )
{
if ( m_oVertexDrawer.GetDrawAsSelected(oVertex) )
{
oSelectedVertices.AddLast(oVertex);
}
else
{
DrawVertex(oVertex, graphDrawingContext);
}
}
foreach (IVertex oVertex in oSelectedVertices)
{
DrawVertex(oVertex, graphDrawingContext);
}
oSelectedVertices = null;
// 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);
if ( m_oGroupDrawer.TryDrawGroupLabels(graph, graphDrawingContext,
out oGroupVisual) )
{
m_oVisualCollection.Add(oGroupVisual);
}
}