ASP.NET Bar Chart with Custom Labels

In this blog post we will look on how to build a bar chart with multiple series, custom X-labels and inner labels using MindFusion.Charting for ASP.NET control.

1. General Appearance of the Chart

We want to show three data series and we want to show the first values of each series in a cluster, then second cluster with the second values of each series etc. In order to render the bars in this order we set the BarType property to BarType.Clustered3D. We correct the Depth3D to make the bars less thick and increase the distance between each series with DistBtwBars. We make the bars wider, setting BarWidth to 30.

    //general appearance
    BarChart1.BarType = MindFusion.Charting.BarType.Clustered3D;
    BarChart1.BarWidth = 30f;        
    BarChart1.Depth3D = 15f;
    BarChart1.DistBtwBars = 10f;

2. Grid

Our bars are vertical e.g. it is a column chart, so we would like to have a horizontal grid. We set the GridType property to GridType.HorScale and we set the AltGridBrush and GridBruh to different gray colors to give us nice alternating grid stripes. For the outlining of the whole plot area we use the PlotAreaOutlinePen.

    //grid settings
    BarChart1.GridBrush= new MindFusion.Drawing.SolidBrush(System.Drawing.Color.FromArgb(242, 239, 224));
    BarChart1.GridPen= new MindFusion.Drawing.Pen(System.Drawing.Color.FromArgb(204, 196, 185));
    BarChart1.GridType = MindFusion.Charting.GridType.HorScale;
    BarChart1.PlotAreaOutlinePen = new MindFusion.Drawing.Pen(System.Drawing.Color.FromArgb(204, 196, 185));

3. Labels Inside Bars

The labels inside bars show the data of each bar – this is done by setting the InnerLabelType property to LabelType.Data. We use a white LabelBrush to draw the labels and set their InnerLabelBorder to Border.RoundedRect. The background of the inner labels is set to gray.

    //inner labels settings
    BarChart1.InnerLabelBackground = new MindFusion.Drawing.SolidBrush(System.Drawing.Color.FromArgb(112, 128, 144));
    BarChart1.InnerLabelBorder = MindFusion.Charting.Border.RoundedRect;
    BarChart1.InnerLabelType = MindFusion.Charting.LabelType.Data; 
    BarChart1.LabelBrush = new MindFusion.Drawing.SolidBrush(System.Drawing.Color.FromArgb(255, 255, 255));

4. Title

We want not only title but also second title – or a subtitle. That’s why we use both the TitleText and SubTitleText properties. We leave no distance between them – we set SubTitleOffset to 0. But we would like the two titles to be well above the plot area, that’s why we set bigger TitleOffset.

    //title and subtitle
    BarChart1.SubTitleOffset = 30;
    BarChart1.SubTitleText = "all major continents";
    BarChart1.TitleOffset = 0;
    BarChart1.TitleText = "Sales";

5. Labels at the Axes

The X-labels are custom text – we set them with XLabels. The title of the Y-axis is rotated, so we use LabelOrientation.BottomToTop.

    //axes labels settings
    BarChart1.XAxisSettings.LabelSeriesIndex = 1;
    BarChart1.XLabels.Clear();
    BarChart1.XLabels.Add(new List(){ "Europe", "Asia", "North America", "South America"});
    
    BarChart1.YAxisSettings.TitleLabel = "in mlns. USD";
    BarChart1.YAxisSettings.TitleLabelOrientation = MindFusion.Charting.LabelOrientation.BottomToTop;

Here is a screenshot of the final chart:

ASP.NET Bar Chart with Custom Labels

ASP.NET Bar Chart with Custom Labels

The sample can be downloaded from here:

Download ASP.NET Bar Chart with Labels Sample

The trial version of MindFusion.Charting for ASP.NET can be downloaded directly from the link below. It contains many more samples and tutorials:

Download MindFusion.Charting for WebForms Trial Version

About MindFusion.Charting for WebForms: A powerful WebForms control that lets programmers add compelling charts in any ASP.NET application. With the component you can design and create any bar, column, line, area, scatter, bubble, radar, polar, doughnut and pie chart. Its elegant API includes data binding, themes, hit-testing, zoom, drill-down, wizards and many more.

