Skip to content

Tutorial

Alexander Smirnov edited this page Jul 13, 2016 · 1 revision

< Back to contents >

 

This tutorial uses basic/default graph configuration and visual templates. If you want to customize your graph behaviors and look please read following tutorials.

Last checked against version: 2.1.8
Note: You can find detailed and commented example projects in the library sources.


There are 2 methods of graph generation available.

  1. Create and fill graph data, create GXLogicCore object and then call GenerateGraph(graph) method. This approach lets GraphArea to generate visuals automatically. Method is explained below.
  2. Create and fill graph data. manually create custom VertexControls, EdgeControls and inject them into GraphArea. Assign data graph to GXLogicCore.Graph property and call GraphArea.RelayoutGraph(). This method allows manual handling of customized objects. Method is explained in the end of the article.

Method 1. Automatic visual handling.

To start using GraphX library you only need to do the following:

  1. Define your visual control and data types based on the GraphX core types.
  2. Code to create corresponding objects (XAML or code-behind)
  3. Generate data graph, logic object and invoke generation method

First of all let's define all the classes we will need for the graph operations. This will be:

  • Layout visual object (must derive from GraphArea<TVertex, TEdge, TGraph> class)
  • Logic core object (must derive from IGXLogicCore<TVertex, TEdge, TGraph> interface)
  • Graph data object (must derive from QuickGraph's IBidirectionalGraph)
  • Vertex data object (must derive from VertexBase)
  • Edge data object (must derive from EdgeBase<VertexBase>)

PLEASE NOTE: All derived classes must implement parameterless constructors for feature compatibility such as save/load graphs!

For example:

    //Layout visual class
    public class GraphAreaExample : GraphArea<DataVertex, DataEdge, BidirectionalGraph<DataVertex, DataEdge>> { }

    //Graph data class
    public class GraphExample : BidirectionalGraph<DataVertex, DataEdge> { }

    //Logic core class
    public class GXLogicCoreExample : GXLogicCore<DataVertex, DataEdge, BidirectionalGraph<DataVertex, DataEdge>> { }

    //Vertex data object
    public class DataVertex: VertexBase
    {
        /// <summary>
        /// Some string property for example purposes
        /// </summary>
        public string Text { get; set; }

        public override string ToString()
        {
            return Text;
        }
    }

    //Edge data object
    public class DataEdge: EdgeBase<DataVertex>
    {
        public DataEdge(DataVertex source, DataVertex target, double weight = 1)
			: base(source, target, weight)
        {
        }

        public DataEdge()
            : base(null, null, 1)
        {
        }

        public string Text { get; set; }

        public override string ToString()
        {
            return Text;
        }
    }

Now you need to create GraphArea visual object. The best practice is to use ZoomControl included into the GraphX library as the wrapper for our custom GraphAreaExample control. This approach will help us to handle zooming, scaling and other display actions without any problems. For example, in XAML it will look like:

        xmlns:graphxctrl="clr-namespace:GraphX.Controls;assembly=GraphX.WPF.Controls"
        xmlns:local="clr-namespace:ShowcaseApp.WPF"  

                        <graphxctrl:ZoomControl x:Name="gg_zoomctrl">
                            <local:GraphAreaExample x:Name="gg_Area"/>
                        </graphxctrl:ZoomControl>

Now we need to create the data graph and fill it with the data.

PLEASE NOTE: All edge and vertex data properties MUST have unique positive number assigned to ID property. Nowadays this is done automatically whenever you add new VertexControl or EdgeControl or run GraphArea::GenerateGraph() method. Several GraphX features such as serialization depends on it!

            Random Rand = new Random();

            //Create data graph object
            var graph = new GraphExample();

            //Create and add vertices using some DataSource for ID's
            foreach (var item in DataSource.Take(100))
                graph.AddVertex(new DataVertex() { ID = item.ID, Text = item.Text });

            var vlist = graph.Vertices.ToList();
            //Generate random edges for the vertices
            foreach (var item in vlist)
            {
                if (Rand.Next(0, 50) > 25) continue;
                var vertex2 = vlist[Rand.Next(0, graph.VertexCount - 1)];
                graph.AddEdge(new DataEdge(item, vertex2, Rand.Next(1, 50)) 
                      { Text = string.Format("{0} -> {1}",item, vertex2) });
            }

With this approach we now have data graph object with 100 vertices and aprox. 50 edges. 

At this point we have class definitions and raw graph data. Now we need to create object responsible for logic operations and specify calculation settings.

            var LogicCore = new GXLogicCoreExample();
            //This property sets layout algorithm that will be used to calculate vertices positions
            //Different algorithms uses different values and some of them uses edge Weight property.
            LogicCore.DefaultLayoutAlgorithm = GraphX.LayoutAlgorithmTypeEnum.KK;
            //Now we can set optional parameters using AlgorithmFactory
            //NOTE: default parameters can be automatically created each time you change Default algorithms
            LogicCore.DefaultLayoutAlgorithmParams = 
                               LogicCore.AlgorithmFactory.CreateLayoutParameters(GraphX.LayoutAlgorithmTypeEnum.KK);
            //Unfortunately to change algo parameters you need to specify params type which is different for every algorithm.
            ((KKLayoutParameters)LogicCore.DefaultLayoutAlgorithmParams).MaxIterations = 100;

            //This property sets vertex overlap removal algorithm.
            //Such algorithms help to arrange vertices in the layout so no one overlaps each other.
            LogicCore.DefaultOverlapRemovalAlgorithm = GraphX.OverlapRemovalAlgorithmTypeEnum.FSA;
            //Setup optional params
            LogicCore.DefaultOverlapRemovalAlgorithmParams =
                              LogicCore.AlgorithmFactory.CreateOverlapRemovalParameters(GraphX.OverlapRemovalAlgorithmTypeEnum.FSA);
            ((OverlapRemovalParameters)LogicCore.DefaultOverlapRemovalAlgorithmParams).HorizontalGap = 50;
            ((OverlapRemovalParameters)LogicCore.DefaultOverlapRemovalAlgorithmParams).VerticalGap = 50;

            //This property sets edge routing algorithm that is used to build route paths according to algorithm logic.
            //For ex., SimpleER algorithm will try to set edge paths around vertices so no edge will intersect any vertex.
            LogicCore.DefaultEdgeRoutingAlgorithm = GraphX.EdgeRoutingAlgorithmTypeEnum.SimpleER;

            //This property sets async algorithms computation so methods like: Area.RelayoutGraph() and Area.GenerateGraph()
            //will run async with the UI thread. Completion of the specified methods can be catched by corresponding events:
            //Area.RelayoutFinished and Area.GenerateGraphFinished.
            LogicCore.AsyncAlgorithmCompute = false;

            //Finally assign logic core to GraphArea object
            gg_Area.LogicCore = LogicCore;

As you see in the example code above, we can setup default graph algorithms and behaviors optionally. Default algorithms are used if no external algorithm of the same type is assigned by the user. For more info you can read Using external calculation algorithms.

To generate and display visual graph all what we need is to call gg_Area::GenerateGraph() method for GraphArea control created in the example above. Optionally you can use param to specify if you want the edges to be generated after this method call.

//Also we specified optional param to generate edges
gg_Area.GenerateGraph(true);

To relayout already built graph you can use this method:

//Optional param specifies if edges should be automatically generated
gg_Area.RelayoutGraph(true);

In the terms of performance edge controls are not created automatically though you can do it by specifying generateAllEdges parameter in the graph generation/relayout methods or manually using GraphArea::GenerateAllEdges() and GraphArea::GenerateEdgesForVertex() to generate all possible edges or edges for specified vertex only. 

Remember that edge routing points are calculated in RelayoutGraph() stage but new route points are applied in GenerateAllEdges() or GenerateEdgesForVertex() methods when visual edges are being generated. Individual edge recalculations are performed on each EdgeControl::Render pass if current ER algorithm allows such operation.

Method 2. Manual handling of custom visuals.

We will skip needed classes definitions as they are the same as in Method 1.
First of all lets create sample data objects:

            //Create data graph object
            var graph = new GraphExample();

            //Create and add vertices
            graph.AddVertex(new DataVertex() { ID = 1, Text = "Item 1" });
            graph.AddVertex(new DataVertex() { ID = 2, Text = "Item 2" });
            var v1 = graph.Vertices.First();
            var v2 = graph.Vertices.Last();
            var e1 = new DataEdge(v1, v2, 1) { Text = "1 -> 2" }
            graph.AddEdge(e1);
            gg_Area.LogicCore.Graph = graph;

Next we create and add visual objects manually:

             //VC DataContext will be bound to v1 by default. You can control this by specifying additional property in the constructor
             var vc1 = new VertexControl(v1);
             gg_Area.AddVertex(v1, vc1);
             var vc2 = new VertexControl(v2);
             gg_Area.AddVertex(v2, vc2);

             var ec = new EdgeControl(e1, vc1, vc2);
             gg_Area.InsertEdge(e1, ec); //inserts edge into the start of the children list to draw it below vertices

Now the final part where we create logic core and call relayout method to create visualization:

//Create logic core
var LogicCore = new GXLogicCoreExample() { Graph = graph };
//Assign data graph
gg_Area.LogicCore = LogicCore;
//Call relayout. Additional parameters shows that edges must be created or updated if any exists.
gg_Area.RelayoutGraph(true);