ChartDirector 6.2 (.NET Edition)

XY Zooming and Scrolling (Windows)




NOTE: This section describes XY Zooming and Scrolling for Windows applications only. For web applications, please refer to XY Zooming and Scrolling (Web).

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

This example demonstrates zooming and scrolling in both horizontal and vertical directions. In addition to using mouse click and drag, this example demonstrates using a slider, the mouse wheel and a WinViewPortControl to control scrolling and zooming. This example also includes a crosshair track cursor with dynamic labels on the x-axis and y-axis showing the mouse cursor position, and an image map for data point tooltips. A save button is included to save the chart in PNG, JPG, GIF, BMP, SVG or PDF.

The main source code listing of this sample program is included at the end of this section. The code consists of the following main parts:

Source Code Listing

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

namespace CSharpChartExplorer
{
    public partial class FrmXYZoomScroll : Form
    {
        // XY data points for the chart
        private double[] dataX0;
        private double[] dataY0;
        private double[] dataX1;
        private double[] dataY1;
        private double[] dataX2;
        private double[] dataY2;

        public FrmXYZoomScroll()
        {
            InitializeComponent();
        }

        private void FrmXYZoomScroll_Load(object sender, EventArgs e)
        {
            // Load the data
            loadData();

            // Trigger the ViewPortChanged event to draw the chart
            winChartViewer1.updateViewPort(true, true);

            // Draw the full thumbnail chart for the ViewPortControl
            drawFullChart(viewPortControl1, winChartViewer1);
        }

        //
        // Load the data
        //
        private void loadData()
        {
            //
            // For simplicity, in this demo, we just use hard coded data.
            //
            dataX0 = new double[] { 10, 15, 6, -12, 14, -8, 13, -3, 16, 12, 10.5, -7, 3, -10, -5, 2, 5 };
            dataY0 = new double[] {130, 150, 80, 110, -110, -105, -130, -15, -170, 125,  125, 60, 25, 150,
                150, 15, 120};
            dataX1 = new double[] { 6, 7, -4, 3.5, 7, 8, -9, -10, -12, 11, 8, -3, -2, 8, 4, -15, 15 };
            dataY1 = new double[] {65, -40, -40, 45, -70, -80, 80, 10, -100, 105, 60, 50, 20, 170, -25, 50,
                75};
            dataX2 = new double[] { -10, -12, 11, 8, 6, 12, -4, 3.5, 7, 8, -9, 3, -13, 16, -7.5, -10, -15 };
            dataY2 = new double[] {65, -80, -40, 45, -70, -80, 80, 90, -100, 105, 60, -75, -150, -40, 120,
                -50, -30};
        }

        //
        // The ViewPortChanged event handler. This event occurs if the user scrolls or zooms in
        // or out the chart by dragging or clicking on the chart. It can also be triggered by
        // calling WinChartViewer.updateViewPort.
        //
        private void winChartViewer1_ViewPortChanged(object sender, WinViewPortEventArgs e)
        {
            // In addition to updating the chart, we may also need to update other controls that
            // changes based on the view port.
            updateControls(winChartViewer1);

            // Update the chart if necessary
            if (e.NeedUpdateChart)
                drawChart(winChartViewer1);

            // Update the image map if necessary
            if (e.NeedUpdateImageMap)
                updateImageMap(winChartViewer1);
        }

        //
        // Update controls when the view port changed
        //
        private void updateControls(WinChartViewer viewer)
        {
            // Synchronize the zoom bar value with the view port width/height
            zoomBar.Value = (int)Math.Round(Math.Min(viewer.ViewPortWidth, viewer.ViewPortHeight) * 
                zoomBar.Maximum); 
        }

        //
        // Draw the chart and display it in the given viewer
        //
        private void drawChart(WinChartViewer viewer)
        {
            // Create an XYChart object 500 x 480 pixels in size, with the same background color
            // as the container
            XYChart c = new XYChart(500, 480, Chart.CColor(BackColor));

            // Set the plotarea at (50, 40) and of size 400 x 400 pixels. Use light grey (c0c0c0)
            // horizontal and vertical grid lines. Set 4 quadrant coloring, where the colors of 
            // the quadrants alternate between lighter and deeper grey (dddddd/eeeeee). 
            c.setPlotArea(50, 40, 400, 400, -1, -1, -1, 0xc0c0c0, 0xc0c0c0
                ).set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000);

            // Enable clipping mode to clip the part of the data that is outside the plot area.
            c.setClipping();

            // Set 4 quadrant mode, with both x and y axes symetrical around the origin
            c.setAxisAtOrigin(Chart.XYAxisAtOrigin, Chart.XAxisSymmetric + Chart.YAxisSymmetric);

            // Add a legend box at (450, 40) (top right corner of the chart) with vertical layout
            // and 8 pts Arial Bold font. Set the background color to semi-transparent grey.
            LegendBox b = c.addLegend(450, 40, true, "Arial Bold", 8);
            b.setAlignment(Chart.TopRight);
            b.setBackground(0x40dddddd);

            // Add a titles to axes
            c.xAxis().setTitle("Alpha Index");
            c.yAxis().setTitle("Beta Index");

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

            // The default ChartDirector settings has a denser y-axis grid spacing and less-dense
            // x-axis grid spacing. In this demo, we want the tick spacing to be symmetrical.
            // We use around 50 pixels between major ticks and 25 pixels between minor ticks.
            c.xAxis().setTickDensity(50, 25);
            c.yAxis().setTickDensity(50, 25);

            //
            // In this example, we represent the data by scatter points. If you want to represent
            // the data by somethings else (lines, bars, areas, floating boxes, etc), just modify
            // the code below to use the layer type of your choice. 
            //

            // Add scatter layer, using 11 pixels red (ff33333) X shape symbols
            c.addScatterLayer(dataX0, dataY0, "Group A", Chart.Cross2Shape(), 11, 0xff3333);

