Creating custom CompositeNode components

In this post we’ll examine how CompositeNode components work in MindFusion.Diagramming for Windows Forms, and in the process create a custom radio button component. You can find the completed sample project here: RadioComponent.zip

CompositeNode was created as alternative of the ControlNode class, which lets you present any Windows Forms control as a diagram node. ControlNode has many advantages, such as letting you design the hosted user controls using Visual Studio designer, reusing them in other parts of the user interface, and including complex framework or third-party controls as their children. From the fact that each user control creates a hierarchy of Win32 windows come some disadvantages too:

  • ControlNodes cannot mix with other diagram elements in the Z order but are always drawn on top
  • performance deteriorates if showing hundreds of nodes
  • mouse events might not reach the diagram if hosted controls capture mouse input
  • print and export might not be able to reproduce the appearance of hosted controls without additional work (handling PaintControl event)

On the other hand, CompositeNode does all its drawing in DiagramView control’s canvas and is not affected by the issues listed above. CompositeNode lets you build node’s UI by composing hierarchy of components derived from ComponentBase class. Pre-defined components include layout panels, read-only or editable text fields, images, borders, buttons, check-boxes and sliders. If the UI component you need isn’t provided out of the box, you could still implement it as a custom class that derives from ComponentBase or more specific type and overriding the GetDesiredSize, ArrangeComponents and Draw methods. Lets see how that works using a RadioButtonComponent as an example.

Derive RadioButtonComponent from CheckBoxComponent so we reuse its IsChecked and Content properties:

class RadioButtonComponent : CheckBoxComponent
{
}

CompositeNode relies on a dynamic layout system that lets components determine their size by overriding GetDesiredSize method, and arranging children in allocated size by means of ArrangeComponents method. For radio button we’ll call its base class to measure content size and add enough space for drawing the radio graphics element (a circle) horizontally, while fitting it in measured height:

float RadioSize(SizeF size)
{
	return Math.Min(size.Width, size.Height);
}

public override SizeF GetDesiredSize(SizeF availableSize, IGraphics graphics)
{
	var s = base.GetDesiredSize(availableSize, graphics);
	s.Width += RadioSize(s);
	return s;
}

ArrangeComponents calls the base class to arrange its content on the right side of available space:

public override void ArrangeComponents(RectangleF availableSpace, IGraphics graphics)
{
	var radioSize = RadioSize(availableSpace.Size);
	availableSpace.X += radioSize;
	availableSpace.Width -= radioSize;
	base.ArrangeComponents(availableSpace, graphics);
}

Now override Draw and render standard radio button graphics on the left side of the component, and content on the right side:

public override void Draw(IGraphics graphics, RenderOptions options)
{
	var radioSize = RadioSize(Bounds.Size);
	var r = radioSize / 2 - 1;
	var cr = r - 1;

	graphics.FillEllipse(Brushes.White, Bounds.X + 1, Bounds.Y + 1, 2 * r, 2 * r);
	using (var pen = new System.Drawing.Pen(Color.Black, 0.1f))
		graphics.DrawEllipse(pen, Bounds.X + 1, Bounds.Y + 1, 2 * r, 2 * r);
	if (IsChecked)
		graphics.FillEllipse(Brushes.Black, Bounds.X + 2, Bounds.Y + 2, 2 * cr, 2 * cr);

	GraphicsState s = graphics.Save();
	graphics.TranslateTransform(radioSize - 1 + Bounds.X, Bounds.Y);
	Content.Draw(graphics, options);
	graphics.Restore(s);
}

We’ll want only one radio from a group to be selected. For our purposes we can count all radio buttons placed inside same stack panel as part of same group. Override the OnClick method to unselect all buttons in parent panel and select the clicked one:

protected override void OnClicked(EventArgs e)
{
	var parentStack = Parent as StackPanel;
	if (parentStack != null)
	{
		foreach (var child in parentStack.Components)
		{
			var radio = child as RadioButtonComponent;
			if (radio != null)
				radio.IsChecked = false;
		}
	}
	this.IsChecked = true;
}

That’s it, the radio button component is ready with just a screenful of code 🙂 Let’s check how it works by creating an OptionNode class that shows a group of radio buttons and exposes a property to access or change selected one:

class OptionNode : CompositeNode
{
}

You could create the stack panel and radio buttons from code if you need more dynamic configuration, e.g. one with variable number of radio buttons. For this example we’ll just load a fixed template consisting of four buttons from XML:

