Database schema diagram

In this post we’ll show how to use TableNode objects to display tabular data, more specifically database schema information. A Visual Studio sample project containing the code from this post is available for download here:

DatabaseSchema.zip

To start, create a new Windows Forms application, and place a text field for connection string, a button and a DiagramView on the form. In the code-behind file, add following field to map table name to respective TableNode objects:

Dictionary<string, tablenode=""> tables = new Dictionary<string, tablenode="">();
</string,></string,>

Add a RectangleF that stores default size passed to CreateTableNode method:

RectangleF defaultSize = new RectangleF(0, 0, 30, 30);

Create a ReadTables method, which provided an SqlConnection, parses its schema information and creates diagram nodes:

void ReadTables(SqlConnection connection)
{
	// get table schema definitions from connection
	var schema = connection.GetSchema("Tables");
	foreach (DataRow row in schema.Rows)
	{
		// fetch table name
		var name = row["TABLE_NAME"].ToString();

		// create respective node
		var table = diagram.Factory.CreateTableNode(defaultSize);
		table.Caption = name;
		table.Shape = SimpleShape.RoundedRectangle;
		table.Brush = new MindFusion.Drawing.SolidBrush(Color.LightGray);

		// register node in dictionary for future foreign key reference
		tables[name.Replace(" ", "_")] = table;
		ReadFields(table, connection,
			row["TABLE_CATALOG"].ToString(), null, name);
	}

	ReadForeignKeys(connection);
}

The ReadFields method takes table node and name parameters and creates node cells that will show information for the column name and type of database tables:

void ReadFields(TableNode node,
	SqlConnection connection, string db, string owner, string tableName)
{
	// remove default cells
	node.RowCount = 0;

	// reserve one column for name and one for data type
	node.ColumnCount = 2;

	// read column definitions of specified table
	var schema = connection.GetSchema("Columns", new[] { db, owner, tableName });
	foreach (DataRow row in schema.Rows)
	{
		// add a new row to the node
		int r = node.AddRow();

		// set cells' text to the column name and type
		node[0, r].Text = row["COLUMN_NAME"].ToString();
		node[1, r].Text = row["DATA_TYPE"].ToString();

	}

	// make table cells big enough to show all text
	node.ResizeToFitText(false);
}

The ReadForeignKeys method creates DiagramLink connectors between table nodes to show the relationships between database tables:

void ReadForeignKeys(SqlConnection connection)
{
	var schema = connection.GetSchema("ForeignKeys");
	foreach (DataRow row in schema.Rows)
	{
		// read foreign key information
		string fkName = row["CONSTRAINT_NAME"].ToString();
		string tableName = row["TABLE_NAME"].ToString().Replace(" ", "_");
		string prefix = "FK_" + tableName + "_";
		if (fkName.StartsWith(prefix))
		{
			string targetName = fkName.Substring(prefix.Length);

			// get table nodes registered for specified names
			if (tables.ContainsKey(targetName) && tables.ContainsKey(tableName))
			{
				var table = tables[tableName];
				var targetTable = tables[targetName];

				// create a link between the nodes to show relationship
				diagram.Factory.CreateDiagramLink(table, targetTable);
			}
		}
	}
}

Finally handle the button’s click event to open specified connection and call ReadTables. Apply AnnealLayout to arrange the tables so that they do not overlap:

private void btnOpen_Click(object sender, System.EventArgs e)
{
	diagram.ClearAll();

	try
	{
		var connection = new SqlConnection(tbConnection.Text);
		connection.Open();

		// read schema and create corresponding diagram items
		ReadTables(connection);

		connection.Close();
	}
	catch (Exception exception)
	{
		MessageBox.Show(exception.Message);
		diagram.ClearAll();
	}

	// arrange the tables to remove overlaps
	var layout = new AnnealLayout();
	layout.SplitGraph = true;
	layout.Randomize = false;
	layout.MultipleGraphsPlacement = MultipleGraphsPlacement.MinimalArea;
	layout.Margins = new SizeF(10, 10);
	layout.Arrange(diagram);
}

If you run the project and open the Northwind sample database by Microsoft, you should see this diagram:

database schema layout

The code above uses MindFusion’s .NET API and can be used with Windows Forms, WPF, Silverlight and ASP.NET diagramming components. The Java API for Android and desktop Swing application will look similar, with setter method calls instead of property assignments.