The control takes advantage of the rich capabilities of Microsoft WebForms but its functionality does not bring excess complexity. The API is very easy to understand, all the features are duly documented. Numerous samples and step-by-step tutorials let you learn quickly how to tailor the tool in order to meet best your needs.

Details about the various features of the control are provided here. The online demo will give you an overview of the control’s rich capabilities. Licenses, prices and discounts are listed at the buy page.

Create diagram connectors using drag and drop

In this post we show how to create diagram links via drag-and-drop operation from a NodeListView used as a palette. Since the NodeListView control can contain only DiagramNode objects, we will create a custom node shape representing a connector. Once the dummy connector node is created, the NodeCreated event handler will replace it with a DiagramLink. In addition, the handler will find nearby nodes and automatically connect the link to them.

Let’s start with a new WPF project. Add a reference to mindfusion.diagramming.dll, and add Diagram and NodeListView controls to the window:

<ScrollViewer
	Grid.Column="0"
	Focusable="False"
	HorizontalScrollBarVisibility="Visible">
	
	<diag:Diagram
		x:Name="diagram"
		AllowDrop="True"
		NodeCreated="OnNodeCreated"
		NodeModified="OnNodeModified">
	</diag:Diagram>
	
</ScrollViewer>

<diag:NodeListView
	x:Name="nodeList"
	Grid.Column="1">
</diag:NodeListView>

In the Window constructor, create a custom node shape that will represent connectors:

var connectorShape = new Shape(
	null, // no borders
	new[] // decorations
	{
		new LineTemplate(10, 10, 10, 50),
		new LineTemplate(10, 50, 90, 50),
		new LineTemplate(90, 50, 90, 90)
	},
	null,
	FillRule.Nonzero, "Connector");

Add a few nodes to the NodeListView, along with the dummy connector node:

var item1 = new ShapeNode { Shape = Shapes.Rectangle };
NodeListView.SetLabel(item1, "Activity");
nodeList.Items.Add(item1);

var item2 = new ShapeNode { Shape = Shapes.Decision };
NodeListView.SetLabel(item2, "Decision");
nodeList.Items.Add(item2);

var item3 = new ShapeNode { Shape = connectorShape };
NodeListView.SetLabel(item3, "Connector");
nodeList.Items.Add(item3);

In the NodeCreated event handler, check if the newly created node represents a connector, and replace it with a DiagramLink:

var node = e.Node as ShapeNode;
if (node != null)
{
	if (node.Shape.Id == "Connector")
	{
		// replace the dummy connector node with a DiagramLink
		var bounds = node.Bounds;
		diagram.Items.Remove(node);

		var link = diagram.Factory.CreateDiagramLink(
			bounds.TopLeft, bounds.BottomRight);
		link.SegmentCount = 3;
		link.Shape = LinkShape.Cascading;

		ConnectToNearbyNode(link);
	}
	...
}

Otherwise if it is a regular node, set its anchor points and connect it to nearby unconnected links, if there are any:

else
{
	node.AnchorPattern = AnchorPattern.Decision2In2Out;
	node.Effects.Add(new GlassEffect());
	ConnectToNearbyLink(node);
}

The ConnectToNearbyNode method uses a LINQ query to find nodes in the vicinity of the link’s start or end point. If one is found at a distance shorter than 120 points, it is set as the link’s Origin or Destination.

private void ConnectToNearbyNode(DiagramLink link)
{
	// connect to a nearby origin node
	var origin = diagram.Nodes.OrderBy(n =>
		Utilities.Distance(n.GetCenter(), link.StartPoint)).FirstOrDefault();

	if (origin != null)
	{
		var distance = Utilities.Distance(origin.GetCenter(), link.StartPoint);
		if (distance < 120)
		{
			link.Origin = origin;
			link.Route();
		}
	}

	// connect to a nearby destination node
	var destination = diagram.Nodes.Where(n => n != origin).OrderBy(n =>
		Utilities.Distance(n.GetCenter(), link.EndPoint)).FirstOrDefault();

	if (destination != null)
	{
		var distance = Utilities.Distance(destination.GetCenter(), link.EndPoint);
		if (distance < 120)
		{
			link.Destination = destination;
			link.Route();
		}
	}
}

Similarly, the ConnectToNearbyLink method finds a nearby unconnected link and sets the specified node as the link’s Origin or Destination.

private void ConnectToNearbyLink(DiagramNode node)
{
	var outLink = diagram.Links.Where(l => l.Origin is DummyNode).OrderBy(l =>
		Utilities.Distance(node.GetCenter(), l.StartPoint)).FirstOrDefault();

	if (outLink != null)
	{
		var distance = Utilities.Distance(node.GetCenter(), outLink.StartPoint);
		if (distance < 90)
		{
			outLink.Origin = node;
			outLink.Route();
			return;
		}
	}

	var inLink = diagram.Links.Where(l => l.Destination is DummyNode).OrderBy(l =>
		Utilities.Distance(node.GetCenter(), l.EndPoint)).FirstOrDefault();

	if (inLink != null)
	{
		var distance = Utilities.Distance(node.GetCenter(), inLink.EndPoint);
		if (distance < 90)
		{
			inLink.Destination = node;
			inLink.Route();
			return;
		}
	}
}

The result of several drag and drop operation is displayed below.

The complete sample project is available for download here:
https://mindfusion.eu/_samples/LinkDragDrop.zip

Enjoy!

Custom appearance of WPF Resource view

Continuing from the previous post we will now customize the appearance of the Resource view to achieve a more aesthetically pleasing presentation. The appearance of the view is customized by setting various properties of the Calendar.ResourceViewSettings object.

We will build our presentation on the Silver theme by reducing the sharpness and contrast of colors and making the font uniform across the entire view. The customization process is divided to the following steps:

Customize the view background.

calendar.ResourceViewSettings.CalendarStyle.Background = Brushes.White;

Customize the row headers.

calendar.ResourceViewSettings.ExpandableRows = false;
calendar.ResourceViewSettings.HeaderStyle.FontFamily = headerFont;
calendar.ResourceViewSettings.HeaderStyle.FontSize = 13;
calendar.ResourceViewSettings.HeaderStyle.Foreground = new SolidColorBrush(Color.FromArgb(255, 64, 64, 64));
calendar.ResourceViewSettings.HeaderStyle.Background = Brushes.White;
calendar.ResourceViewSettings.HeaderStyle.BorderBrush = borderBrush;
calendar.ResourceViewSettings.HeaderStyle.BorderThickness = new Thickness(0, 0, 0, 1);

Customize the view cells.

calendar.ResourceViewSettings.CellStyle.Background = Brushes.White;
calendar.ResourceViewSettings.CellStyle.BorderBrush = borderBrush;
calendar.ResourceViewSettings.WeekendStyle.Background = new SolidColorBrush(Color.FromArgb(255, 250, 250, 250));
calendar.ResourceViewSettings.WeekendStyle.BorderBrush = borderBrush;

Customize the view timelines.

calendar.ResourceViewSettings.BottomTimelineSettings.CalendarStyle.Background = Brushes.White;
calendar.ResourceViewSettings.BottomTimelineSettings.CalendarStyle.BorderBrush = borderBrush;
calendar.ResourceViewSettings.BottomTimelineSettings.CalendarStyle.FontFamily = headerFont;
calendar.ResourceViewSettings.BottomTimelineSettings.CalendarStyle.FontSize = 13;
calendar.ResourceViewSettings.BottomTimelineSettings.CalendarStyle.FontWeight = FontWeights.Normal;
calendar.ResourceViewSettings.BottomTimelineSettings.NowFillBrush = Brushes.Transparent;
calendar.ResourceViewSettings.MiddleTimelineSettings.CalendarStyle.Background = Brushes.White;
calendar.ResourceViewSettings.MiddleTimelineSettings.CalendarStyle.BorderBrush = borderBrush;
calendar.ResourceViewSettings.MiddleTimelineSettings.CalendarStyle.FontFamily = headerFont;
calendar.ResourceViewSettings.MiddleTimelineSettings.CalendarStyle.FontSize = 13;
calendar.ResourceViewSettings.MiddleTimelineSettings.CalendarStyle.FontWeight = FontWeights.Normal;
calendar.ResourceViewSettings.MiddleTimelineSettings.NowFillBrush = Brushes.Transparent;

Note that the font, headerFont and borderBrush variables are defined as follows:

FontFamily font = new FontFamily("Segoe UI");
FontFamily headerFont = new FontFamily("Segoe UI Light");
Brush borderBrush = new SolidColorBrush(Color.FromArgb(255, 224, 224, 224));

