Charting for WinForms Programmer's Guide
Tutorial 1: Dashboard Layout

The Dashboard class represents a container of visual components such as plots, line graphics and text labels. Initially the dashboard contains two Panel classes to which its components should be added:

Let's start by dragging a Dashboard from the toolbox to our form, or alternatively add the control from code as shown below. In addition, set the Dock property to Fill to specify that the Dashboard should fill the entire form.

C#  Copy Code

dashboard = new Dashboard();
dashboard.Dock = DockStyle.Fill;
Controls.Add(dashboard);

Next, add a TextComponent that will display the dashboard's title. As LayoutPanel arranges its children in a vertical stack, the title will show at the top of the dashboard if it's the first component added. The following code creates and adds the title component, while also setting its appearance properties:

C#  Copy Code

dashboard.LayoutPanel.Children.Add(new TextComponent()
{
    Text = "TITLE TEXT",
    FontName = "Times New Roman",
    FontStyle = System.Drawing.FontStyle.Underline,
    FontSize = 20.4,
    HorizontalAlignment = LayoutAlignment.Center
});

We want to divide the remaining space in two rows and two columns, creating four cells in total. For that purpose, add a GridPanel, which can host multiple components in each of its cells. Its default constructor creates one GridRow and one GridColumn, i.e. it defines a single cell. We'll add one more row and column to respective collection properties of the panel:

C#  Copy Code

GridPanel grid = new GridPanel();
grid.Columns.Add(new GridColumn());
grid.Rows.Add(new GridRow());
dashboard.LayoutPanel.Children.Add(grid);


Now that we have defined our layout, lets create sample data series. MindFusion.Charting offers multiple classes you could use to represent data. All of them implement the Series interface, which you could implement in your own model classes if you prefer. The data class we'll use in this example is BarSeries; it takes a list of values to display as bar height, and automatically assigns bar positions at equal distances. It is convenient for creation of barcharts, but can actually be used with any other type of SeriesRenderer.

C#  Copy Code

BarSeries data1 = new BarSeries(
    new List<double>
    { 10, 15, 17, 16, 20, 15, 14, 13, 10, 17, 22, 25, 25, 25, 21, 21 },
    null, null,
    new List<string>() {"nov 2014", "dec 2014",
    "jan 2015", "feb 2015", "mar 2015", "apr 2015",
    "may 2015", "jun 2015", "jul 2015", "aug 2015",
    "sep 2015", "oct 2015", "nov 2015", "dec 2015",
    "jan 2016", "feb 2016"})
{
    Title = "LP Vinyl"
};

BarSeries data2 = new BarSeries(
    new List<double>() { 13, 13, 14, 16, 23, 25, 20, 19, 16, 17, 15, 13, 13, 17, 18, 21 },
    null, null)
{
    Title = "CD"
};

BarSeries data3 = new BarSeries(
    new List<double>() { 29, 28, 29, 31, 33, 35, 33, 27, 28, 28, 27, 29, 29, 31, 33, 29 },
    null, null)
{
    Title = "MP3"
};

You can see that BarSeries has two overloaded constructors. They let you specify different kinds of labels for the bars. MindFusion.Charting supports six kinds of labels displayed at various locations of rendered graphics. BarSeries lets you define inner labels drawn inside bars, top labels drawn above their bars, and X axis labels drawn along the axis. In the example above we'll only show axis labels, passing null for the other arguments. The Title values will be drawn by a LegendRenderer we are going to add later.

For visualizing the data, you must create an instance of a class derived from SeriesRenderer, each drawing a different kind of graphics. In this tutorial we'll see how several of them work, starting with a LineRenderer:

C#  Copy Code

LineRenderer lineRenderer = new LineRenderer(
    new System.Collections.ObjectModel.ObservableCollection<Series>() {data1, data2, data3}
);