You can download the trial version of any MindFusion.Diagramming component from this page.

Enjoy!

Combine layout algorithms

Apply TreeLayout twice to arrange a genealogy tree

In a series of posts we’ll explore ways to combine graph layout algorithms for various purposes, such as improving layout speed or achieving specific layout constraints.

In this example we’ll show how to apply two TreeLayout instances with different settings to arrange a genealogy tree. The genealogy tree is focused on specific person, with several levels of ancestors drawn above and descendants drawn below. A Visual Studio sample project containing the code from this post is available for download here:

GenealogyLayout.zip

As a start, let’s define a new node class that will draw a person’s photo and name inside a frame, along with their partners’. This will simplify our layout code since we won’t have to take care of keeping partner nodes close to each other:

class GenealogyNode : DiagramNode
{
	public List Partners { get; set; }

	public override void DrawLocal(IGraphics graphics, RenderOptions options)
	{
		float relationLinkLen = Bounds.Width / 7;
		int relations = Partners.Count - 1;
		float personViewWidth = Bounds.Width - relations * relationLinkLen;
		personViewWidth /= Partners.Count;

		var rect = GetLocalBounds();
		rect.Width = personViewWidth;
		for (int i = 0; i < Partners.Count; i++)
		{
			DrawPerson(Partners[i], graphics, rect);

			if (i < Partners.Count - 1)
			{
				float rx = rect.Right;
				float ry = rect.Y + 4 * rect.Height / 5;
				rect.X += personViewWidth + relationLinkLen;
				graphics.DrawLine(Pens.Gray, rx, ry, rect.X, ry);
			}
		}
	}

	public override void DrawShadowLocal(IGraphics graphics, RenderOptions options)
	{
	}

	void DrawPerson(Person person, IGraphics graphics, RectangleF rect)
	{
		const float labelHeight = 5;
		const float padding = 3;

		// draw name
		var labelRect = RectangleF.FromLTRB(
			rect.Left,
			rect.Bottom - labelHeight,
			rect.Right,
			rect.Bottom);

		graphics.DrawString(person.Name,
			EffectiveFont, Brushes.Black, labelRect,
			new StringFormat { Alignment = StringAlignment.Center });

		// draw image
		var imageRect = rect;
		imageRect.Height -= labelHeight + padding;

		Utilities.DrawImage(graphics, person.Image, imageRect, ImageAlign.Fit);

		// draw frame
		var frameColor = person.Gender == Gender.Female ?
			Color.Red : Color.BlueViolet;
		var framePen = new System.Drawing.Pen(frameColor, 0);
		graphics.DrawRectangle(framePen, rect);
		framePen.Dispose();
	}
}

Alternatively, we could draw a single person per node instead, placing partners’ nodes close to each other, grouping them using AttachTo method, and later running TreeLayout with its KeepGroupLayout property enabled.

Now to generate a sample tree, we’ll define recursive methods that will create specified number of ancestor pairs (GenerateAncestors) and create random number of descendants (GenerateDescendants):

void GenerateAncestors(GenealogyNode node, int levels)
{
	if (levels == 0)
		return;
	for (int i = 0; i < 2; i++)
	{
		var p = AddPair();
		var link = diagram.Factory.CreateDiagramLink(p, node);
		link.DestinationAnchor = i;
		link.OriginAnchor = 2;
		GenerateAncestors(p, levels - 1);
	}
}

void GenerateDescendants(GenealogyNode node, int levels)
{
	if (levels == 0)
		return;
	int children = random.Next(1, 5);
	for (int i = 0; i < children; i++)
	{
		int r = random.Next(0, 3);
		if (r == 2)
		{
			var p = AddPair();
			var link = diagram.Factory.CreateDiagramLink(node, p);
			link.OriginAnchor = 2;
			link.DestinationAnchor = 0;
			GenerateDescendants(p, levels - 1);
		}
		else if (r == 1)
		{
			var p = new Person { Name = "daughter", Gender = Gender.Female, Image = fImage };
			var childNode = AddNode(p);
			diagram.Factory.CreateDiagramLink(node, childNode);
		}
		else if (r == 0)
		{
			var p = new Person { Name = "son", Gender = Gender.Male, Image = mImage };
			var childNode = AddNode(p);
			diagram.Factory.CreateDiagramLink(node, childNode);
		}
	}
}