The final result is displayed below.

scheduling-resourceviewappearance

The complete sample project is available for download here:
https://mindfusion.eu/_samples/WpfPlannerResourceViewAppearance.zip

You can get the trial version of MindFusion.Scheduling for WPF from this link:
https://mindfusion.eu/WpfPlannerTrial.zip

Enjoy!

Line Chart with a Separator in WinForms

In today’s post we’ll show how to build a line chart with several line series and a separator line. The separator line is drawn at a given height and divides the chart into two halves.

The Line Series

The line series are three, so we must add three lists with data to the XData and YData properties. We can do this in three ways: type the values in design time, write them in code or use data binding. In design time we use the built-in collection editors of the control:

The Series collection editor

The Series collection editor

In code we make lists with the data and add them to XData or YData:

lineChart1.YData.Clear();
lineChart1.YData.Add(new List { 45, 64, 38.2, 33.03, 56, 68, 39, 42 });
lineChart1.YData.Add(new List { 34, 42, 28, 42, 35, 31, 62, 55 });
lineChart1.YData.Add(new List { 22, 19, 32, 28, 17, 25, 31, 36 });

If you want to use data binding then set DataSource to the name of the data source, DataMember to specify the name of the table which will supply the data and XDataFields/YDataFields to provide the name(s) of the fields. In this case we will require three data base columns for each of the three series – separate the names with a comma e.g.

lineChart1.YDataFields = "Sales1,Sales2,Sales3";

For XData we use the same list because we want the series to appear exactly under each other:

lineChart1.XData.Clear();
lineChart1.XData.Add(new List { 10, 20, 30, 40, 50, 60, 70, 80 });
lineChart1.XData.Add(new List { 10, 20, 30, 40, 50, 60, 70, 80 });
lineChart1.XData.Add(new List { 10, 20, 30, 40, 50, 60, 70, 80 });

The Series

We set LineType to Line and Scatter because we want to show scatters at data points:

lineChart1.LineType = MindFusion.Charting.LineTypes.Line |  
MindFusion.Charting.LineTypes.Scatter;

The LineTypes enumeration allows bit wise combining of its members. We use ChartPens to set the pens for the line series. ShapesPens and ShapeBrushes set the pens for the outline and the brushes for filling the scatters:

lineChart1.ChartPens.Add(new MindFusion.Drawing.Pen(Color.FromArgb(102, 205, 170), 6));
lineChart1.ShapePens.Add(new MindFusion.Drawing.Pen(Color.FromArgb(102, 205, 170), 2));   

The pens for the series are thicker than the pens for the scatters. The fill brush is slightly lighter:

lineChart1.ShapeBrushes.Add(new MindFusion.Drawing.SolidBrush(Color.FromArgb(175, 251, 175)));

Finally – we set the size of the shapes and their type:

lineChart1.Shapes.Add(MindFusion.Charting.Shape.Circle);
lineChart1.ShapeSizes.Add(10);

The Separator

There are two ways to draw the separator line.

The first is to add a custom summary value. In this case the separator line will be drawn from the
smallest X-data value to the biggest one, parallel to the axes.

lineChart1.AddCustomSummary(40.0, "");

The summary line is drawn with shapes at both ends, but we can hide them by setting their size to 0:

lineChart1.SummaryBrushes.Add(new MindFusion.Drawing.SolidBrush(Color.Gray));
lineChart1.SummaryShapeSizes.Add(0);

The second way to draw the separator line is to add it as a 4th series in the XData and YData lists and add a pen for it in the ChartPens list. The advantage is that we can make the line as long as we want, in our case – as long as the length of the X-axis:

lineChart1.XData.Add(new List { 0, 90 });

The Legend

We use LegendLabels to add the labels for the line series:

lineChart1.LegendLabels = new List() { "2010", "2011", "2012" };

We set the background of the legend, but we don’t have to add any brushes or shapes for the legend items – they are taken automatically from the line series settings:

lineChart1.LegendBackgroundBrush = new MindFusion.Drawing.SolidBrush(Color.FromArgb(253, 253,  
253));


The Grid

The GridType is GridType.HorScale. We use GridBrush, AltGridBrush and GridPen to set the colors for the grid.

