ChartDirector 6.2 (.NET Edition)

Track Box with Floating Legend (Windows)




NOTE: This section describes Track Box with Floating Legend for Windows applications only. For web applications, please refer to Track Box with Floating Legend (Web).

NOTE: For conciseness, some of the following descriptions only mention WinChartViewer. Those descriptions apply to WPFChartViewer as well.

This sample program demonstrates a track cursor programmed with the following features:

The code first draws the chart. Then in the WinChartViewer.MouseMovePlotArea event handler, the track cursor is drawn to reflect the mouse position. The track cursor is configured to automatically hide itself when the mouse leaves the plot area.

The trackBoxLegend method is the routine that draws the track cursor. Its key elements are:

Source Code Listing

[Windows Forms - C# version] NetWinCharts\CSharpWinCharts\frmtrackbox.cs
using System;
using System.Collections;
using System.Windows.Forms;
using ChartDirector;

namespace CSharpChartExplorer
{
    public partial class FrmTrackBox : Form
    {
        public FrmTrackBox()
        {
            InitializeComponent();
        }

        private void FrmTrackBox_Load(object sender, EventArgs e)
        {
            // The data for the bar chart
            double[] data0 = {100, 125, 245, 147, 67};
            double[] data1 = {85, 156, 179, 211, 123};
            double[] data2 = {97, 87, 56, 267, 157};
            string[] labels = {"Mon", "Tue", "Wed", "Thur", "Fri"};

            // Create a XYChart object of size 540 x 375 pixels
            XYChart c = new XYChart(540, 375);

            // Add a title to the chart using 18pt Times Bold Italic font
            c.addTitle("Average Weekly Network Load", "Times New Roman Bold Italic", 18);

            // Set the plotarea at (50, 55) and of 440 x 280 pixels in size. Use a vertical gradient color
            // from light blue (f9f9ff) to blue (6666ff) as background. Set border and grid lines to white
            // (ffffff).
            c.setPlotArea(50, 55, 440, 280, c.linearGradientColor(0, 55, 0, 335, 0xf9f9ff, 0x6666ff), -1,
                0xffffff, 0xffffff);

            // Add a legend box at (50, 28) using horizontal layout. Use 10pt Arial Bold as font, with
            // transparent background.
            c.addLegend(50, 28, false, "Arial Bold", 10).setBackground(Chart.Transparent);

            // Set the x axis labels
            c.xAxis().setLabels(labels);

            // Draw the ticks between label positions (instead of at label positions)
            c.xAxis().setTickOffset(0.5);

            // Set axis label style to 8pt Arial Bold
            c.xAxis().setLabelStyle("Arial Bold", 8);
            c.yAxis().setLabelStyle("Arial Bold", 8);

            // Set axis line width to 2 pixels
            c.xAxis().setWidth(2);
            c.yAxis().setWidth(2);

            // Add axis title
            c.yAxis().setTitle("Throughput (MBytes Per Hour)");

            // Add a multi-bar layer with 3 data sets
            BarLayer layer = c.addBarLayer2(Chart.Side);
            layer.addDataSet(data0, 0xff0000, "Server #1");
            layer.addDataSet(data1, 0x00ff00, "Server #2");
            layer.addDataSet(data2, 0xff8800, "Server #3");

            // Set bar border to transparent. Use glass lighting effect with light direction from left.
            layer.setBorderColor(Chart.Transparent, Chart.glassEffect(Chart.NormalGlare, Chart.Left));

            // Configure the bars within a group to touch each others (no gap)
            layer.setBarGap(0.2, Chart.TouchBar);

            // Assign the chart to the WinChartViewer
            winChartViewer1.Chart = c;
        }

        //
        // Draw track cursor when mouse is moving over plotarea
        //
        private void winChartViewer1_MouseMovePlotArea(object sender, MouseEventArgs e)
        {
            WinChartViewer viewer = (WinChartViewer)sender;
            trackBoxLegend((XYChart)viewer.Chart, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY);
            viewer.updateDisplay();

            // Hide the track cursor when the mouse leaves the plot area
            viewer.removeDynamicLayer("MouseLeavePlotArea");
        }

        //
        // Draw the track box with legend
        //
        private void trackBoxLegend(XYChart c, int mouseX, int mouseY)
        {
            // Clear the current dynamic layer and get the DrawArea object to draw on it.
            DrawArea d = c.initDynamicLayer();

            // The plot area object
            PlotArea plotArea = c.getPlotArea();

            // Get the data x-value that is nearest to the mouse
            double xValue = c.getNearestXValue(mouseX);

            // Compute the position of the box. This example assumes a label based x-axis, in which the
            // labeling spacing is one x-axis unit. So the left and right sides of the box is 0.5 unit from
            // the central x-value.
            int boxLeft = c.getXCoor(xValue - 0.5);
            int boxRight = c.getXCoor(xValue + 0.5);
            int boxTop = plotArea.getTopY();
            int boxBottom = plotArea.getBottomY();

            // Draw the track box
            d.rect(boxLeft, boxTop, boxRight, boxBottom, 0x000000, Chart.Transparent);

            // Container to hold the legend entries
            ArrayList legendEntries = new ArrayList();

            // Iterate through all layers to build the legend array
            for(int i = 0; i < c.getLayerCount(); ++i) {
                Layer layer = c.getLayerByZ(i);

                // The data array index of the x-value
                int xIndex = layer.getXIndexOf(xValue);

                // Iterate through all the data sets in the layer
                for(int j = 0; j < layer.getDataSetCount(); ++j) {
                    ChartDirector.DataSet dataSet = layer.getDataSetByZ(j);

                    // Build the legend entry, consist of the legend icon, the name and the data value.
                    double dataValue = dataSet.getValue(xIndex);
                    if ((dataValue != Chart.NoValue) && (dataSet.getDataColor() != Chart.Transparent)) {
                        legendEntries.Add(dataSet.getLegendIcon() + " " + dataSet.getDataName() + ": " +
                            c.formatValue(dataValue, "{value|P4}"));
                    }
                }
            }

            // Create the legend by joining the legend entries
            if (legendEntries.Count > 0) {
                legendEntries.Reverse();
                string legend = "<*block,bgColor=FFFFCC,edgeColor=000000,margin=5*><*font,underline=1*>" +
                    c.xAxis().getFormattedLabel(xValue) + "<*/font*><*br*>" + String.Join("<*br*>", (string[])
                    legendEntries.ToArray(typeof(string))) + "<*/*>";

                // Display the legend at the bottom-right side of the mouse cursor, and make sure the legend
                // will not go outside the chart image.
                TTFText t = d.text(legend, "Arial Bold", 8);
                t.draw(Math.Min(mouseX + 12, c.getWidth() - t.getWidth()), Math.Min(mouseY + 18, c.getHeight()
                     - t.getHeight()), 0x000000, Chart.TopLeft);
            }
        }
    }
}

[Windows Forms - VB Version] NetWinCharts\VBNetWinCharts\frmtrackbox.vb
Imports ChartDirector
Imports System.Collections

Public Class FrmTrackBox

    Private Sub FrmTrackBox_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' The data for the bar chart
        Dim data0() As Double = {100, 125, 245, 147, 67}
        Dim data1() As Double = {85, 156, 179, 211, 123}
        Dim data2() As Double = {97, 87, 56, 267, 157}
        Dim labels() As String = {"Mon", "Tue", "Wed", "Thur", "Fri"}

        ' Create a XYChart object of size 540 x 375 pixels
        Dim c As XYChart = New XYChart(540, 375)

        ' Add a title to the chart using 18pt Times Bold Italic font
        c.addTitle("Average Weekly Network Load", "Times New Roman Bold Italic", 18)

        ' Set the plotarea at (50, 55) and of 440 x 280 pixels in size. Use a vertical gradient color from
        ' light blue (f9f9ff) to blue (6666ff) as background. Set border and grid lines to white (ffffff).
        c.setPlotArea(50, 55, 440, 280, c.linearGradientColor(0, 55, 0, 335, &Hf9f9ff, &H6666ff), -1, _
            &Hffffff, &Hffffff)

        ' Add a legend box at (50, 28) using horizontal layout. Use 10pt Arial Bold as font, with transparent
        ' background.
        c.addLegend(50, 28, False, "Arial Bold", 10).setBackground(Chart.Transparent)

        ' Set the x axis labels
        c.xAxis().setLabels(labels)

        ' Draw the ticks between label positions (instead of at label positions)
        c.xAxis().setTickOffset(0.5)

        ' Set axis label style to 8pt Arial Bold
        c.xAxis().setLabelStyle("Arial Bold", 8)
        c.yAxis().setLabelStyle("Arial Bold", 8)

        ' Set axis line width to 2 pixels
        c.xAxis().setWidth(2)
        c.yAxis().setWidth(2)

        ' Add axis title
        c.yAxis().setTitle("Throughput (MBytes Per Hour)")

        ' Add a multi-bar layer with 3 data sets
        Dim layer As BarLayer = c.addBarLayer2(Chart.Side)
        layer.addDataSet(data0, &Hff0000, "Server #1")
        layer.addDataSet(data1, &H00ff00, "Server #2")
        layer.addDataSet(data2, &Hff8800, "Server #3")

        ' Set bar border to transparent. Use glass lighting effect with light direction from left.
        layer.setBorderColor(Chart.Transparent, Chart.glassEffect(Chart.NormalGlare, Chart.Left))

        ' Configure the bars within a group to touch each others (no gap)
        layer.setBarGap(0.2, Chart.TouchBar)

        ' Assign the chart to the WinChartViewer
        winChartViewer1.Chart = c

    End Sub

    '
    ' Draw track cursor when mouse is moving over plotarea
    '
    Private Sub winChartViewer1_MouseMovePlotArea(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) Handles winChartViewer1.MouseMovePlotArea

        Dim viewer As WinChartViewer = sender
        trackBoxLegend(viewer.Chart, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY)
        viewer.updateDisplay()

        ' Hide the track cursor when the mouse leaves the plot area
        viewer.removeDynamicLayer("MouseLeavePlotArea")

    End Sub

    '
    ' Draw the track box with legend
    '
    Private Sub trackBoxLegend(c As XYChart, mouseX As Integer, mouseY As Integer)

        ' Clear the current dynamic layer and get the DrawArea object to draw on it.
        Dim d As DrawArea = c.initDynamicLayer()

        ' The plot area object
        Dim plotArea As PlotArea = c.getPlotArea()

        ' Get the data x-value that is nearest to the mouse
        Dim xValue As Double = c.getNearestXValue(mouseX)

        ' Compute the position of the box. This example assumes a label based x-axis, in which the labeling
        ' spacing is one x-axis unit. So the left and right sides of the box is 0.5 unit from the central
        ' x-value.
        Dim boxLeft As Integer = c.getXCoor(xValue - 0.5)
        Dim boxRight As Integer = c.getXCoor(xValue + 0.5)
        Dim boxTop As Integer = plotArea.getTopY()
        Dim boxBottom As Integer = plotArea.getBottomY()

        ' Draw the track box
        d.rect(boxLeft, boxTop, boxRight, boxBottom, &H000000, Chart.Transparent)

        ' Container to hold the legend entries
        Dim legendEntries As ArrayList = New ArrayList()

        ' Iterate through all layers to build the legend array
        For i As Integer = 0 To c.getLayerCount() - 1
            Dim layer As Layer = c.getLayerByZ(i)

            ' The data array index of the x-value
            Dim xIndex As Integer = layer.getXIndexOf(xValue)

            ' Iterate through all the data sets in the layer
            For j As Integer = 0 To layer.getDataSetCount() - 1
                Dim dataSet As ChartDirector.DataSet = layer.getDataSetByZ(j)

                ' Build the legend entry, consist of the legend icon, the name and the data value.
                Dim dataValue As Double = dataSet.getValue(xIndex)
                If (dataValue <> Chart.NoValue) And (dataSet.getDataColor() <> Chart.Transparent) Then
                    legendEntries.Add(dataSet.getLegendIcon() & " " & dataSet.getDataName() & ": " & _
                        c.formatValue(dataValue, "{value|P4}"))
                End If
            Next
        Next

        ' Create the legend by joining the legend entries
        If legendEntries.Count > 0 Then
            legendEntries.Reverse()
            Dim legend As String = "<*block,bgColor=FFFFCC,edgeColor=000000,margin=5*><*font,underline=1*>" _
                 & c.xAxis().getFormattedLabel(xValue) & "<*/font*><*br*>" & Join(CType( _
                legendEntries.ToArray(GetType(String)), String()), "<*br*>") & "<*/*>"

            ' Display the legend at the bottom-right side of the mouse cursor, and make sure the legend will
            ' not go outside the chart image.
            Dim t As TTFText = d.text(legend, "Arial Bold", 8)
            t.draw(Math.Min(mouseX + 12, c.getWidth() - t.getWidth()), Math.Min(mouseY + 18, c.getHeight() - _
                t.getHeight()), &H000000, Chart.TopLeft)
        End If

    End Sub

End Class

[WPF - XAML] NetWPFCharts\TrackBoxWindow.xaml
<Window x:Class="CSharpWPFDemo.TrackBoxWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CSharpWPFDemo"
        mc:Ignorable="d"
        xmlns:ChartDirector="clr-namespace:ChartDirector;assembly=netchartdir" UseLayoutRounding="True"
        Title="Track Box with Floating Legend" SizeToContent="WidthAndHeight" ResizeMode="NoResize" Loaded="Window_Loaded">
    <Grid>
        <ChartDirector:WPFChartViewer x:Name="WPFChartViewer1" Margin="5" MouseMovePlotArea="WPFChartViewer1_MouseMovePlotArea" />
    </Grid>
</Window>

[WPF - C#] NetWPFCharts\TrackBoxWindow.xaml.cs
using System;
using System.Collections;
using System.Windows;
using System.Windows.Input;
using ChartDirector;

namespace CSharpWPFDemo
{
    public partial class TrackBoxWindow : Window
    {
        public TrackBoxWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // The data for the bar chart
            double[] data0 = {100, 125, 245, 147, 67};
            double[] data1 = {85, 156, 179, 211, 123};
            double[] data2 = {97, 87, 56, 267, 157};
            string[] labels = {"Mon", "Tue", "Wed", "Thur", "Fri"};

            // Create a XYChart object of size 540 x 375 pixels
            XYChart c = new XYChart(540, 375);

            // Add a title to the chart using 18pt Times Bold Italic font
            c.addTitle("Average Weekly Network Load", "Times New Roman Bold Italic", 18);

            // Set the plotarea at (50, 55) and of 440 x 280 pixels in size. Use a vertical gradient color
            // from light blue (f9f9ff) to blue (6666ff) as background. Set border and grid lines to white
            // (ffffff).
            c.setPlotArea(50, 55, 440, 280, c.linearGradientColor(0, 55, 0, 335, 0xf9f9ff, 0x6666ff), -1,
                0xffffff, 0xffffff);

            // Add a legend box at (50, 28) using horizontal layout. Use 10pt Arial Bold as font, with
            // transparent background.
            c.addLegend(50, 28, false, "Arial Bold", 10).setBackground(Chart.Transparent);

            // Set the x axis labels
            c.xAxis().setLabels(labels);

            // Draw the ticks between label positions (instead of at label positions)
            c.xAxis().setTickOffset(0.5);

            // Set axis label style to 8pt Arial Bold
            c.xAxis().setLabelStyle("Arial Bold", 8);
            c.yAxis().setLabelStyle("Arial Bold", 8);

            // Set axis line width to 2 pixels
            c.xAxis().setWidth(2);
            c.yAxis().setWidth(2);

            // Add axis title
            c.yAxis().setTitle("Throughput (MBytes Per Hour)");

            // Add a multi-bar layer with 3 data sets
            BarLayer layer = c.addBarLayer2(Chart.Side);
            layer.addDataSet(data0, 0xff0000, "Server #1");
            layer.addDataSet(data1, 0x00ff00, "Server #2");
            layer.addDataSet(data2, 0xff8800, "Server #3");

            // Set bar border to transparent. Use glass lighting effect with light direction from left.
            layer.setBorderColor(Chart.Transparent, Chart.glassEffect(Chart.NormalGlare, Chart.Left));

            // Configure the bars within a group to touch each others (no gap)
            layer.setBarGap(0.2, Chart.TouchBar);

            // Assign the chart to the WPFChartViewer
            WPFChartViewer1.Chart = c;
        }

        //
        // Draw track cursor when mouse is moving over plotarea
        //
        private void WPFChartViewer1_MouseMovePlotArea(object sender, MouseEventArgs e)
        {
            WPFChartViewer viewer = (WPFChartViewer)sender;
            trackBoxLegend((XYChart)viewer.Chart, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY);
            viewer.updateDisplay();

            // Hide the track cursor when the mouse leaves the plot area
            viewer.removeDynamicLayer("MouseLeavePlotArea");
        }

        //
        // Draw the track box with legend
        //
        private void trackBoxLegend(XYChart c, int mouseX, int mouseY)
        {
            // Clear the current dynamic layer and get the DrawArea object to draw on it.
            DrawArea d = c.initDynamicLayer();

            // The plot area object
            PlotArea plotArea = c.getPlotArea();

            // Get the data x-value that is nearest to the mouse
            double xValue = c.getNearestXValue(mouseX);

            // Compute the position of the box. This example assumes a label based x-axis, in which the
            // labeling spacing is one x-axis unit. So the left and right sides of the box is 0.5 unit from
            // the central x-value.
            int boxLeft = c.getXCoor(xValue - 0.5);
            int boxRight = c.getXCoor(xValue + 0.5);
            int boxTop = plotArea.getTopY();
            int boxBottom = plotArea.getBottomY();

            // Draw the track box
            d.rect(boxLeft, boxTop, boxRight, boxBottom, 0x000000, Chart.Transparent);

            // Container to hold the legend entries
            ArrayList legendEntries = new ArrayList();

            // Iterate through all layers to build the legend array
            for(int i = 0; i < c.getLayerCount(); ++i) {
                Layer layer = c.getLayerByZ(i);

                // The data array index of the x-value
                int xIndex = layer.getXIndexOf(xValue);

                // Iterate through all the data sets in the layer
                for(int j = 0; j < layer.getDataSetCount(); ++j) {
                    ChartDirector.DataSet dataSet = layer.getDataSetByZ(j);

                    // Build the legend entry, consist of the legend icon, the name and the data value.
                    double dataValue = dataSet.getValue(xIndex);
                    if ((dataValue != Chart.NoValue) && (dataSet.getDataColor() != Chart.Transparent)) {
                        legendEntries.Add(dataSet.getLegendIcon() + " " + dataSet.getDataName() + ": " +
                            c.formatValue(dataValue, "{value|P4}"));
                    }
                }
            }

            // Create the legend by joining the legend entries
            if (legendEntries.Count > 0) {
                legendEntries.Reverse();
                string legend = "<*block,bgColor=FFFFCC,edgeColor=000000,margin=5*><*font,underline=1*>" +
                    c.xAxis().getFormattedLabel(xValue) + "<*/font*><*br*>" + String.Join("<*br*>", (string[])
                    legendEntries.ToArray(typeof(string))) + "<*/*>";

                // Display the legend at the bottom-right side of the mouse cursor, and make sure the legend
                // will not go outside the chart image.
                TTFText t = d.text(legend, "Arial Bold", 8);
                t.draw(Math.Min(mouseX + 12, c.getWidth() - t.getWidth()), Math.Min(mouseY + 18, c.getHeight()
                     - t.getHeight()), 0x000000, Chart.TopLeft);
            }
        }
    }
}