Apart from the kind of graphics drawn, a SeriesRenderer lets you specify styling attributes for the graphic primitives, such as stroke and fill colors:

C#  Copy Code

lineRenderer.SeriesStyle = new PerSeriesStyle
{
    Strokes = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.Red),
        new MindFusion.Drawing.SolidBrush(Color.Blue),
        new MindFusion.Drawing.SolidBrush(Color.Green)
    }
};

SeriesRenderer objects must be placed inside Plot components. In this tutorial we'll use three different kinds of plots: Plot2D and Plot3D for drawing in 2D and 3D Cartesian coordinate systems respectively, and RadarPlot for drawing in polar coordinate system. For the LineRenderer we defined above, create a Plot2D and set its background:

C#  Copy Code

Plot2D linePlot = new Plot2D()
{
    Background = new MindFusion.Drawing.LinearGradientBrush(
        Color.White, Color.FromArgb(224, 224, 224))
    {
        Angle = 90
    }
};

Add the line renderer to plot's SeriesRenderers collection:

C#  Copy Code

linePlot.SeriesRenderers.Add(lineRenderer);

The data points renderered inside a Plot2D are scaled relatively to the Axis ranges defined via its XAxis and YAxis properties. For each Axis you can define its first and last visible values by setting MinValue and MaxValue properties. If their values are not specified, the control will calculate them automatically to fit all data provided through plot's SeriesRenderer objects. In adition, the Interval property lets you specify the distance between coordinate labels. The Axis class also has a Title property that lets you show description for the measure represented by an axis:

C#  Copy Code

linePlot.XAxis = new Axis();
linePlot.XAxis.MinValue = 0;
linePlot.XAxis.MaxValue = 15;
linePlot.XAxis.Interval = 1;
linePlot.XAxis.Title = "When?";

linePlot.YAxis = new Axis();
linePlot.YAxis.MinValue = 0;
linePlot.YAxis.MaxValue = 35;
linePlot.YAxis.Interval = 5;
linePlot.YAxis.Title = "How many thousands?";


If you want to show coordinates for the values drawn inside a plot, you must also add XAxisRenderer and YAxisRenderer components to the dashboard. Each of them can optionally display Axis coordinates and Title, ticks and data labels. In order to display data labels from a series, you must also set the LabelsSource property to either a SeriesRenderer or a Plot:

C#  Copy Code

XAxisRenderer xRenderer = new XAxisRenderer(linePlot.XAxis)
{
    LabelsSource = linePlot,
    ShowCoordinates = false
};

YAxisRenderer yRenderer = new YAxisRenderer(linePlot.YAxis);

In order to align axes to their plot, we'll add an intermediate GridPanel inside the top-left cell of the parent grid. We can do this on a couple of lines of code with the help of LayoutBuilder. That class lets us build layout fragments from several components and add them to a parent panel. In this case we'll call the CreatePlotWithBottomAndLeftAxes method to automatically create a GridPanel and add specified components to it. Other variants of this method can also automatically add the created panel to dashbaord' LayoutPanel, but in this tutorial we'll add the result to our custom child panel.

C#  Copy Code

LayoutBuilder builder = new LayoutBuilder(dashboard);
var panel1 = builder.CreatePlotWithBottomAndLeftAxes(
    linePlot, xRenderer, yRenderer);

We want to display this fragment inside top-left cell of the GridPanel we created earlier. This can be done by setting the GridRow and GridColumn properties defined in base Component class, which specify respectively the row or column index of a cell inside parent grid panel. The index is zero-based, so we set both properties to zero:

C#  Copy Code

panel1.GridRow = 0;
panel1.GridColumn = 0;
grid.Children.Add(panel1);

Following the same approach, we can display same data using a different kind of graphics and with different appearance attributes. Lets draw bars this time:

C#  Copy Code

BarRenderer barDataRenderer = new BarRenderer(
    new System.Collections.ObjectModel.ObservableCollection<Series>() { data1, data2, data3 }
);