const string Template = @"
	<simplepanel>

        <shape name="" shape""="" shape="" roundrect""="">

		<border padding="" 2""="">

			<stackpanel name="" radiogroup""="" orientation="" vertical""="" spacing="" 1""="" horizontalalignment="" center""="">
				<radiobuttoncomponent padding="" 2""="">
					<radiobuttoncomponent.content>
						<text text="" option="" 1""="" font="" verdana,="" 3world,="" style="Bold&quot;&quot;">
					</text></radiobuttoncomponent.content>
				</radiobuttoncomponent>
				<radiobuttoncomponent padding="" 2""="">
					<radiobuttoncomponent.content>
						<text text="" option="" 2""="" font="" verdana,="" 3world,="" style="Bold&quot;&quot;">
					</text></radiobuttoncomponent.content>
				</radiobuttoncomponent>
				<radiobuttoncomponent padding="" 2""="">
					<radiobuttoncomponent.content>
						<text text="" option="" 3""="" font="" verdana,="" 3world,="" style="Bold&quot;&quot;">
					</text></radiobuttoncomponent.content>
				</radiobuttoncomponent>
				<radiobuttoncomponent padding="" 2""="">
					<radiobuttoncomponent.content>
						<text text="" option="" 4""="" font="" verdana,="" 3world,="" style="Bold&quot;&quot;">
					</text></radiobuttoncomponent.content>
				</radiobuttoncomponent>
			</stackpanel>

		</border>

    </shape></simplepanel>";

The template can be loaded using the XmlLoader class. We’ll also store a reference to the stack panel so we can access its child radio buttons:

public OptionNode()
{
	Load();
}

public OptionNode(Diagram d)
	: base(d)
{
	Load();
}

private void Load()
{
	Components.Add(XmlLoader.Load(Template, this, null));

	radioGroup = FindComponent("RadioGroup") as StackPanel;
}

StackPanel radioGroup;

Now implement a SelectedOption property that lets us select a radio button by its index. Define it as nullable integer so we can represent missing select too:

public int? SelectedOption
{
	get
	{
		for (int i = 0; i < radioGroup.Components.Count; i++)
		{
			var radioButton = (RadioButtonComponent)radioGroup.Components[i];
			if (radioButton.IsChecked)
				return i;
		}
		return null;
	}
	set
	{
		for (int i = 0; i < radioGroup.Components.Count; i++)
		{
			var radioButton = (RadioButtonComponent)radioGroup.Components[i];
			radioButton.IsChecked = value == i;
		}
	}
}

Let’s try it – create a few nodes and run the application, you’ll see the screen shown below:

var node1 = new OptionNode();
node1.Bounds = new RectangleF(20, 20, 30, 40);
node1.SelectedOption = 0;
diagram.Nodes.Add(node1);

var node2 = new OptionNode();
node2.Bounds = new RectangleF(90, 20, 30, 40);
node2.SelectedOption = 1;
diagram.Nodes.Add(node2);

var node3 = new OptionNode();
node3.Bounds = new RectangleF(20, 80, 30, 40);
node3.SelectedOption = null;
diagram.Nodes.Add(node3);

var node4 = new OptionNode();
node4.Bounds = new RectangleF(90, 80, 30, 40);
node4.SelectedOption = 3;
diagram.Nodes.Add(node4);

for (int i = 0; i < diagram.Nodes.Count - 1; i++)
	diagram.Factory.CreateDiagramLink(
		diagram.Nodes[i], diagram.Nodes[i + 1]);

Radio buttons in MindFusion diagram nodes

To be fair, this kind of nodes is simple enough to implement using standard TableNode class where radio button graphics are either custom drawn or set as Image inside table cells in first column, and text displayed in second column. However the radio buttons can be mixed with other components in CompositeNodes to implement more complex user interfaces than ones possible with tables.

For more information on MindFusion flow diagramming libraries for various desktop, web and mobile platforms, see MindFusion.Diagramming Pack page.

Enjoy!

Nested DataRanges in MindFusion Report Application

In this tutorial we are going to build a report with nested data ranges. In our case, we are going to retrieve all categories from the sample Norhtwind database. For each category we will get all products in it, which is the nested data range.

I. Preparing the Project

We create a new WinForms project and add a new DataSet from the Nortwind database. We choose all fields from the Categories and Products tables. Then we add a new item from the menu “Project -> Add -> New Item -> MindFusion Report” from the dialog that appears.

Adding the MindFusion Report item.

Adding the MindFusion Report item.