GenealogyNode AddPair()
{
	var p1 = new Person { Name = "mom", Gender = Gender.Female, Image = fImage };
	var p2 = new Person { Name = "dad", Gender = Gender.Male, Image = mImage };
	return AddNode(p1, p2);
}

GenealogyNode AddNode(Person p)
{
	var bounds = new RectangleF(0, 0, 30, 40);

	var node = new GenealogyNode();
	node.Bounds = bounds;
	node.Partners = new List { p };
	node.AnchorPattern = AnchorPattern.TopInBottomOut;
	diagram.Nodes.Add(node);
	return node;
}

GenealogyNode AddNode(Person p1, Person p2)
{
	var bounds = new RectangleF(0, 0, 70, 40);

	var node = new GenealogyNode();
	node.Bounds = bounds;
	node.Partners = new List { p1, p2 };
	node.AnchorPattern = PairPattern;
	diagram.Nodes.Add(node);
	return node;
}

Finally we run TreeLayout twice with specified root node, arranging ancestor nodes above the root and descendant nodes below it, creating the genealogy drawing shown below:

private void GenealogyForm_Load(object sender, EventArgs e)
{
	var root = AddPair();
	GenerateAncestors(root, 2);
	GenerateDescendants(root, 3);

	var l1 = new TreeLayout();
	l1.ReversedLinks = true;
	l1.Direction = TreeLayoutDirections.BottomToTop;
	l1.Anchoring = Anchoring.Keep;
	l1.LevelDistance *= 2;
	l1.NodeDistance *= 1.4f;
	l1.LinkStyle = TreeLayoutLinkType.Cascading3;
	l1.Arrange(diagram);

	var l2 = new TreeLayout();
	l2.Root = root;
	l2.KeepRootPosition = true;
	l2.Anchoring = Anchoring.Keep;
	l2.LevelDistance *= 2;
	l2.NodeDistance *= 1.4f;
	l2.LinkStyle = TreeLayoutLinkType.Cascading3;
	l2.Arrange(diagram);

	diagram.ResizeToFitItems(5);
	//diagramView.ZoomToFit();
}

genealogy tree layout

The code above uses MindFusion’s .NET API and can be used with Windows Forms, WPF, Silverlight and ASP.NET diagramming components. The Java API for Android and desktop Swing application will look similar, with setter method calls instead of property assignments.

You can download the trial version of any MindFusion.Diagramming component from this page.

Enjoy!

Combine layout algorithms

Use OrthogonalLayout to generate initial placement for SpringLayout

In a series of posts we’ll explore ways to combine graph layout algorithms for various purposes, such as improving layout speed or achieving specific layout constraints.

In this example we’ll show how to apply OrthogonalLayout as preprocessing step for SpringLayout used to minimize edge crossings. A problem with force-directed layout algorithms such as SpringLayout is that they can reach equilibrium of the simulated forces while there are link crossings present. However if the simulation starts from an initial layout that has minimal number of crossing, it will tend to reach balance without introducing new crossings. So we can use any of the layout algorithms used for arranging planar graphs (OrthogonalLayout, TriangularLayout, CascadingLayout) to create the initial configuration for SpringLayout.

OrthogonalLayout is designed to create planar drawings of graphs (having no crossing links at all if possible) where edge segments are either horizontal or vertical. For some types of diagrams, such as flowcharts, you might use OrthogonalLayout as is. However in many cases you might prefer SpringLayout, e.g. in order to achieve aesthetic criteria like uniform edge lengths, or to conform to accepted drawing conventions such as the one used to present state machines. So when you know your graphs are planar or close to planar, you can run OrthogonalLayout as pre-processing step, and then run the physical-force simulation using SpringLayout to achieve straight-line uniform length drawings:

void ApplySpringLayout(bool preArrange)
{
    if (preArrange)
    {
        var tl = new OrthogonalLayout();
        tl.Arrange(diagram);
    }

    var sl = new SpringLayout();
    sl.Randomize = false;
    sl.MinimizeCrossings = true;
    sl.IterationCount = 50;
    sl.Arrange(diagram);

    diagramView.ZoomToFit();
}

Here are several examples of the method results when called respectively with false (on the left side) and with true (on the right side of image). Note that for such small graphs SpringLayout will probably remove the crossings if left to run for more iterations, but in the general case and with larger graphs that’s not guaranteed.