We'll specify a different fill color for each series of bars by providing a list of brushes to PerSeriesStyle class.

C#  Copy Code

barDataRenderer.SeriesStyle = new PerSeriesStyle()
{
    Fills = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Purple)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Khaki)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Orange))
    }
};

You could add the BarRenderer to the same plot that now draws line graphics to create a mixed chart, but lets create a new plot for drawing bars only:

C#  Copy Code

Plot2D barPlot = new Plot2D();

Lets show axes for this plot too. Note that we are assigning same range properties as for the linechart, but through a new Axis instance. If you reused the Axis instance (by setting barPlot.XAxis = linePlot.XAxis), the plots would scroll together when the user pans any of them.

C#  Copy Code

barPlot.XAxis = new Axis();
barPlot.XAxis.MinValue = 0;
barPlot.XAxis.MaxValue = 15;
barPlot.XAxis.Interval = 1;
barPlot.XAxis.Title = "When?";

barPlot.YAxis = new Axis();
barPlot.YAxis.MinValue = 0;
barPlot.YAxis.MaxValue = 35;
barPlot.YAxis.Interval = 5;
barPlot.YAxis.Title = "How many thousands?";

Add the bar renderer to its plot:

C#  Copy Code

barPlot.SeriesRenderers.Add(barDataRenderer);


Use LayoutBuilder again to align axes to plot and add them to a grid panel:

C#  Copy Code

var panel2 = builder.CreatePlotWithBottomAndRightAxes(barPlot,
    new XAxisRenderer(barPlot.XAxis)
    {
        LabelsSource = linePlot,
        ShowCoordinates = false
    },
    new YAxisRenderer(barPlot.YAxis));

panel2.GridColumn = 1;
panel2.GridRow = 0;
grid.Children.Add(panel2);

Now lets draw a radar chart using RadarRenderer:

C#  Copy Code

RadarRenderer radarDataRenderer = new RadarRenderer(
    new System.Collections.ObjectModel.ObservableCollection<Series> { data1, data2, data3 }
);

Set the AreaOpacity property to fill the radar polygons using a semi-transparent fill:

C#  Copy Code

radarDataRenderer.AreaOpacity = 0.2;

Set the stroke and fill colors:

C#  Copy Code

radarDataRenderer.SeriesStyle = new PerSeriesStyle
{
    Strokes = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.PaleGreen),
        new MindFusion.Drawing.SolidBrush(Color.PaleTurquoise),
        new MindFusion.Drawing.SolidBrush(Color.PaleVioletRed)
    },
    Fills = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.PaleGreen),
        new MindFusion.Drawing.SolidBrush(Color.PaleTurquoise),
        MindFusion.Drawing.SolidBrush(Color.PaleVioletRed)
    },
    StrokeThicknesses = new List<double> { 5 }
};


Create a RadarPlot and set its data range:

C#  Copy Code

RadarPlot radarPlot = new RadarPlot();
radarPlot.DefaultAxis.MinValue = 0;
radarPlot.DefaultAxis.MaxValue = 35;
radarPlot.DefaultAxis.Interval = 5;

Set axis appearance attributes:

C#  Copy Code

radarPlot.AxisOptions.AxisStroke = new MindFusion.Drawing.SolidBrush(Color.LightGray);
radarPlot.DefaultAxis.Title = "";

Add the radar renderer to its plot:

C#  Copy Code

radarPlot.SeriesRenderers.Add(radarDataRenderer);

Add the plot to lower-left cell of parent grid:

C#  Copy Code

radarPlot.GridColumn = 0;
radarPlot.GridRow = 1;
grid.Children.Add(radarPlot);


We now have one last unused cell in our parent grid - lets add four more plots to it using third-level grid panel. In its first cell we'll show horizontal bars. In other ones we'll show 3D bars and also show how to set them up to scroll together. We'll again show the same data as in previous plots:

C#  Copy Code

BarRenderer horizontalBarRenderer = new BarRenderer(
    new System.Collections.ObjectModel.ObservableCollection<Series> { data1, data2, data3 }
);

Set their fills:

C#  Copy Code

horizontalBarRenderer.SeriesStyle = new PerSeriesStyle
{
    Fills = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Purple)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Khaki)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Orange))
    },
    StrokeThicknesses = new List<double> { 1 }
};

Set the bars orientation:

C#  Copy Code

horizontalBarRenderer.HorizontalBars = true;


Add the horizontal bar renderer to a plot:

C#  Copy Code

Plot2D plot4 = new Plot2D();
plot4.SeriesRenderers.Add(horizontalBarRenderer);

Set up plot's axes:

C#  Copy Code

plot4.YAxis = new Axis()
{
    MinValue = 0,
    MaxValue = 4,
    Interval = 1,
    Title = "Months"
};
plot4.XAxis = new Axis()
{
    MinValue = 0,
    MaxValue = 40,
    Interval = 5,
    Title = "Thousands"
};


Specify that this plot should scroll vertically:

C#  Copy Code

plot4.VerticalScroll = true;

Create axis renderers and set up their appearance:

C#  Copy Code

var xAxisRenderer =  new XAxisRenderer(plot4.XAxis)
{
    AxisStroke = new MindFusion.Drawing.SolidBrush(Color.Red),
    AxisStrokeThickness = 3,
    TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Orange),
    TitleFontName = "Courier New",
    TitleFontSize = 15
};

var yAxisRenderer = new YAxisRenderer(plot4.YAxis)
{
    AxisStroke = new MindFusion.Drawing.SolidBrush(Color.Red),
    AxisStrokeThickness = 3,
    TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Orange),
    TitleFontName = "Courier New",
    TitleFontSize = 15
};

Create a panel for the plot and axes:

C#  Copy Code

var panel6 = builder.CreatePlotWithTopAndLeftAxes(plot4, xAxisRenderer, yAxisRenderer);
panel6.GridColumn = 0;
panel6.GridRow = 0;

Now let's create some 3D bars:

C#  Copy Code

BarRenderer3D bar3DDataRenderer = new BarRenderer3D(
    new System.Collections.ObjectModel.ObservableCollection<Series> { data1, data2, data3 }
);

Set their style:

C#  Copy Code

bar3DDataRenderer.SeriesStyle = new PerSeriesStyle()
{
    Fills = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Purple)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Khaki)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Orange))
    },
    StrokeThicknesses = new List<double> { 1 }
};

Create a plot for 3D bars:

C#  Copy Code

Plot3D plot3d1 = new Plot3D();
plot3d1.SeriesRenderers.Add(bar3DDataRenderer);


Set up axes. We'll reuse these Axis instances later in order to implement synchronized scrolling in several plots:

C#  Copy Code

plot3d1.XAxis = new Axis()
{
    MinValue = 0,
    MaxValue = 5,
    Interval = 1,
    Title = "Months"
};
plot3d1.YAxis = new Axis()
{
    MinValue = 0,
    MaxValue = 40,
    Interval = 5,
    Title = "Thousands"
};


Add last plot and axes to the layout:

C#  Copy Code

var panel3 = builder.CreatePlotWithTopAndRightAxes(
    plot3d1,
    new XAxisRenderer(plot3d1.XAxis)
    {
        ShowCoordinates = false,
        TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Green),
        TitleFontName = "Verdana",
        TitleFontSize = 10
    },
    new YAxisRenderer(plot3d1.YAxis)
    {
    TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Green),
    TitleFontName = "Verdana",
    TitleFontSize = 10
});

Create a BarOverlayRenderer3D instance, which draws each series as a different row of bars:

C#  Copy Code