Drag and drop the nwindDataSet, CategoriesTableAdapter and ProductsTableAdapter on the report form. Finally, in the code behind fill the two adapters:

 productsTableAdapter1.Fill(nwindDataSet1.Products);
 categoriesTableAdapter1.Fill(nwindDataSet1.Categories); 

II. The Categories DataRange

We create the first data range by right clicking on the newly created report. There we see “Create Data Range from Data Source”. We choose the categories table and two fields – CategoryName and Picture. When the data range is generated we resize the picture to make it bigger.

Create DataRange context menu.

Create DataRange context menu.

III. Running the Report

We would use MindFusion ReportViewer to preview what we’ve done so far. We drag it from the Toolbox and place it on the form, which shows when the application runs. We compile the project and see that the Report1 class that we’ve created appears in the Toolbox, under the Data tab. This means we can create instances of our report just by drag and drop. We drag the Report1 icon and drop it on Form1. We have a report11 instance, which we assign to the Report property of the ReportView in the property grid.

Dragging the Report1 item.

Dragging the Report1 item.

Finally, we run the report:

report11.Run();

IV. The Nested DataRange

It’s time to create the second DataRange. We right-click the Report1 form and choose again “Create DataRange from Data Source.” This time we choose the Products table and we choose ProductName, UnitsInStock and UnitPrice fields. This time we check the “Generate Header” checkbox at the bottom. The second DataRange is ready. Nesting it is very easy. We resize the first DataRange to make it wider and just drag and drop the second DataRange in it. What is important is to set the MasterDetailRelation property. It must be the name of the relation between the two tables that provide data for the two DataRange-s. We can see it by clicking on the nwindDataSet -> “Edit DataSet with Designer. There we click on the relation between or two tables and see it is called “CategoriesProducts”. We place this name as a value to the MasterDetailRelation property.

Master detail relationship between the Products and Categories table.

Master detail relationship between the Products and Categories table.

V. Run the report

We run the report and see that everything is in place: the categories are listed with their picture, each category lists all its products.

VI. Style Adjustments

Finally let’s add some appearance optimizations that will make the report look better and thus be easier to read. First, we make the background of the Category label darker.

Then we make the product lines with alternating colors. This is done in the property grid for the dataRange2 object -> AlternatingBackground property.

We add a light gray border to the first data range with the Border property editor and we add a bottom margin of 30 mm. Here is the final look of the report:

Nested DataRange-s: the final report.

Nested DataRange-s: the final report.

You can download the sample from this link:

Download Nested DataRanges MindFusion Reporting Sample

More about MindFusion Reporting for WinForms component can be found here.

WPF Diagram Control, V3.3.1 and WinForms Diagram Control, V6.3.3

MindFusion is pleased to announce the new releases of two of its popular flowchart control: Diagramming for WinForms, V6.3.3 and Diagramming for WPF, V3.3.1. Both releases have similar new features, which are listed below:

Resize table columns and rows
Columns and rows of a TableNode can now be resized interactively if its AllowResizeColumns or AllowResizeRows properties are enabled. In order to resize, move the mouse pointer to the border line on column’s right side or row’s bottom side until it shows resize cursor and start dragging. The control raises TableColumnResizing and TableRowResizing events to let you validate new size or prevent resizing some elements. The TableColumnResized and TableRowResized events are raised after the operation completes.

Barcode nodes
The BarcodeNode class displays EAN, UPC or QR barcodes as node’s content. In-place edit operations let users enter new numeric codes for 1D codes or text strings for QR codes. The barcode format is specified via the Format property, the encoded number or text is set via Content, and color of 1D bars / 2D modules via BarColor.

Barcode diagram nodes

Barcode diagram nodes

New Features in Diagramming for WPF, V3.3.1

ShapeDesigner improvements

  • The ShapeDesigner control supports undo. Call its Undo or Redo methods to respectively undo or redo a change done to the designed shape.
  • ZoomFactor property added to ShapeDesigner. It also supports interactive zoom in/out via mouse wheel.
  • The SelectedElement property exposes the graphic element currently selected in ShapeDesigner canvas. You can bind to its stroke and brush properties to create alternative user interface for editing element attributes.

Miscellaneous

  • NodeConstraints.KeepInsideDiagram prevents a node from leaving diagram boundaries during user interaction (the older RestrictItemsToBounds property does not stop nodes from leaving diagram area but returns them to original position if dropped outside).
  • dashed selection frames are now drawn in two colors and should be visible on both the default white background and custom darker backgrounds. You can change the second dash color via HandlesVisualStyle.DashBackground property.
  • set the WhileModifying flag in RoutingOptions.TriggerRerouting to reroute links while users drag their end points.
  • custom connection point classes can now override ConnectionPoint.NearestAnchorPoint to implement custom anchor point selection logic; the method is now also called at the beginning of interactive link creation.