lineChart1.GridPen = new MindFusion.Drawing.Pen(Color.FromArgb(200, 200, 200));
lineChart1.GridPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
lineChart1.GridType = MindFusion.Charting.GridType.HorScale;

If you make the changes to the chart in code, don’t forget to call UpdateChart() to make sure the
control knows changes have happened and the chart must be updated.

Here is the final chart:

A line chart with a separator line.

A line chart with a separator line.

The sample is available for download from here:

Download Line Chart with Separator Line Sample

You can download the trial version of the component with extensive documentation and many other samples from here:

Download MindFusion.Charting for WinForms Trial Version

PERT network charts in WPF

In this post we show how to build a PERT network chart using MindFusion.Diagramming for WPF.

First, create a new WPF project and add a mindfusion.diagramming.wpf.dll reference. Add the appropriate Xaml namespace to the window Xaml file:

xmlns:diag="http://mindfusion.eu/diagramming/wpf"

Add a ScrollViewer and Diagram to the window markup:

<scrollviewer>
	<diag:diagram x:name="diagram">
</diag:diagram></scrollviewer>

There are two conventions for drawing PERT networks: activity on arrow and activity on node. Activity on arrow shows activity information on arrows, and the nodes they connect represent project milestones. Activity on node shows activity information on nodes and the arrows between them display relationships. We will use the first convention.

Let’s define a model class to hold activity data:

public class Activity
{
	// activity name
	public string Name { get; set; }

	// start milestone name
	public string From { get; set; }

	// end milestone  name
	public string To { get; set; }

	// time in months
	public int Time { get; set; }
}

Define a simple PERT project consisting of five milestones and six activities:

var network = new[]
{
	new Activity { Name = "A", From = "10", To = "30", Time = 3 },
	new Activity { Name = "B", From = "10", To = "20", Time = 4 },
	new Activity { Name = "C", From = "20", To = "50", Time = 3 },
	new Activity { Name = "D", From = "30", To = "40", Time = 1 },
	new Activity { Name = "E", From = "30", To = "50", Time = 3 },
	new Activity { Name = "F", From = "40", To = "50", Time = 3 }
};

Create a diagram node for each milestone in the project:

// create diagram nodes for each milestone
foreach (var activity in network)
{
	if (diagram.FindNodeById(activity.From) == null)
	{
		var node = diagram.Factory.CreateShapeNode(bounds, Shapes.Ellipse);
		node.Id = node.Text = activity.From;
	}

	if (diagram.FindNodeById(activity.To) == null)
	{
		var node = diagram.Factory.CreateShapeNode(bounds, Shapes.Ellipse);
		node.Id = node.Text = activity.To;
	}
}

Create a diagram link for each activity and display the activity name and estimated time as link labels:

// create a diagram link for each activity
foreach (var activity in network)
{
	var link = diagram.Factory.CreateDiagramLink(
		diagram.FindNodeById(activity.From),
		diagram.FindNodeById(activity.To));

	var label1 = link.AddLabel(activity.Name);
	label1.RelativeTo = RelativeToLink.Segment;
	label1.Index = 0;
	label1.VerticalOffset = -20;
	label1.TextBrush = new SolidColorBrush(Colors.Red);

	var label2 = link.AddLabel(string.Format("t={0}mo", activity.Time));
	label2.RelativeTo = RelativeToLink.Segment;
	label2.Index = 0;
	label2.VerticalOffset = 10;
	label2.TextBrush = new SolidColorBrush(Colors.Blue);
}

Use automatic graph layout algorithm to arrange the PERT network chart:

// arrange the diagram from left to right
var layout = new LayeredLayout();
layout.Margins = new Size(100, 100);
layout.EnforceLinkFlow = true;
layout.Orientation = Orientation.Horizontal;
layout.Arrange(diagram);

The final result is displayed below.
PERT network chart

For more information on the PERT methodology, see this Wikipedia article:
http://en.wikipedia.org/wiki/PERT

The complete sample project is available for download here:
https://mindfusion.eu/_samples/PertNetwork.zip

All MindFusion.Diagramming libraries expose the same programming interface, so most of the sample code shown above will work with only a few modifications in Windows Forms, ASP.NET, Silverlight and Java versions of the control.

Enjoy!