            // Add scatter layer, using 11 pixels green (33ff33) circle symbols
            c.addScatterLayer(dataX1, dataY1, "Group B", Chart.CircleShape, 11, 0x33ff33);

            // Add scatter layer, using 11 pixels blue (3333ff) triangle symbols
            c.addScatterLayer(dataX2, dataY2, "Group C", Chart.TriangleSymbol, 11, 0x3333ff);
           
            //
            // In this example, we have not explicitly configured the full x and y range. In this case, the
            // first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis and
            // assume the resulting range is the full range. In subsequent calls, ChartDirector will set the
            // axis range based on the view port and the full range.
            //
            viewer.syncLinearAxisWithViewPort("x", c.xAxis());
            viewer.syncLinearAxisWithViewPort("y", c.yAxis());

            // We need to update the track line too. If the mouse is moving on the chart (eg. if 
            // the user drags the mouse on the chart to scroll it), the track line will be updated
            // in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
            if ((!viewer.IsInMouseMoveEvent) && viewer.IsMouseOnPlotArea)
                crossHair(c, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY);

            // Set the chart image to the WinChartViewer
            viewer.Chart = c;
        }

        //
        // Draw the full thumbnail chart and display it in the given ViewPortControl
        //
        private void drawFullChart(WinViewPortControl vpc, WinChartViewer viewer)
        {
            // Create an XYChart object of the same size as the Viewport Control
            XYChart c = new XYChart(viewPortControl1.ClientSize.Width, viewPortControl1.ClientSize.Height);

            // Set the plotarea to cover the entire chart. Disable grid lines by setting their colors
            // to transparent. Set 4 quadrant coloring, where the colors of the quadrants alternate 
            // between lighter and deeper grey (dddddd/eeeeee). 
            c.setPlotArea(0, 0, c.getWidth() - 1, c.getHeight() - 1, -1, -1, 0xff0000, Chart.Transparent, 
                Chart.Transparent).set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000);

            // Set 4 quadrant mode, with both x and y axes symetrical around the origin
            c.setAxisAtOrigin(Chart.XYAxisAtOrigin, Chart.XAxisSymmetric + Chart.YAxisSymmetric);

            // The x and y axis scales reflect the full range of the view port
            c.xAxis().setLinearScale(viewer.getValueAtViewPort("x", 0), viewer.getValueAtViewPort("x", 1),
                Chart.NoValue);
            c.yAxis().setLinearScale(viewer.getValueAtViewPort("y", 0), viewer.getValueAtViewPort("y", 1),
                Chart.NoValue);

            // Add scatter layer, using 3 pixels red (ff33333) X shape symbols
            c.addScatterLayer(dataX0, dataY0, "Group A", Chart.Cross2Shape(), 3, 0xff3333, 0xff3333);

            // Add scatter layer, using 3 pixels green (33ff33) circle symbols
            c.addScatterLayer(dataX1, dataY1, "Group B", Chart.CircleShape, 3, 0x33ff33, 0x33ff33);

            // Add scatter layer, using 3 pixels blue (3333ff) triangle symbols
            c.addScatterLayer(dataX2, dataY2, "Group C", Chart.TriangleSymbol, 3, 0x3333ff, 0x3333ff);