BarOverlayRenderer3D barOverlay3DRenderer = new BarOverlayRenderer3D(
    new System.Collections.ObjectModel.ObservableCollection<Series> { data1, data2, data3 }
);

Set its style:

C#  Copy Code

barOverlay3DRenderer.SeriesStyle = new PerSeriesStyle
{   
    Fills = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Purple)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Khaki)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Orange))
    },
    StrokeThicknesses = new List<double> { 1 }
};

Create a plot and reuse axes of previous one:

C#  Copy Code

Plot3D plot3d3 = new Plot3D();
plot3d3.SeriesRenderers.Add(barOverlay3DRenderer);
plot3d3.XAxis = plot3d1.XAxis;
plot3d3.YAxis = plot3d1.YAxis;


Add this plot and new axis renderers to the layout:

C#  Copy Code

var panel5 = builder.CreatePlotWithTopAndRightAxes(
    plot3d3,
    new XAxisRenderer(plot3d3.XAxis)
    {
        TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Purple),
        TitleFontName = "Courier New",
        TitleFontSize = 12
    },
    new YAxisRenderer(plot3d3.YAxis)
    {
        TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Purple),
        TitleFontName = "Courier New",
        TitleFontSize = 12
 });

And the last one will show data series as 3D bar stacks:

C#  Copy Code

BarStackRenderer3D barStack3DRenderer = new BarStackRenderer3D(
    new System.Collections.ObjectModel.ObservableCollection<Series> { data1, data2, data3 }
);

barStack3DRenderer.SeriesStyle = new PerSeriesStyle
{
    Fills = new List<MindFusion.Drawing.Brush>
    {
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Purple)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Khaki)),
        new MindFusion.Drawing.SolidBrush(Color.FromArgb(150, Color.Orange))
    },
    StrokeThicknesses = new List<double> { 1 }
};

Plot3D plot3d2 = new Plot3D();
plot3d2.SeriesRenderers.Add(barStack3DRenderer);
plot3d2.XAxis = plot3d1.XAxis;

Here we want a different Y axis range because the stacks will get higher than standalone bars from older plots:

C#  Copy Code

plot3d2.YAxis = new Axis()
{
    MinValue = 0,
    MaxValue = 100,
    Interval = 5,
    Title = "Thousands"
};

Add a panel for the last plot and axes:

C#  Copy Code

var panel4 = builder.CreatePlotWithTopAndLeftAxes(
    plot3d2,
    new XAxisRenderer(plot3d2.XAxis)
    {
        TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Blue),
        TitleFontName = "Times New Roman",
        TitleFontSize = 15
    },
    new YAxisRenderer(plot3d2.YAxis)
    {
        TitleBrush = new MindFusion.Drawing.SolidBrush(Color.Blue),
        TitleFontName = "Times New Roman",
        TitleFontSize = 15,
        TitleFontStyle = FontStyle.Bold
});

Set the grid positions for all child panels:

C#  Copy Code

panel3.GridColumn = 1;
panel3.GridRow = 0;
panel4.GridColumn = 0;
panel4.GridRow = 1;
panel5.GridColumn = 1;
panel5.GridRow = 1;

Finally, arrange them in a grid in the lower-right corner of parent grid:

C#  Copy Code

GridPanel miniGrid = new GridPanel();
miniGrid.Columns.Add(new GridColumn());
miniGrid.Rows.Add(new GridRow());
miniGrid.Children.Add(panel3);
miniGrid.Children.Add(panel4);
miniGrid.Children.Add(panel5);
miniGrid.Children.Add(panel6);

miniGrid.GridColumn = 1;
miniGrid.GridRow = 1;
grid.Children.Add(miniGrid);

Now create a legend and the dashboard is complete. The legend will display the series titles we assigned earlier:

C#  Copy Code

LegendRenderer legend = new LegendRenderer();
legend.Content.Add(lineRenderer);
dashboard.RootPanel.Children.Add(legend);

If you run the application, you should now see this image: