ChartDirector 7.1 (.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. Those descriptions apply to WPFChartViewer 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\CSharpWPFCharts\XYZoomScrollWindow.xaml
<Window x:Class="CSharpWPFCharts.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:CSharpWPFCharts" 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="/icons/scroll_icon.png" 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="/icons/zoomin_icon.png" 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="/icons/zoomout_icon.png" 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="/icons/save_icon.png" Height="16" /> <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" ViewPortExternalColor="#7F000000" ViewPortBorderColor="#7FFFFFFF" SelectionBorderColor="#7FFFFFFF" /> </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\CSharpWPFCharts\XYZoomScrollWindow.xaml.cs
using System; using System.Windows; using System.Windows.Input; using Microsoft.Win32; using ChartDirector; namespace CSharpWPFCharts { /// <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) { 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); } } }