1

2

3

The code above uses MindFusion’s .NET API and can be used with Windows Forms, WPF, Silverlight and ASP.NET diagramming components. The Java API for Android and desktop Swing application will look similar, with setter method calls instead of property assignments.

Enjoy!

Spreadsheet for WinForms, V1.2

MindFusion has released a new version of Spreadsheet for WinForms with the following new features:

Formatted text support
You can import, display and export texts with mixed formatting. You can create formatted texts programmatically by assigning a FormattedText instance to the Data property of cells. Currently the control supports: font name and size, bold, italic, underlined, strikeout, and text color.

New Functions
At your disposal are 22 new functions, which have been added to the core calculation engine, including a full set of database functions, such as DGET, DSUM, and so on. Check Function Reference for additional information.

Improved in-place editing
Now when you type a new formula, pressing the arrow keys will result in navigation within the worksheet (instead of adjusting the caret position) and the selected cell or cell ranges will be inserted as a reference in the currently edited formula.

Sport events schedule

Sport events schedule

You can download the trial version from the link below:

Download MindFusion.Spreadsheet for WinForms 1.2, Trial Version

If you require technical support, you can post a message at the forum, send us an e-mail at support@mindfusion.eu. or use the help desk. MindFusion takes special effort in providing fast and detailed answers to all inquiries that we receive.

About MindFusion.Spreadsheet for WinForms: An easy-to-use programming component suitable for building all types of spreadsheets fast and easy. The tool supports formulas, tool-tips, cell annotations, cell spanning, scrolling and many more. You can add charts and images as well use the flexible style system to design the perfect spreadsheet. The component supports full undo and redo as well copy and paste from Windows clipboard.
You can import spreadsheet data from CSV, XLSX or ODS files and export the final spreadsheet in a number of formats – as images, PDF or CSV, XLSX or ODS files. Various auxiliary forms help you quickly adjust the data and appearance of your spreadsheet. Read more about the features of the component here or check the license prices at the buy page.

Spreadsheet for WinForms is part of MindFusion Pack for WinForms, which offers other useful components that are of great use when you build any type of WinForms application – from a diagramming library to map control to gauges: check them here.

Diagramming for Silverlight, V3.0.1

We have released a new version of the Diagramming component for Silverlight. Here is an overview of the new features:

Zoom control

The new ZoomControl class lets users interactively change the zoom level and position of a diagram. In order to use the ZoomControl, place it anywhere over the target diagram and set the target property of the control to that diagram. Use the ZoomStep and ScrollStep properties to customize the control. The control’s appearance is customized with the Fill, Stroke, CornerRadius and TickPosition properties.

The zoom control and the new predefined node shapes.

The zoom control and the new predefined node shapes.

Miscellaneous

  • Use the new AllowRenamePages property of TabbedDiagramView to let users rename a DiagramPage interactively by clicking on its tab.
  • Several new stock shapes have been added: RightTriangle, Decagon, Trapezoid, Star4Pointed, Star5Pointed, Star6Pointed, Star7Pointed, Star16Pointed, Star24Pointed, Star32Pointed, Donut, Plaque.
  • The HandlesStyle property of nodes supports two new handle styles – RoundAndSquare and RoundAndSquare2.
  • The SetSelfLoopShape event is raised when a link becomes a self-loop. You can set a custom shape for that link.
  • and much more – find the full list here.
Tabbed view.

Tabbed view.

Direct download of the trial version is available from the link below:

Download DiagramLite 3.0.1 Trial Version

Feel free to contact us with any questions about Diagramming for Silverlight or any other of our products – please use the forum, email support@mindfusion.eu or the help desk. We strive to provide competent and detailed answers to all support inquiries within hours of receiving them.

About MindFusion.Diagramming for Silverlight: A programming component specially designed and developed to provide web developers with a fast and easy way to create diagrams, graphs, schemes, hierarchies, charts and many more. The impressive feature set of the control ranges from predefined node shapes to custom nodes and thirteen automatic layouts. The style and appearance of all diagram elements are completely customizable, the numerous samples provide programmers with plenty of example code to look from.

The control boasts intuitive API that is documented in details in the help file provided with the installation. There are also step-by-step tutorials and various guides. You can check the features list here to find out more about the capabilities of the tool. An online demo is also available. The prices are per developer, source code is also available. Learn more about the licensing scheme here.