Practical Example To Visualize Entities In Live Application Using MSAGL

Finding it difficult to understand the complex relationship between the different objects which are part of an application? Think of Visualizing the objects and their state when the application is running, Seems exciting right? 

Yes, It's possible with the little help from MSAGL(Microsoft Automatic Graph Layout) and some boilerplate code to do that. I have created a sample application demonstrating how it can be done, including the state of these objects in a live manner.

I would recommend you to go through MSAGL Basics

Check out the video here:


Check out the code here:


using Microsoft.Msagl.Drawing;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading;


namespace MSAGL_Demo

{

public interface INodeRepository

{

void AddNode(Node node);

void OnActiveNodeChanged(string obj);

}


public class NodeRepository : INodeRepository

{

private IList<Node> nodes = new List<Node>();

private Graph g = null;

public NodeRepository() { g = new Graph(); }

public event Action Invalidate;


public void AddNode(Node node)

{

if (node == null) throw new ArgumentNullException("Node is null");

nodes.Add(node);

}


public Graph Visualize()

{

g.Directed = true;

foreach (var item in nodes)

{

g.AddNode(item.ToString());

}


foreach (var item in nodes)

{

var node = nodes.FirstOrDefault(n => n.GetType() == item.GetType().BaseType);

if (node != null)

{

g.AddEdge(item.ToString(),node.ToString());

}

}


return g;

}


public void OnActiveNodeChanged(string currentActiveNode)

{

if(string.IsNullOrEmpty(currentActiveNode)) throw new ArgumentNullException(nameof(currentActiveNode));


var activeNode = g.FindNode(currentActiveNode);


if (activeNode != null)

{

activeNode.Attr.FillColor = Color.Green;

Invalidate();

}


}

}


public class Node

{

public event Action<string> ActiveNodeChanged;


public Node(INodeRepository nodeRepository) 

{

nodeRepository.AddNode(this);

ActiveNodeChanged += nodeRepository.OnActiveNodeChanged;

}


private bool isActive;


public bool IsActive 

{

get

{

return isActive;

}

set

{

isActive = value;

if(isActive)

{

ActiveNodeChanged(this.ToString());

}

}

}

}




//Chain of Responsibility


//Node Hierarchy

public class ParentExecutor:Node

{

protected ParentExecutor executor;

protected INodeRepository m_nodeRepository;

public ParentExecutor(INodeRepository nodeRepository):base(nodeRepository) { m_nodeRepository = nodeRepository; }


public virtual void Execute()

{

IsActive = true;

Thread.Sleep(3000);

Console.WriteLine($"{nameof(ParentExecutor)} Executing");

}

}


public class ChildExecutorI : ParentExecutor

{

public ChildExecutorI(INodeRepository nodeRepository):base(nodeRepository)

{

executor = new ParentExecutor(nodeRepository);

}


public override void Execute()

{

IsActive = true;

Thread.Sleep(3000);

Console.WriteLine($"{nameof(ChildExecutorI)} Executing");

if (null != executor)

{

executor.Execute();

}

}

}


public class ChildExecutorII : ParentExecutor

{

public ChildExecutorII(INodeRepository nodeRepository) : base(nodeRepository)

{

executor = new ChildExecutorI(nodeRepository);

}


public override void Execute()

{

IsActive = true;

Thread.Sleep(3000);

Console.WriteLine($"{nameof(ChildExecutorII)} Executing");

if (null != executor)

{

executor.Execute();

}

}

}


public class ChildExecutorIII : ParentExecutor

{

public ChildExecutorIII(INodeRepository nodeRepository) : base(nodeRepository)

{

executor = new ChildExecutorII(nodeRepository);

}


public override void Execute()

{

IsActive = true;

Thread.Sleep(3000);

Console.WriteLine($"{nameof(ChildExecutorIII)} Executing");

if (null != executor)

{

executor.Execute();

}

}

}

}

Driver Code:


using Microsoft.Msagl.DebugHelpers;
using Microsoft.Msagl.Drawing;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MSAGL_Demo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private NodeRepository m_NodeRepository;
private ChildExecutorIII m_executor;

public MainWindow()
        {
            InitializeComponent();

m_NodeRepository = new NodeRepository();
m_NodeRepository.Invalidate += OnInvalidate;
}

private void OnInvalidate()
{
gViewer.Refresh();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
        {
m_executor = new ChildExecutorIII(m_NodeRepository);

Graph g = m_NodeRepository.Visualize();

gViewer.Graph = g;

var observable = Observable.Return(true)
.Delay(TimeSpan.FromSeconds(1))
.Subscribe(_ =>
{
Dispatcher.BeginInvoke(new Action(Execute));
});

}

private void Execute()
{
m_executor.Execute();
}
}
}


I hope this exercise serves as a starting point to visualize the different entities involved in a live application, helping developers to better understand the various entities involved in an application and the relationship between them.


Comments

Popular posts from this blog

Creating RESTful Minimal WebAPI in .Net 6 in an Easy Manner! | FastEndpoints

Step By Step Guide to Detect Heap Corruption in Windows Easily

How to dynamically add Properties in C# .NET?