New in Diagramming for WinForms, V6.3.3

Miscellaneous

  • the component now selects better end points for auto-routed links when using WhileCreating and WhileModifying routing modes;
  • custom connection point classes can now override ConnectionPoint.NearestAnchorPoint to implement custom anchor point selection logic; the method is now also called at the beginning of interactive link creation.
  • NodeConstraints.KeepInsideDiagram prevents a node from leaving diagram boundaries during user interaction (the older RestrictItemsToBounds property does not stop nodes from leaving diagram area but returns them to original position if dropped outside).
  • SvgNode supports SVG images with vector-effect=’non-scaling-stroke’ stroke attributes.
  • improved default Pen for dash-frame selection handles style. Dashed frames should now be visible on both the default white background and custom darker backgrounds.
  • ZoomControl can now be used with other components by MindFusion and has been moved to MindFusion.Common.WinForms assembly and namespace.

You are welcome to download the trial version for each control from the following links:

Diagramming for WinForms, V6.3.3 Trial Version Download

Diagramming for WPF, V3.3.1 Trial Version Download

If you have questions or run into problems using the components you can use the Diagramming components forum, the help desk or write us at support@mindfusion.eu. Our support team will be pleased to help you.

About MindFusion.Diagramming for WinForms: A programming component that provides any WinForms application with a full set of features for creating and customizing all types of diagrams, flowcharts, schemes, hierarchies, trees, graphs etc. The control provides numerous ways to save and load a diagram, six auxiliary controls and more than 10 automatic graph layout algorithms. Diagram elements include scrollable tables, container nodes, multi-segment arrows, custom diagram item types and many more. Further details here.

Diagramming for WinForms is a royalty-free component, clients get 12 month upgrade subscription when buying a license. The source code is also available for purchase. Visit the buy page for a list with the current license prices.

About MindFusion.Diagramming for Wpf: Designed and developed to be easy to integrate, use, and customize, this native WPF flowchart component places at your disposal every single feature you would ever need to create diagrams, graphs, schemes, org charts, DB relationships, genealogy trees, class hierarchies and many more. Its long list of style options gives you complete control over the appearance of the diagram. With a set of eight automatic layouts you are sure to find the arrangement that suits perfectly your WPF application.

The diagram control boasts a long list of events, properties and methods for user interaction, item creation, data input and output. You can read the full features list here. The online demo shows samples that demonstrate various capabilities of the control. The licensing scheme and prices are uploaded at the buy page. Source code is also available.

Layout Management with the WinForms Dock Control

In this post we will show you how to build a sample application with customizable layout based on the WinForms Dock control, which is part of MindFusion WinForms pack. You can find further details about the control at its web page.

For the purpose of the demonstration we’ve chosen an entertaining topic – a cooking recipes electronic book. Our book so far has only three recipes, all for sweets. Lets start by looking at the

I. Architecture of the sample.

The sample has two custom classes – Ingredient and Recipe. Each Ingredient has quantity, name and an image, which illustrates it. The Recipe class holds a strongly typed list with Ingredient objects, the title for each recipe, an image for the recipe, an icon for the recipe and preparation instructions.

The application consists of the dock control, which contains four DockItem-s : for the recipes, for their images, for their ingredients and with the preparation instructions. Any dock item can be dragged, dropped, aligned, minimized, hidden etc.

The API of the cook book application

The API of the cook book application

II. The Dock Control.

We create an empty WinForms project and add the Dock control from the toolbox. The dock control fills the entire client area:

this.dockControl1.Dock = System.Windows.Forms.DockStyle.Fill;         
this.dockControl1.Location = new System.Drawing.Point(0, 0);           
this.dockControl1.Size = new System.Drawing.Size(784, 562);

These properties are set in the Property grid.

Each dock item is created with a title and id, which helps us identify it.

titleItem = new DockItem() { Header = "Recipe Name", Id = "Name" };
titleItem.DockStyle = DockStyle.Left;      
titleItem.Content = GetContent("Name");

The DockStyle property is responsible for the initial layout of the dock item. You can choose among various dock styles – fill, bottom, top and more.

The GetContent method is very important. It prepares the controls that will be placed into each dock item and returns the appropriate one according to the parameter.

Once the dock item is created, let’s not forget to add it:

dockControl1.Items.Add(titleItem);

III. Creating the Content.

Let’s see how the content for a dock item is created. Let’s take the grid with the ingredients. It is identified with the “Ingredients” id:

private Control GetContent(string contentType)
{
  Control control = null;
  ....
  else if (contentType == "Ingredients")
         {                
             grid = new DataGridView();
             grid.DefaultCellStyle.BackColor = Color.FromArgb(247, 226, 189);
             grid.Dock = DockStyle.Fill;
             grid.MultiSelect = false;
             grid.DataSource = selectedRecipe.Ingredients;
             grid.RowTemplate.MinimumHeight = 35;
            
             grid.RowHeadersWidthSizeMode =
            DataGridViewRowHeadersWidthSizeMode.DisableResizing;
             grid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
             grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
             grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
             grid.BackgroundColor = Color.FromArgb(253, 244, 247);

             control = grid; 
         }
...
return control;

The idea is clear: you create the control, which will be rendered in the dock item and assign it to DockItem.Content. Here we make different customization for the grid – we change the color of table rows, adjust the height, turn off multiple row select and more.

IV. Changing the Content.

We change the content by handling the SelectedIndexChanged event for the ListView, which lists our recipes. When the user selects a new list item, we extract its Recipe and change the content of the other DockItem-s:

selectedRecipe = recipes[recipesListView.SelectedIndices[0]];
tb.Text = selectedRecipe.Preparation;
grid.DataSource = selectedRecipe.Ingredients;
pictureBox.Image = Image.FromFile(selectedRecipe.ImageUrl);

Here is the final output of the sample application:

MindFusion WinForms Dock COntrol

A Sample electronic cook book based on MindFusion WinForms Dock Control

You can download the sample directly from this link:

MindFusion WinForms Layout Control Sample: Electronic Cook Book in C#

MindFusion WinForms Layout Control Sample: Electronic Cook Book in VB.NET

Enjoy the fast, easy and straight-forwarded manner in which you can create a WinForms application with a flexible layout and multiple panels.

MindFusion Charting for WinForms, V3.6

MindFusion has released a new version of its Charting for WinForms programming control. Here is an overview of the most important new features:

Custom Formatting of Labels
You can now use custom formatting for numeric labels for all chart types. The formatting is applied when you set the format of the label to NumberFormat.Custom. The properties that set the custom number format use the .net custom format strings.

Labels in a WinForms bar chart

Labels in a WinForms bar chart

Sorting of Bars
MindFusion WinFormsCharting control offers you greatly improved sorting of bar values in a bar chart. You can use the SortOrder property to sort the bars in each series or cluster in ascending or descending order. You can also sort each series/cluster with the SortSeriesBy property. You can preserve the color of each bar when sorted if you set the SortColor property to true.

A WinForms chart with sorted bars.

A WinForms chart with sorted bars.

License keys
MindFusion no longer provides separate trial and licensed versions of its components. Instead, you should set the LicenseKey property to disable the component’s evaluation mode and stop displaying trial messages. If your application has more than one Diagram instance or other controls by MindFusion, a single call to MindFusion.Licensing.LicenseManager.AddLicense(key) is enough to specify the key for all the controls. You can find your license key strings listed on the Keys & Downloads page at your http://clientsarea.eu account.

Miscellaneous

  • Bars are now outlined with the consecutive pen from the ChartPens collection rather than the AxisPen.
  • Drawing of line and area charts has greatly been improved – the control now draws only the visible portion of the chart rather than the whole chart, which was clipped to the visible rectangle.
  • AreaOpacity property added to radar charts.
  • AxesOnTop property in radar charts sets the order of drawing for the graphic and the axes.
  • SortYData and SortXData properties added to line charts.
  • In line charts colors can be sorted with line series or scatters when those get sorted.

The trial version is available for direct download from this link:

Download MindFusion.Charting for WinForms 3.6

Technical support is available at the forum, help desk or at e-mail support@mindfusion.eu. All inquiries are answered within hours of being received.

About MindFusion.Charting for WinForms: a professional programming component for WinForms, which lets you create remarkable charts fast and easy. The tool supports all major chart types – line, pie, radar and bar – and numerous variations of them – column, area, bubble, polar, doughnut etc. 3D charts are supported as well.

Charting for WinForms supports a rich user interaction model with features like zoom, hit testing, drill down, mouse dragging and more. You can use delegates to present mathematical functions, undefined values are also acceptable. Values can be data arrays or retrieved through a database.

The appearance of each chart is fully customizable. The control offers strong design-time support with custom collection editors and chart wizards. At your disposal is a set of predefined appearance themes and a theme editor tool. A full list of the features can be read here.