            // Set the chart image to the WinChartViewer
            vpc.Chart = c;
        }

        //
        // Update the image map
        //
        private void updateImageMap(WinChartViewer viewer)
        {
            // Include tool tip for the chart
            if (viewer.ImageMap == null)
            {
                viewer.ImageMap = viewer.Chart.getHTMLImageMap("clickable", "",
                    "title='[{dataSetName}] Alpha = {x}, Beta = {value}'");
            }
        }

        //
        // ClickHotSpot event handler. In this demo, we just display the hot spot parameters in a pop up 
        // dialog.
        //
        private void winChartViewer1_ClickHotSpot(object sender, WinHotSpotEventArgs e)
        {
            // We show the pop up dialog only when the mouse action is not in zoom-in or zoom-out mode.
            if ((winChartViewer1.MouseUsage != WinChartMouseUsage.ZoomIn)
                && (winChartViewer1.MouseUsage != WinChartMouseUsage.ZoomOut))
                new ParamViewer().Display(sender, e);
        }

        //
        // Pointer (Drag to Scroll) button event handler
        //
        private void pointerPB_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
                winChartViewer1.MouseUsage = WinChartMouseUsage.ScrollOnDrag;
        }

        //
        // Zoom In button event handler
        //
        private void zoomInPB_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
                winChartViewer1.MouseUsage = WinChartMouseUsage.ZoomIn;
        }

        //
        // Zoom Out button event handler
        //
        private void zoomOutPB_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
                winChartViewer1.MouseUsage = WinChartMouseUsage.ZoomOut;
        }

        //
        // Save button event handler
        //
        private void savePB_Click(object sender, EventArgs e)
        {
            // The standard Save File dialog
            SaveFileDialog fileDlg = new SaveFileDialog();
            fileDlg.Filter = "PNG (*.png)|*.png|JPG (*.jpg)|*.jpg|GIF (*.gif)|*.gif|BMP (*.bmp)|*.bmp|" +
                "SVG (*.svg)|*.svg|PDF (*.pdf)|*.pdf";
            fileDlg.FileName = "chartdirector_demo";
            if (fileDlg.ShowDialog() != DialogResult.OK)
                return;

            // Save the chart
            if (null != winChartViewer1.Chart)
                winChartViewer1.Chart.makeChart(fileDlg.FileName);
        }

        //
        // ValueChanged event handler for zoomBar. Zoom in around the center point and try to 
        // maintain the aspect ratio
        //
        private void zoomBar_ValueChanged(object sender, EventArgs e)
        {
            if (!winChartViewer1.IsInViewPortChangedEvent)
            {
                //Remember the center point
                double centerX = winChartViewer1.ViewPortLeft + winChartViewer1.ViewPortWidth / 2;
                double centerY = winChartViewer1.ViewPortTop + winChartViewer1.ViewPortHeight / 2;

                //Aspect ratio and zoom factor
                double aspectRatio = winChartViewer1.ViewPortWidth / winChartViewer1.ViewPortHeight;
                double zoomTo = ((double)zoomBar.Value) / zoomBar.Maximum;

                //Zoom while respecting the aspect ratio
                winChartViewer1.ViewPortWidth = zoomTo * Math.Max(1, aspectRatio);
                winChartViewer1.ViewPortHeight = zoomTo * Math.Max(1, 1 / aspectRatio);

                //Adjust ViewPortLeft and ViewPortTop to keep center point unchanged
                winChartViewer1.ViewPortLeft = centerX - winChartViewer1.ViewPortWidth / 2;
                winChartViewer1.ViewPortTop = centerY - winChartViewer1.ViewPortHeight / 2;

                //update the chart, but no need to update the image map yet, as the chart is still
                //zooming and is unstable
                winChartViewer1.updateViewPort(true, false);
            }
        }        

        //
        // Draw track cursor when mouse is moving over plotarea, and update image map if necessary
        //
        private void winChartViewer1_MouseMovePlotArea(object sender, MouseEventArgs e)
        {
            WinChartViewer viewer = (WinChartViewer)sender;

            // Draw crosshair track cursor
            crossHair((XYChart)viewer.Chart, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY);
            viewer.updateDisplay();

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

            // Update image map if necessary. If the mouse is still dragging, the chart is still 
            // updating and not confirmed, so there is no need to set up the image map.
            if (!viewer.IsMouseDragging)
                updateImageMap(viewer);            
        }

        //
        // Draw cross hair cursor with axis labels
        //
        private void crossHair(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();

            // Draw a vertical line and a horizontal line as the cross hair
            d.vline(plotArea.getTopY(), plotArea.getBottomY(), mouseX, d.dashLineColor(0x000000, 0x0101));
            d.hline(plotArea.getLeftX(), plotArea.getRightX(), mouseY, d.dashLineColor(0x000000, 0x0101));

            // Draw y-axis label
            string label = "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" + c.formatValue(c.getYValue(
                mouseY, c.yAxis()), "{value|P4}") + "<*/*>";
            TTFText t = d.text(label, "Arial Bold", 8);
            t.draw(plotArea.getLeftX() - 5, mouseY, 0x000000, Chart.Right);

            // Draw x-axis label
            label = "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" + c.formatValue(c.getXValue(mouseX),
                "{value|P4}") + "<*/*>";
            t = d.text(label, "Arial Bold", 8);
            t.draw(mouseX, plotArea.getBottomY() + 5, 0x000000, Chart.Top);
        }
    }
}

[Windows Forms - VB Version] NetWinCharts\VBNetWinCharts\frmxyzoomscroll.vb
Imports ChartDirector

Public Class FrmXYZoomScroll

    ' Data arrays
    Dim dataX0 As Double()
    Dim dataY0 As Double()
    Dim dataX1 As Double()
    Dim dataY1 As Double()
    Dim dataX2 As Double()
    Dim dataY2 As Double()

    Private Sub FrmXYZoomScroll_Load(ByVal sender As Object, ByVal e As EventArgs) _
        Handles MyBase.Load

        ' Load the data
        loadData()

        ' Trigger the ViewPortChanged event to draw the chart
        winChartViewer1.updateViewPort(True, True)

        ' Draw the full thumbnail chart for the ViewPortControl
        drawFullChart(viewPortControl1, winChartViewer1)

    End Sub

    '
    ' Load the data
    '
    Private Sub loadData()

        '
        ' For simplicity, in this demo, we just use hard coded data.
        '
        dataX0 = New Double() {10, 15, 6, -12, 14, -8, 13, -3, 16, 12, 10.5, -7, 3, -10, -5, 2, 5}
        dataY0 = New Double() {130, 150, 80, 110, -110, -105, -130, -15, -170, 125, 125, 60, 25, 150, _
            150, 15, 120}
        dataX1 = New Double() {6, 7, -4, 3.5, 7, 8, -9, -10, -12, 11, 8, -3, -2, 8, 4, -15, 15}
        dataY1 = New Double() {65, -40, -40, 45, -70, -80, 80, 10, -100, 105, 60, 50, 20, 170, -25, 50, 75}
        dataX2 = New Double() {-10, -12, 11, 8, 6, 12, -4, 3.5, 7, 8, -9, 3, -13, 16, -7.5, -10, -15}
        dataY2 = New Double() {65, -80, -40, 45, -70, -80, 80, 90, -100, 105, 60, -75, -150, -40, 120, _
            -50, -30}

    End Sub

    '
    ' The ViewPortChanged event handler. This event occurs if the user scrolls or zooms in
    ' or out the chart by dragging or clicking on the chart. It can also be triggered by
    ' calling WinChartViewer.updateViewPort.
    '
    Private Sub winChartViewer1_ViewPortChanged(ByVal sender As Object, _
        ByVal e As WinViewPortEventArgs) Handles winChartViewer1.ViewPortChanged

        ' In addition to updating the chart, we may also need to update other controls that
        ' changes based on the view port.
        updateControls(winChartViewer1)

        ' Update the chart if necessary
        If e.NeedUpdateChart Then
            drawChart(winChartViewer1)
        End If

        ' Update the image map if necessary
        If e.NeedUpdateImageMap Then
            updateImageMap(winChartViewer1)
        End If

    End Sub

    '
    ' Update controls when the view port changed
    '
    Private Sub updateControls(ByVal viewer As WinChartViewer)

        ' Synchronize the zoom bar value with the view port width/height
        zoomBar.Value = Math.Round(Math.Min(viewer.ViewPortWidth, viewer.ViewPortHeight) * zoomBar.Maximum)

    End Sub

    '
    ' Draw the chart.
    '
    Private Sub drawChart(ByVal viewer As WinChartViewer)

        ' Create an XYChart object 500 x 480 pixels in size, with the same background color
        ' as the container
        Dim c As XYChart = New XYChart(500, 480, Chart.CColor(BackColor))

        ' Set the plotarea at (50, 40) and of size 400 x 400 pixels. Use light grey (c0c0c0)
        ' horizontal and vertical grid lines. Set 4 quadrant coloring, where the colors of 
        ' the quadrants alternate between lighter and deeper grey (dddddd/eeeeee). 
        c.setPlotArea(50, 40, 400, 400, -1, -1, -1, &HC0C0C0, &HC0C0C0 _
            ).set4QBgColor(&HDDDDDD, &HEEEEEE, &HDDDDDD, &HEEEEEE, &H0)

        ' Enable clipping mode to clip the part of the data that is outside the plot area.
        c.setClipping()

        ' Set 4 quadrant mode, with both x and y axes symetrical around the origin
        c.setAxisAtOrigin(Chart.XYAxisAtOrigin, Chart.XAxisSymmetric + Chart.YAxisSymmetric)

        ' Add a legend box at (450, 40) (top right corner of the chart) with vertical layout
        ' and 8 pts Arial Bold font. Set the background color to semi-transparent grey.
        Dim b As LegendBox = c.addLegend(450, 40, True, "Arial Bold", 8)
        b.setAlignment(Chart.TopRight)
        b.setBackground(&H40DDDDDD)

        ' Add a titles to axes
        c.xAxis().setTitle("Alpha Index")
        c.yAxis().setTitle("Beta Index")

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

        ' The default ChartDirector settings has a denser y-axis grid spacing and less-dense
        ' x-axis grid spacing. In this demo, we want the tick spacing to be symmetrical.
        ' We use around 50 pixels between major ticks and 25 pixels between minor ticks.
        c.xAxis().setTickDensity(50, 25)
        c.yAxis().setTickDensity(50, 25)

        '
        ' In this example, we represent the data by scatter points. If you want to represent
        ' the data by somethings else (lines, bars, areas, floating boxes, etc), just modify
        ' the code below to use the layer type of your choice. 
        '

        ' Add scatter layer, using 11 pixels red (ff33333) X shape symbols
        c.addScatterLayer(dataX0, dataY0, "Group A", Chart.Cross2Shape(), 11, &HFF3333)

        ' Add scatter layer, using 11 pixels green (33ff33) circle symbols
        c.addScatterLayer(dataX1, dataY1, "Group B", Chart.CircleShape, 11, &H33FF33)

        ' Add scatter layer, using 11 pixels blue (3333ff) triangle symbols
        c.addScatterLayer(dataX2, dataY2, "Group C", Chart.TriangleSymbol, 11, &H3333FF)

        '
        ' In this example, we have not explicitly configured the full x and y range. In this case, the
        ' first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis and
        ' assume the resulting range is the full range. In subsequent calls, ChartDirector will set the
        ' axis range based on the view port and the full range.
        '
        viewer.SyncLinearAxisWithViewPort("x", c.xAxis())
        viewer.SyncLinearAxisWithViewPort("y", c.yAxis())

        ' We need to update the track line too. If the mouse is moving on the chart (eg. if 
        ' the user drags the mouse on the chart to scroll it), the track line will be updated
        ' in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
        If (Not viewer.IsInMouseMoveEvent) And viewer.IsMouseOnPlotArea Then
            crossHair(c, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY)
        End If

        ' Set the chart image to the WinChartViewer
        viewer.Chart = c

    End Sub

    '
    ' Draw the full thumbnail chart and display it in the given ViewPortControl
    '
    Private Sub drawFullChart(ByVal vpc As WinViewPortControl, ByVal viewer As WinChartViewer)

        ' Create an XYChart object of the same size as the Viewport Control
        Dim c As XYChart = New XYChart(viewPortControl1.ClientSize.Width, viewPortControl1.ClientSize.Height)

        ' Set the plotarea to cover the entire chart. Disable grid lines by setting their colors
        ' to transparent. Set 4 quadrant coloring, where the colors of the quadrants alternate 
        ' between lighter and deeper grey (dddddd/eeeeee). 
        c.setPlotArea(0, 0, c.getWidth() - 1, c.getHeight() - 1, -1, -1, &HFF0000, Chart.Transparent, _
            Chart.Transparent).set4QBgColor(&HDDDDDD, &HEEEEEE, &HDDDDDD, &HEEEEEE, &H0)

        ' Set 4 quadrant mode, with both x and y axes symetrical around the origin
        c.setAxisAtOrigin(Chart.XYAxisAtOrigin, Chart.XAxisSymmetric + Chart.YAxisSymmetric)

        ' The x and y axis scales reflect the full range of the view port
        c.xAxis().setLinearScale(viewer.getValueAtViewPort("x", 0), viewer.getValueAtViewPort("x", 1), _
            Chart.NoValue)
        c.yAxis().setLinearScale(viewer.getValueAtViewPort("y", 0), viewer.getValueAtViewPort("y", 1), _
            Chart.NoValue)

        ' Add scatter layer, using 3 pixels red (ff33333) X shape symbols
        c.addScatterLayer(dataX0, dataY0, "Group A", Chart.Cross2Shape(), 3, &HFF3333, &HFF3333)

        ' Add scatter layer, using 3 pixels green (33ff33) circle symbols
        c.addScatterLayer(dataX1, dataY1, "Group B", Chart.CircleShape, 3, &H33FF33, &H33FF33)

        ' Add scatter layer, using 3 pixels blue (3333ff) triangle symbols
        c.addScatterLayer(dataX2, dataY2, "Group C", Chart.TriangleSymbol, 3, &H3333FF, &H3333FF)

        ' Set the chart image to the WinChartViewer
        vpc.Chart = c

    End Sub

    '
    ' Update the image map
    '
    Private Sub updateImageMap(ByVal viewer As WinChartViewer)

        ' Include tool tip for the chart
        If IsNothing(viewer.ImageMap) Then
            viewer.ImageMap = viewer.Chart.getHTMLImageMap("clickable", "", _
                "title='[{dataSetName}] Alpha = {x}, Beta = {value}'")
        End If

    End Sub

    '
    ' ClickHotSpot event handler. In this demo, we just display the hot spot parameters in a pop up 
    ' dialog.
    '
    Private Sub winChartViewer1_ClickHotSpot(ByVal sender As Object, ByVal e As WinHotSpotEventArgs) _
        Handles winChartViewer1.ClickHotSpot

        ' We show the pop up dialog only when the mouse action is not in zoom-in or zoom-out mode.
        If winChartViewer1.MouseUsage <> WinChartMouseUsage.ZoomIn And _
            winChartViewer1.MouseUsage <> WinChartMouseUsage.ZoomOut Then
            Dim f As New ParamViewer()
            f.Display(sender, e)
        End If

    End Sub

    '
    ' Pointer (Drag to Scroll) button event handler
    '
    Private Sub pointerPB_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) _
        Handles pointerPB.CheckedChanged

        If sender.Checked Then
            winChartViewer1.MouseUsage = WinChartMouseUsage.ScrollOnDrag
        End If

    End Sub

    '
    ' Zoom In button event handler
    '
    Private Sub zoomInPB_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) _
        Handles zoomInPB.CheckedChanged

        If sender.Checked Then
            winChartViewer1.MouseUsage = WinChartMouseUsage.ZoomIn
        End If

    End Sub

    '
    ' Zoom Out button event handler
    '
    Private Sub zoomOutPB_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) _
        Handles zoomOutPB.CheckedChanged

        If sender.Checked Then
            winChartViewer1.MouseUsage = WinChartMouseUsage.ZoomOut
        End If

    End Sub

    '
    ' Save button event handler
    '
    Private Sub savePB_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles savePB.Click

        ' The standard Save File dialog
        Dim fileDlg As SaveFileDialog = New SaveFileDialog()
        fileDlg.Filter = "PNG (*.png)|*.png|JPG (*.jpg)|*.jpg|GIF (*.gif)|*.gif|BMP (*.bmp)|*.bmp|" & _
            "SVG (*.svg)|*.svg|PDF (*.pdf)|*.pdf"
        fileDlg.FileName = "chartdirector_demo"
        If fileDlg.ShowDialog() <> DialogResult.OK Then
            Return
        End If
        ' Save the chart
        If Not IsNothing(winChartViewer1.Chart) Then
            winChartViewer1.Chart.makeChart(fileDlg.FileName)
        End If

    End Sub

    '
    ' ValueChanged event handler for zoomBar. Zoom in around the center point and try to 
    ' maintain the aspect ratio
    '
    Private Sub zoomBar_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles zoomBar.ValueChanged

        If Not winChartViewer1.IsInViewPortChangedEvent Then
            'Remember the center point
            Dim centerX As Double = winChartViewer1.ViewPortLeft + winChartViewer1.ViewPortWidth / 2
            Dim centerY As Double = winChartViewer1.ViewPortTop + winChartViewer1.ViewPortHeight / 2

            'Aspect ratio and zoom factor
            Dim aspectRatio As Double = winChartViewer1.ViewPortWidth / winChartViewer1.ViewPortHeight
            Dim zoomTo As Double = CDbl(zoomBar.Value) / zoomBar.Maximum

            'Zoom while respecting the aspect ratio
            winChartViewer1.ViewPortWidth = zoomTo * Math.Max(1, aspectRatio)
            winChartViewer1.ViewPortHeight = zoomTo * Math.Max(1, 1 / aspectRatio)

            'Adjust ViewPortLeft and ViewPortTop to keep center point unchanged
            winChartViewer1.ViewPortLeft = centerX - winChartViewer1.ViewPortWidth / 2
            winChartViewer1.ViewPortTop = centerY - winChartViewer1.ViewPortHeight / 2

            'update the chart, but no need to update the image map yet, as the chart is still 
            'zooming and is unstable
            winChartViewer1.updateViewPort(True, False)
        End If

    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
        crossHair(viewer.Chart, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY)
        viewer.updateDisplay()

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

        ' Update image map if necessary. If the mouse is still dragging, the chart is still 
        ' updating and not confirmed, so there is no need to set up the image map.
        If Not viewer.IsMouseDragging Then
            updateImageMap(viewer)
        End If

    End Sub

    '
    ' Draw cross hair cursor with axis labels
    '
    Private Sub crossHair(ByVal c As XYChart, ByVal mouseX As Integer, ByVal 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()

        ' Draw a vertical line and a horizontal line as the cross hair
        d.vline(plotArea.getTopY(), plotArea.getBottomY(), mouseX, d.dashLineColor(&H0, &H101))
        d.hline(plotArea.getLeftX(), plotArea.getRightX(), mouseY, d.dashLineColor(&H0, &H101))

        ' Draw y-axis label
        Dim label As String = "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" & c.formatValue( _
            c.getYValue(mouseY, c.yAxis()), "{value|P4}") & "<*/*>"
        Dim t As TTFText = d.text(label, "Arial Bold", 8)
        t.draw(plotArea.getLeftX() - 5, mouseY, &H0, Chart.Right)

        ' Draw x-axis label
        label = "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" & c.formatValue(c.getXValue(mouseX), _
            "{value|P4}") & "<*/*>"
        t = d.text(label, "Arial Bold", 8)
        t.draw(mouseX, plotArea.getBottomY() + 5, &H0, Chart.Top)

    End Sub

End Class

[WPF - XAML] NetWPFCharts\XYZoomScrollWindow.xaml
´╗┐<Window x:Class="CSharpWPFDemo.XYZoomScrollWindow"
        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="XY Zooming and Scrolling" Loaded="Window_Loaded" Background="#FFCCCCFF" SizeToContent="WidthAndHeight" ResizeMode="NoResize"
        >
    <DockPanel>
        <Label Content="Advanced Software Engineering" Height="25" DockPanel.Dock="Top" FontFamily="Arial" FontStyle="Italic" FontWeight="Bold" 
               FontSize="13" Background="#FF02098D" Foreground="#FFF4FF04" HorizontalContentAlignment="Right"/>
        <StackPanel DockPanel.Dock="Left" Width="120" Background="#FFF0F0F0">
            <RadioButton x:Name="pointerPB" Style="{StaticResource {x:Type ToggleButton}}" HorizontalContentAlignment="Left" Checked="pointerPB_Checked" >
                <StackPanel Orientation="Horizontal" Margin="5">
                    <Image Source="pointer.gif" Height="16" />
                    <TextBlock Text="Pointer" Margin="6,0,0,0" />
                </StackPanel>
            </RadioButton>
            <RadioButton x:Name="zoomInPB" Style="{StaticResource {x:Type ToggleButton}}" HorizontalContentAlignment="Left" Checked="zoomInPB_Checked" >
                <StackPanel Orientation="Horizontal" Margin="5" >
                    <Image Source="zoomInIcon.gif" Height="16" />
                    <TextBlock Text="Zoom In" Margin="6,0,0,0" />
                </StackPanel>
            </RadioButton>
            <RadioButton x:Name="zoomOutPB" Style="{StaticResource {x:Type ToggleButton}}" HorizontalContentAlignment="Left" Checked="zoomOutPB_Checked">
                <StackPanel Orientation="Horizontal" Margin="5" >
                    <Image Source="zoomOutIcon.gif" Height="16" />
                    <TextBlock Text="Zoom Out" Margin="6,0,0,0" />
                </StackPanel>
            </RadioButton>
            <Button x:Name="savePB" Margin="0,32,0,0" HorizontalContentAlignment="Left" Click="savePB_Click">
                <StackPanel Orientation="Horizontal" Margin="5" >
                    <Image Source="saveIcon.gif" Width="14" Height="14" />
                    <TextBlock Text="Save" Margin="6,0,0,0" />
                </StackPanel>
            </Button>
            <TextBlock Text="Zoom Level" Margin="0,46,0,0" HorizontalAlignment="Center" FontWeight="Bold" />
            <Slider x:Name="zoomBar" Margin="5" ValueChanged="zoomBar_ValueChanged" TickPlacement="Both" Foreground="#FF555555"/>
            <ChartDirector:WPFViewPortControl x:Name="ViewPortControl1" Width="110" Height="110" Margin="0,68,0,0" />
        </StackPanel>
        <ChartDirector:WPFChartViewer x:Name="WPFChartViewer1" Width="500" Height="480" MouseMovePlotArea="WPFChartViewer1_MouseMovePlotArea" 
            ClickHotSpot="WPFChartViewer1_ClickHotSpot" ViewPortChanged="WPFChartViewer1_ViewPortChanged" 
            ScrollDirection="HorizontalVertical" ZoomDirection="HorizontalVertical" MouseWheelZoomRatio="1.1" />
    </DockPanel>
</Window>

[WPF - C#] NetWPFCharts\XYZoomScrollWindow.xaml.cs
´╗┐using System;
using System.Windows;
using System.Windows.Input;
using Microsoft.Win32;
using ChartDirector;

namespace CSharpWPFDemo
{
    /// <summary>
    /// Interaction logic for XYZoomScroll.xaml
    /// </summary>
    public partial class XYZoomScrollWindow : Window
    {
        // XY data points for the chart
        private double[] dataX0;
        private double[] dataY0;
        private double[] dataX1;
        private double[] dataY1;
        private double[] dataX2;
        private double[] dataY2;

        public XYZoomScrollWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Connect the WPFViewPortControl to the WPfChartViewer
            ViewPortControl1.Viewer = WPFChartViewer1;

            // Load the data
            loadData();

            // Trigger the ViewPortChanged event to draw the chart
            WPFChartViewer1.updateViewPort(true, true);

            // Draw the full thumbnail chart for the ViewPortControl
            drawFullChart(ViewPortControl1, WPFChartViewer1);
        }

        //
        // Load the data
        //
        private void loadData()
        {
            //
            // For simplicity, in this demo, we just use hard coded data.
            //
            dataX0 = new double[] { 10, 15, 6, -12, 14, -8, 13, -3, 16, 12, 10.5, -7, 3, -10, -5, 2, 5 };
            dataY0 = new double[] {130, 150, 80, 110, -110, -105, -130, -15, -170, 125,  125, 60, 25, 150,
                150, 15, 120};
            dataX1 = new double[] { 6, 7, -4, 3.5, 7, 8, -9, -10, -12, 11, 8, -3, -2, 8, 4, -15, 15 };
            dataY1 = new double[] {65, -40, -40, 45, -70, -80, 80, 10, -100, 105, 60, 50, 20, 170, -25, 50,
                75};
            dataX2 = new double[] { -10, -12, 11, 8, 6, 12, -4, 3.5, 7, 8, -9, 3, -13, 16, -7.5, -10, -15 };
            dataY2 = new double[] {65, -80, -40, 45, -70, -80, 80, 90, -100, 105, 60, -75, -150, -40, 120,
                -50, -30};
        }

        //
        // The ViewPortChanged event handler. This event occurs if the user scrolls or zooms in
        // or out the chart by dragging or clicking on the chart. It can also be triggered by
        // calling WinChartViewer.updateViewPort.
        //
        private void WPFChartViewer1_ViewPortChanged(object sender, WPFViewPortEventArgs e)
        {
            var viewer = sender as WPFChartViewer;

            // In addition to updating the chart, we may also need to update other controls that
            // changes based on the view port.
            updateControls(viewer);

            // Update the chart if necessary
            if (e.NeedUpdateChart)
                drawChart(viewer);

            // Update the image map if necessary
            if (e.NeedUpdateImageMap)
                updateImageMap(viewer);
        }

        //
        // Update controls when the view port changed
        //
        private void updateControls(WPFChartViewer viewer)
        {
            // Synchronize the zoom bar value with the view port width/height
            zoomBar.Value = Math.Min(viewer.ViewPortWidth, viewer.ViewPortHeight) * zoomBar.Maximum;
        }

        //
        // Draw the chart and display it in the given viewer
        //
        private void drawChart(WPFChartViewer viewer)
        {
            // Create an XYChart object 500 x 480 pixels in size, with the same background color
            // as the container
            XYChart c = new XYChart(500, 480, 0xccccff);

            // Set the plotarea at (50, 40) and of size 400 x 400 pixels. Use light grey (c0c0c0)
            // horizontal and vertical grid lines. Set 4 quadrant coloring, where the colors of 
            // the quadrants alternate between lighter and deeper grey (dddddd/eeeeee). 
            c.setPlotArea(50, 40, 400, 400, -1, -1, -1, 0xc0c0c0, 0xc0c0c0
                ).set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000);

            // Enable clipping mode to clip the part of the data that is outside the plot area.
            c.setClipping();

            // Set 4 quadrant mode, with both x and y axes symetrical around the origin
            c.setAxisAtOrigin(Chart.XYAxisAtOrigin, Chart.XAxisSymmetric + Chart.YAxisSymmetric);

            // Add a legend box at (450, 40) (top right corner of the chart) with vertical layout
            // and 8 pts Arial Bold font. Set the background color to semi-transparent grey.
            LegendBox b = c.addLegend(450, 40, true, "Arial Bold", 8);
            b.setAlignment(Chart.TopRight);
            b.setBackground(0x40dddddd);

            // Add a titles to axes
            c.xAxis().setTitle("Alpha Index");
            c.yAxis().setTitle("Beta Index");

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

            // The default ChartDirector settings has a denser y-axis grid spacing and less-dense
            // x-axis grid spacing. In this demo, we want the tick spacing to be symmetrical.
            // We use around 50 pixels between major ticks and 25 pixels between minor ticks.
            c.xAxis().setTickDensity(50, 25);
            c.yAxis().setTickDensity(50, 25);

            //
            // In this example, we represent the data by scatter points. If you want to represent
            // the data by somethings else (lines, bars, areas, floating boxes, etc), just modify
            // the code below to use the layer type of your choice. 
            //

            // Add scatter layer, using 11 pixels red (ff33333) X shape symbols
            c.addScatterLayer(dataX0, dataY0, "Group A", Chart.Cross2Shape(), 11, 0xff3333);

            // Add scatter layer, using 11 pixels green (33ff33) circle symbols
            c.addScatterLayer(dataX1, dataY1, "Group B", Chart.CircleShape, 11, 0x33ff33);

            // Add scatter layer, using 11 pixels blue (3333ff) triangle symbols
            c.addScatterLayer(dataX2, dataY2, "Group C", Chart.TriangleSymbol, 11, 0x3333ff);

            //
            // In this example, we have not explicitly configured the full x and y range. In this case, the
            // first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis and
            // assume the resulting range is the full range. In subsequent calls, ChartDirector will set the
            // axis range based on the view port and the full range.
            //
            viewer.syncLinearAxisWithViewPort("x", c.xAxis());
            viewer.syncLinearAxisWithViewPort("y", c.yAxis());

            // We need to update the track line too. If the mouse is moving on the chart (eg. if 
            // the user drags the mouse on the chart to scroll it), the track line will be updated
            // in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
            if ((!viewer.IsInMouseMoveEvent) && viewer.IsMouseOnPlotArea)
                crossHair(c, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY);

            // Set the chart image to the ChartViewer
            viewer.Chart = c;
        }

        //
        // Draw the full thumbnail chart and display it in the given ViewPortControl
        //
        private void drawFullChart(WPFViewPortControl vpc, WPFChartViewer viewer)
        {
            // Create an XYChart object of the same size as the Viewport Control
            XYChart c = new XYChart((int)vpc.ActualWidth, (int)vpc.ActualHeight);

            // Set the plotarea to cover the entire chart. Disable grid lines by setting their colors
            // to transparent. Set 4 quadrant coloring, where the colors of the quadrants alternate 
            // between lighter and deeper grey (dddddd/eeeeee). 
            c.setPlotArea(0, 0, c.getWidth() - 1, c.getHeight() - 1, -1, -1, 0xff0000, Chart.Transparent,
                Chart.Transparent).set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000);

            // Set 4 quadrant mode, with both x and y axes symetrical around the origin
            c.setAxisAtOrigin(Chart.XYAxisAtOrigin, Chart.XAxisSymmetric + Chart.YAxisSymmetric);

            // The x and y axis scales reflect the full range of the view port
            c.xAxis().setLinearScale(viewer.getValueAtViewPort("x", 0), viewer.getValueAtViewPort("x", 1),
                Chart.NoValue);
            c.yAxis().setLinearScale(viewer.getValueAtViewPort("y", 0), viewer.getValueAtViewPort("y", 1),
                Chart.NoValue);

            // Add scatter layer, using 3 pixels red (ff33333) X shape symbols
            c.addScatterLayer(dataX0, dataY0, "Group A", Chart.Cross2Shape(), 3, 0xff3333, 0xff3333);

            // Add scatter layer, using 3 pixels green (33ff33) circle symbols
            c.addScatterLayer(dataX1, dataY1, "Group B", Chart.CircleShape, 3, 0x33ff33, 0x33ff33);

            // Add scatter layer, using 3 pixels blue (3333ff) triangle symbols
            c.addScatterLayer(dataX2, dataY2, "Group C", Chart.TriangleSymbol, 3, 0x3333ff, 0x3333ff);

            // Set the chart image to the ViewPortControl
            vpc.Chart = c;
        }

        //
        // Update the image map
        //
        private void updateImageMap(WPFChartViewer viewer)
        {
            // Include tool tip for the chart
            if ((viewer.ImageMap == null) && (viewer.Chart != null))
            {
                viewer.ImageMap = viewer.Chart.getHTMLImageMap("clickable", "",
                    "title='[{dataSetName}] Alpha = {x}, Beta = {value}'");
            }
        }

        //
        // ClickHotSpot event handler. In this demo, we just display the hot spot parameters in a pop up 
        // dialog.
        //
        private void WPFChartViewer1_ClickHotSpot(object sender, WPFHotSpotEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("w=" + Width + ". h=" + Height);
            var viewer = sender as WPFChartViewer;

            // We show the pop up dialog only when the mouse action is not in zoom-in or zoom-out mode.
            if ((viewer.MouseUsage != WinChartMouseUsage.ZoomIn)
                && (viewer.MouseUsage != WinChartMouseUsage.ZoomOut))
                new ParamViewer().Display(sender, e);
        }

        //
        // Pointer (Drag to Scroll) button event handler
        //
        private void pointerPB_Checked(object sender, RoutedEventArgs e)
        {
            WPFChartViewer1.MouseUsage = WinChartMouseUsage.ScrollOnDrag;
        }

        //
        // Zoom In button event handler
        //
        private void zoomInPB_Checked(object sender, RoutedEventArgs e)
        {
            WPFChartViewer1.MouseUsage = WinChartMouseUsage.ZoomIn;
        }

        //
        // Zoom Out button event handler
        //
        private void zoomOutPB_Checked(object sender, RoutedEventArgs e)
        {
            WPFChartViewer1.MouseUsage = WinChartMouseUsage.ZoomOut;
        }

        //
        // Save button event handler
        //
        private void savePB_Click(object sender, RoutedEventArgs e)
        {
            // The standard Save File dialog
            SaveFileDialog fileDlg = new SaveFileDialog();
            fileDlg.Filter = "PNG (*.png)|*.png|JPG (*.jpg)|*.jpg|GIF (*.gif)|*.gif|BMP (*.bmp)|*.bmp|" +
                "SVG (*.svg)|*.svg|PDF (*.pdf)|*.pdf";
            fileDlg.FileName = "chartdirector_demo";
            var ret = fileDlg.ShowDialog(this);
            if (!(ret.HasValue && ret.Value))
                return;

            // Save the chart
            if (null != WPFChartViewer1.Chart)
                WPFChartViewer1.Chart.makeChart(fileDlg.FileName);
        }

        //
        // ValueChanged event handler for zoomBar. Zoom in around the center point and try to 
        // maintain the aspect ratio
        //
        private void zoomBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            var viewer = WPFChartViewer1;

            if (!viewer.IsInViewPortChangedEvent)
            {
                //Remember the center point
                double centerX = viewer.ViewPortLeft + viewer.ViewPortWidth / 2;
                double centerY = viewer.ViewPortTop + viewer.ViewPortHeight / 2;

                //Aspect ratio and zoom factor
                double aspectRatio = viewer.ViewPortWidth / viewer.ViewPortHeight;
                double zoomTo = ((double)zoomBar.Value) / zoomBar.Maximum;
                zoomTo = Math.Max(zoomTo, Math.Min(viewer.ZoomInWidthLimit, viewer.ZoomInHeightLimit));

                //Zoom while respecting the aspect ratio
                viewer.ViewPortWidth = zoomTo * Math.Max(1, aspectRatio);
                viewer.ViewPortHeight = zoomTo * Math.Max(1, 1 / aspectRatio);

                //Adjust ViewPortLeft and ViewPortTop to keep center point unchanged
                viewer.ViewPortLeft = centerX - viewer.ViewPortWidth / 2;
                viewer.ViewPortTop = centerY - viewer.ViewPortHeight / 2;

                //update the chart, but no need to update the image map yet, as the chart is still
                //zooming and is unstable
                viewer.updateViewPort(true, false);
            }
        }

        //
        // Draw track cursor when mouse is moving over plotarea, and update image map if necessary
        //
        private void WPFChartViewer1_MouseMovePlotArea(object sender, MouseEventArgs e)
        {
            var viewer = sender as WPFChartViewer;

            // Draw crosshair track cursor
            crossHair((XYChart)viewer.Chart, viewer.PlotAreaMouseX, viewer.PlotAreaMouseY);
            viewer.updateDisplay();

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

            // Update image map if necessary. If the mouse is still dragging, the chart is still 
            // updating and not confirmed, so there is no need to set up the image map.
            if (!viewer.IsMouseDragging)
                updateImageMap(viewer);
        }

        //
        // Draw cross hair cursor with axis labels
        //
        private void crossHair(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();

            // Draw a vertical line and a horizontal line as the cross hair
            d.vline(plotArea.getTopY(), plotArea.getBottomY(), mouseX, d.dashLineColor(0x000000, 0x0101));
            d.hline(plotArea.getLeftX(), plotArea.getRightX(), mouseY, d.dashLineColor(0x000000, 0x0101));

            // Draw y-axis label
            string label = "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" + c.formatValue(c.getYValue(
                mouseY, c.yAxis()), "{value|P4}") + "<*/*>";
            TTFText t = d.text(label, "Arial Bold", 8);
            t.draw(plotArea.getLeftX() - 5, mouseY, 0x000000, Chart.Right);

            // Draw x-axis label
            label = "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" + c.formatValue(c.getXValue(mouseX),
                "{value|P4}") + "<*/*>";
            t = d.text(label, "Arial Bold", 8);
            t.draw(mouseX, plotArea.getBottomY() + 5, 0x000000, Chart.Top);
        }
    }
}