ChartDirector 7.1 (.NET Edition)

Zoom/Scroll with PDF Report (Windows)




NOTE: This section describes Zoom/Scroll with PDF Report for Windows applications only. For web applications, please refer to Zoom/Scroll with PDF Report (Web).

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

This example extends Zooming and Scrolling with Track Line (1) (Windows) to demonstrate creating a PDF report with multiple charts covering the entire data range.

This example adds two buttons to the Zooming and Scrolling with Track Line (1) (Windows) example. The "Save Chart Image" button uses BaseChart.makeChart to save the visible chart in PNG, JPG, GIF, PDF or SVG format. The "Create PDF Report" button uses MultiPagePDF to create a PDF Report with multiple charts to cover the entire data range.

MultiPagePDF works by generating a PDF page for chart added to it. The chart can be a MultiChart, and can contain free form CDML text, tables, shape and images. In this way, MultiPagePDF can create complete PDF reports.

In this example, the PDF report is generated as follows:

Source Code Listing

[Windows Forms - C# version] NetWinCharts\CSharpWinCharts\frmzoomscrollpdf.cs
using System; using System.Collections; using System.Windows.Forms; using ChartDirector; namespace CSharpChartExplorer { public partial class FrmZoomScrollPDF : Form { // Data arrays private DateTime[] timeStamps; private double[] dataSeriesA; private double[] dataSeriesB; private double[] dataSeriesC; // Flag to indicated if initialization has been completed. Prevents events from firing before // controls are properly initialized. private bool hasFinishedInitialization; public FrmZoomScrollPDF() { InitializeComponent(); } private void FrmZoomScrollPDF_Load(object sender, EventArgs e) { // Load the data loadData(); // Initialize the WinChartViewer initChartViewer(winChartViewer1); // Can handle events now hasFinishedInitialization = true; // Trigger the ViewPortChanged event to draw the chart winChartViewer1.updateViewPort(true, true); } // // Load the data // private void loadData() { // In this example, we just use random numbers as data. RanSeries r = new RanSeries(127); timeStamps = r.getDateSeries(1827, new DateTime(2015, 1, 1), 86400); dataSeriesA = r.getSeries2(1827, 150, -10, 10); dataSeriesB = r.getSeries2(1827, 200, -10, 10); dataSeriesC = r.getSeries2(1827, 250, -8, 8); } // // Initialize the WinChartViewer // private void initChartViewer(WinChartViewer viewer) { // Set the full x range to be the duration of the data viewer.setFullRange("x", timeStamps[0], timeStamps[timeStamps.Length - 1]); // Initialize the view port to show the latest 20% of the time range viewer.ViewPortWidth = 0.2; viewer.ViewPortLeft = 1 - viewer.ViewPortWidth; // Set the maximum zoom to 10 points viewer.ZoomInWidthLimit = 10.0 / timeStamps.Length; // Enable mouse wheel zooming by setting the zoom ratio to 1.1 per wheel event viewer.MouseWheelZoomRatio = 1.1; // Initially set the mouse usage to "Pointer" mode (Drag to Scroll mode) pointerPB.Checked = true; } // // 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 controls when the view port changed // private void updateControls(WinChartViewer viewer) { // In this demo, we need to update the scroll bar to reflect the view port position and // width of the view port. hScrollBar1.Enabled = viewer.ViewPortWidth < 1; hScrollBar1.LargeChange = (int)Math.Ceiling(viewer.ViewPortWidth * (hScrollBar1.Maximum - hScrollBar1.Minimum)); hScrollBar1.SmallChange = (int)Math.Ceiling(hScrollBar1.LargeChange * 0.1); hScrollBar1.Value = (int)Math.Round(viewer.ViewPortLeft * (hScrollBar1.Maximum - hScrollBar1.Minimum)) + hScrollBar1.Minimum; } // // Draw the chart. // private void drawChart(WinChartViewer viewer) { // Get the start date and end date that are visible on the chart. DateTime viewPortStartDate = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortLeft)); DateTime viewPortEndDate = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortRight)); // Draw the XYChart XYChart c = drawXYChart(viewPortStartDate, viewPortEndDate); // Add a title to the chart using 18 pts Times New Roman Bold Italic font c.addTitle(" PDF Report Demonstration", "Times New Roman Bold Italic", 18); // 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) trackLineLabel(c, viewer.PlotAreaMouseX); viewer.Chart = c; } // // Draw an XYChart using data from startX to endX // XYChart drawXYChart(DateTime startX, DateTime endX) { // Get the array indexes that corresponds to the visible start and end dates int startIndex = (int)Math.Floor(Chart.bSearch(timeStamps, startX)); int endIndex = (int)Math.Ceiling(Chart.bSearch(timeStamps, endX)); int noOfPoints = endIndex - startIndex + 1; // Extract the part of the data array that are visible. DateTime[] viewPortTimeStamps = Chart.arraySlice(timeStamps, startIndex, noOfPoints); double[] viewPortDataSeriesA = Chart.arraySlice(dataSeriesA, startIndex, noOfPoints); double[] viewPortDataSeriesB = Chart.arraySlice(dataSeriesB, startIndex, noOfPoints); double[] viewPortDataSeriesC = Chart.arraySlice(dataSeriesC, startIndex, noOfPoints); // // At this stage, we have extracted the visible data. We can use those data to plot the chart. // //================================================================================ // Configure overall chart appearance. //================================================================================ // Create an XYChart object of size 640 x 350 pixels XYChart c = new XYChart(640, 350); // Set the plotarea at (55, 50) with width 80 pixels less than chart width, and height 85 pixels // less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff) // as background. Set border to transparent and grid lines to white (ffffff). c.setPlotArea(55, 50, c.getWidth() - 80, c.getHeight() - 85, c.linearGradientColor(0, 50, 0, c.getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart.Transparent, 0xffffff, 0xffffff); // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping. c.setClipping(); // Add a legend box at (55, 25) using horizontal layout. Use 8pts Arial Bold as font. Set the // background and border color to Transparent and use line style legend key. LegendBox b = c.addLegend(55, 25, false, "Arial Bold", 8); b.setBackground(Chart.Transparent); b.setLineStyleKey(); // Set the axis stem to transparent c.xAxis().setColors(Chart.Transparent); c.yAxis().setColors(Chart.Transparent); // Add axis title using 10pts Arial Bold Italic font c.yAxis().setTitle("Ionic Temperature (C)", "Arial Bold Italic", 10); //================================================================================ // Add data to chart //================================================================================ // // In this example, we represent the data by lines. You may modify the code below to use other // representations (areas, scatter plot, etc). // // Add a line layer for the lines, using a line width of 2 pixels LineLayer layer = c.addLineLayer2(); layer.setLineWidth(2); // In this demo, we do not have too many data points. In real code, the chart may contain a lot // of data points when fully zoomed out - much more than the number of horizontal pixels in this // plot area. So it is a good idea to use fast line mode. layer.setFastLineMode(); // Now we add the 3 data series to a line layer, using the color red (ff33333), green (008800) // and blue (3333cc) layer.setXData(viewPortTimeStamps); layer.addDataSet(viewPortDataSeriesA, 0xff3333, "Alpha"); layer.addDataSet(viewPortDataSeriesB, 0x008800, "Beta"); layer.addDataSet(viewPortDataSeriesC, 0x3333cc, "Gamma"); //================================================================================ // Configure axis scale and labelling //================================================================================ // Set the x-axis scale c.xAxis().setDateScale(startX, endX); // // In this demo, the time range can be from a few years to a few days. We demonstrate how to set // up different date/time format based on the time range. // // If all ticks are yearly aligned, then we use "yyyy" as the label format. c.xAxis().setFormatCondition("align", 360 * 86400); c.xAxis().setLabelFormat("{value|yyyy}"); // If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first label of a // year, and "mmm" for other labels. c.xAxis().setFormatCondition("align", 30 * 86400); c.xAxis().setMultiFormat(Chart.StartOfYearFilter(), "<*font=bold*>{value|mmm yyyy}", Chart.AllPassFilter(), "{value|mmm}"); // If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the first // label of a year, and "mmm dd" in bold font as the first label of a month, and "dd" for other // labels. c.xAxis().setFormatCondition("align", 86400); c.xAxis().setMultiFormat(Chart.StartOfYearFilter(), "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", Chart.StartOfMonthFilter(), "<*font=bold*>{value|mmm dd}"); c.xAxis().setMultiFormat2(Chart.AllPassFilter(), "{value|dd}"); // For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of a day, // and "hh:nn" for other labels. c.xAxis().setFormatCondition("else"); c.xAxis().setMultiFormat(Chart.StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}", Chart.AllPassFilter(), "{value|hh:nn}"); return c; } // // 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; } // // The scroll bar event handler // private void hScrollBar1_ValueChanged(object sender, EventArgs e) { // When the view port is changed (user drags on the chart to scroll), the scroll bar will get // updated. When the scroll bar changes (eg. user drags on the scroll bar), the view port will // get updated. This creates an infinite loop. To avoid this, the scroll bar can update the // view port only if the view port is not updating the scroll bar. if (hasFinishedInitialization && !winChartViewer1.IsInViewPortChangedEvent) { // Set the view port based on the scroll bar winChartViewer1.ViewPortLeft = ((double)(hScrollBar1.Value - hScrollBar1.Minimum)) / (hScrollBar1.Maximum - hScrollBar1.Minimum); // Trigger a view port changed event to update the chart winChartViewer1.updateViewPort(true, false); } } // // Draw track cursor when mouse is moving over plotarea // private void winChartViewer1_MouseMovePlotArea(object sender, MouseEventArgs e) { WinChartViewer viewer = (WinChartViewer)sender; trackLineLabel((XYChart)viewer.Chart, viewer.PlotAreaMouseX); viewer.updateDisplay(); // Hide the track cursor when the mouse leaves the plot area viewer.removeDynamicLayer("MouseLeavePlotArea"); } // // Draw track line with data labels // private void trackLineLabel(XYChart c, int mouseX) { // 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, and find its pixel coordinate. double xValue = c.getNearestXValue(mouseX); int xCoor = c.getXCoor(xValue); // Draw a vertical track line at the x-position d.vline(plotArea.getTopY(), plotArea.getBottomY(), xCoor, d.dashLineColor(0x000000, 0x0101)); // Draw a label on the x-axis to show the track line position. string xlabel = "<*font,bgColor=000000*> " + c.xAxis().getFormattedLabel(xValue, "mmm dd, yyyy") + " <*/font*>"; TTFText t = d.text(xlabel, "Arial Bold", 8); // Restrict the x-pixel position of the label to make sure it stays inside the chart image. int xLabelPos = Math.Max(0, Math.Min(xCoor - t.getWidth() / 2, c.getWidth() - t.getWidth())); t.draw(xLabelPos, plotArea.getBottomY() + 6, 0xffffff); // Iterate through all layers to draw the data labels 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); // Get the color and position of the data label int color = dataSet.getDataColor(); int yCoor = c.getYCoor(dataSet.getPosition(xIndex), dataSet.getUseYAxis()); // Draw a track dot with a label next to it for visible data points in the plot area if ((yCoor >= plotArea.getTopY()) && (yCoor <= plotArea.getBottomY()) && (color != Chart.Transparent) && (!string.IsNullOrEmpty(dataSet.getDataName()))) { d.circle(xCoor, yCoor, 4, 4, color, color); string label = "<*font,bgColor=" + color.ToString("x") + "*> " + c.formatValue( dataSet.getValue(xIndex), "{value|P4}") + " <*/font*>"; t = d.text(label, "Arial Bold", 8); // Draw the label on the right side of the dot if the mouse is on the left side the // chart, and vice versa. This ensures the label will not go outside the chart image. if (xCoor <= (plotArea.getLeftX() + plotArea.getRightX()) / 2) { t.draw(xCoor + 5, yCoor, 0xffffff, Chart.Left); } else { t.draw(xCoor - 5, yCoor, 0xffffff, Chart.Right); } } } } } 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); } private void pdfReportPB_Click(object sender, EventArgs e) { // The standard Save File dialog SaveFileDialog fileDlg = new SaveFileDialog(); fileDlg.Filter = "PDF (*.pdf)|*.pdf"; fileDlg.FileName = "chartdirector_report"; if (fileDlg.ShowDialog() == DialogResult.OK) createPdfReport(fileDlg.FileName); } // // Create a multi-page PDF Report // private void createPdfReport(string filename) { // The MultiPagePDF object can create PDF from multiple pages, each with one chart // object. Since a chart object can contain text (eg. using BaseChart.addText) and // other charts (eg. using MultiChart), that means each page can contain text and // multiple charts. MultiPagePDF doc = new MultiPagePDF(); // Page configuration - A4 = 210 x 297mm. The PDF default is 96 dpi (dot per inch), // so the A4 size is equal to 794 x 1123 dots. string pageConfig = "pagewidth = 794; pageHeight = 1123"; // In this example, we include a cover page with only text. This is by creating an // empty pie chart with text only. PieChart firstPage = new PieChart(720, 960); firstPage.addText(360, 320, "<*size=50*>ChartDirector<*br*><*size=30*>PDF Report Demonstration<*/*>", "Arial Bold", 30, 0x000000, Chart.Center); firstPage.setOutputOptions(pageConfig); doc.addPage(firstPage); // We include 2 charts per page, with each chart showing one year of data. Each page // will also have a header and page number int startYear = timeStamps[0].Year; int endYear = timeStamps[timeStamps.Length - 1].AddSeconds(-1).Year; int pageNumber = 0; for (int yyyy = startYear; yyyy <= endYear; yyyy += 2) { // This chart is the page. MultiChart m = new MultiChart(760, 920); // Use addTitle to add a header m.addTitle("ChartDirector PDF Report Demonstration", "Arial Bold", 20); // Create the first chart XYChart c = drawXYChart(new DateTime(yyyy, 1, 1), new DateTime(yyyy + 1, 1, 1)); m.addChart((m.getWidth() - c.getWidth()) / 2, 100, c); c.addTitle("Year " + yyyy); XYChart c2 = null; if (yyyy < endYear) { // Create the second chart c2 = drawXYChart(new DateTime(yyyy + 1, 1, 1), new DateTime(yyyy + 2, 1, 1)); c2.addTitle("Year " + (yyyy + 1)); m.addChart((m.getWidth() - c2.getWidth()) / 2, 500, c2); } // Add the page number ++pageNumber; m.addTitle(Chart.BottomCenter, "" + pageNumber, "Arial Bold", 8); m.setOutputOptions(pageConfig); doc.addPage(m); } // Output the PDF report doc.outPDF(filename); } } }

[Windows Forms - VB Version] NetWinCharts\VBNetWinCharts\frmzoomscrollpdf.vb
Imports ChartDirector Public Class FrmZoomScrollPDF ' Data arrays Private timeStamps() As DateTime Private dataSeriesA() As Double Private dataSeriesB() As Double Private dataSeriesC() As Double ' Flag to indicated if initialization has been completed. Prevents events from firing before ' controls are properly initialized. Private hasFinishedInitialization As Boolean Private Sub FrmZoomScrollPDF_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' Load the data loadData() ' Initialize the WinChartViewer initChartViewer(winChartViewer1) ' Can handle events now hasFinishedInitialization = True ' Trigger the ViewPortChanged event to draw the chart winChartViewer1.updateViewPort(True, True) End Sub ' ' Load the data ' Private Sub loadData() ' In this example, we just use random numbers as data. Dim r As RanSeries = New RanSeries(127) timeStamps = r.getDateSeries(1827, New DateTime(2015, 1, 1), 86400) dataSeriesA = r.getSeries2(1827, 150, -10, 10) dataSeriesB = r.getSeries2(1827, 200, -10, 10) dataSeriesC = r.getSeries2(1827, 250, -8, 8) End Sub ' ' Initialize the WinChartViewer ' Private Sub initChartViewer(viewer As WinChartViewer) ' Set the full x range to be the duration of the data viewer.setFullRange("x", timeStamps(0), timeStamps(timeStamps.Length - 1)) ' Initialize the view port to show the latest 20% of the time range viewer.ViewPortWidth = 0.2 viewer.ViewPortLeft = 1 - viewer.ViewPortWidth ' Set the maximum zoom to 10 points viewer.ZoomInWidthLimit = 10.0 / timeStamps.Length ' Enable mouse wheel zooming by setting the zoom ratio to 1.1 per wheel event viewer.MouseWheelZoomRatio = 1.1 ' Initially set the mouse usage to "Pointer" mode (Drag to Scroll mode) pointerPB.Checked = True 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(sender As Object, 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 End Sub ' ' Update controls when the view port changed ' Private Sub updateControls(viewer As WinChartViewer) ' Update the scroll bar to reflect the view port position and width of the view port. hScrollBar1.Enabled = viewer.ViewPortWidth < 1 hScrollBar1.LargeChange = Math.Ceiling(viewer.ViewPortWidth * (hScrollBar1.Maximum - hScrollBar1.Minimum)) hScrollBar1.SmallChange = Math.Ceiling(hScrollBar1.LargeChange * 0.1) hScrollBar1.Value = Math.Round(viewer.ViewPortLeft * (hScrollBar1.Maximum - hScrollBar1.Minimum)) + hScrollBar1.Minimum End Sub ' ' Draw the chart. ' Private Sub drawChart(viewer As WinChartViewer) ' Get the start date And end date that are visible on the chart. Dim viewPortStartDate As Date = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortLeft)) Dim viewPortEndDate As Date = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortRight)) ' Draw the XYChart Dim c As XYChart = drawXYChart(viewPortStartDate, viewPortEndDate) ' Add a title to the chart using 18 pts Times New Roman Bold Italic font c.addTitle(" PDF Report Demonstration", "Times New Roman Bold Italic", 18) ' 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) AndAlso viewer.IsMouseOnPlotArea Then trackLineLabel(c, viewer.PlotAreaMouseX) End If viewer.Chart = c End Sub ' ' Draw an XYChart using data from startX to endX ' Private Function drawXYChart(startX As Date, endX As Date) As XYChart ' Get the array indexes that corresponds to the visible start And end dates Dim startIndex As Integer = Math.Floor(Chart.bSearch(timeStamps, startX)) Dim endIndex As Integer = Math.Ceiling(Chart.bSearch(timeStamps, endX)) Dim noOfPoints As Integer = endIndex - startIndex + 1 ' Extract the part of the data array that are visible. Dim viewPortTimeStamps() As Date = Chart.arraySlice(timeStamps, startIndex, noOfPoints) Dim viewPortDataSeriesA() As Double = Chart.arraySlice(dataSeriesA, startIndex, noOfPoints) Dim viewPortDataSeriesB() As Double = Chart.arraySlice(dataSeriesB, startIndex, noOfPoints) Dim viewPortDataSeriesC() As Double = Chart.arraySlice(dataSeriesC, startIndex, noOfPoints) ' ' At this stage, we have extracted the visible data. We can use those data to plot the chart. ' '================================================================================ ' Configure overall chart appearance. '================================================================================ ' Create an XYChart object of size 640 x 350 pixels Dim c As XYChart = New XYChart(640, 350) ' Set the plotarea at (55, 50) with width 80 pixels less than chart width, And height 85 pixels ' less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff) ' as background. Set border to transparent And grid lines to white (ffffff). c.setPlotArea(55, 50, c.getWidth() - 80, c.getHeight() - 85, c.linearGradientColor(0, 50, 0, c.getHeight() - 35, &HF0F6FF, &HA0C0FF), -1, Chart.Transparent, &HFFFFFF, &HFFFFFF) ' As the data can lie outside the plotarea in a zoomed chart, we need enable clipping. c.setClipping() ' Add a legend box at (55, 25) using horizontal layout. Use 8pts Arial Bold as font. Set the ' background And border color to Transparent And use line style legend key. Dim b As LegendBox = c.addLegend(55, 25, False, "Arial Bold", 8) b.setBackground(Chart.Transparent) b.setLineStyleKey() ' Set the axis stem to transparent c.xAxis().setColors(Chart.Transparent) c.yAxis().setColors(Chart.Transparent) ' Add axis title using 10pts Arial Bold Italic font c.yAxis().setTitle("Ionic Temperature (C)", "Arial Bold Italic", 10) '================================================================================ ' Add data to chart '================================================================================ ' ' In this example, we represent the data by lines. You may modify the code below to use other ' representations (areas, scatter plot, etc). ' ' Add a line layer for the lines, using a line width of 2 pixels Dim layer As LineLayer = c.addLineLayer2() layer.setLineWidth(2) ' In this demo, we do Not have too many data points. In real code, the chart may contain a lot ' of data points when fully zoomed out - much more than the number of horizontal pixels in this ' plot area. So it Is a good idea to use fast line mode. layer.setFastLineMode() ' Now we add the 3 data series to a line layer, using the color red (ff33333), green (008800) ' And blue (3333cc) layer.setXData(viewPortTimeStamps) layer.addDataSet(viewPortDataSeriesA, &HFF3333, "Alpha") layer.addDataSet(viewPortDataSeriesB, &H8800, "Beta") layer.addDataSet(viewPortDataSeriesC, &H3333CC, "Gamma") '================================================================================ ' Configure axis scale And labelling '================================================================================ ' Set the x-axis scale c.xAxis().setDateScale(startX, endX) ' ' In this demo, the time range can be from a few years to a few days. We demonstrate how to set ' up different date/time format based on the time range. ' ' If all ticks are yearly aligned, then we use "yyyy" as the label format. c.xAxis().setFormatCondition("align", 360 * 86400) c.xAxis().setLabelFormat("{value|yyyy}") ' If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first label of a ' year, And "mmm" for other labels. c.xAxis().setFormatCondition("align", 30 * 86400) c.xAxis().setMultiFormat(Chart.StartOfYearFilter(), "<*font=bold*>{value|mmm yyyy}", Chart.AllPassFilter(), "{value|mmm}") ' If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the first ' label of a year, And "mmm dd" in bold font as the first label of a month, And "dd" for other ' labels. c.xAxis().setFormatCondition("align", 86400) c.xAxis().setMultiFormat(Chart.StartOfYearFilter(), "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", Chart.StartOfMonthFilter(), "<*font=bold*>{value|mmm dd}") c.xAxis().setMultiFormat2(Chart.AllPassFilter(), "{value|dd}") ' For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of a day, ' And "hh:nn" for other labels. c.xAxis().setFormatCondition("else") c.xAxis().setMultiFormat(Chart.StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}", Chart.AllPassFilter(), "{value|hh:nn}") Return c End Function ' ' 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 ' ' The scroll bar event handler ' Private Sub hScrollBar1_ValueChanged(ByVal sender As Object, ByVal e As EventArgs) _ Handles hScrollBar1.ValueChanged ' When the view port is changed (user drags on the chart to scroll), the scroll bar will get ' updated. When the scroll bar changes (eg. user drags on the scroll bar), the view port will ' get updated. This creates an infinite loop. To avoid this, the scroll bar can update the ' view port only if the view port is not updating the scroll bar. If hasFinishedInitialization And Not winChartViewer1.IsInViewPortChangedEvent Then ' Set the view port based on the scroll bar winChartViewer1.ViewPortLeft = (hScrollBar1.Value - hScrollBar1.Minimum) / (hScrollBar1.Maximum - hScrollBar1.Minimum) ' Trigger a view port changed event to update the chart 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 MouseEventArgs) Handles winChartViewer1.MouseMovePlotArea Dim viewer As WinChartViewer = sender trackLineLabel(viewer.Chart, viewer.PlotAreaMouseX) viewer.updateDisplay() ' Hide the track cursor when the mouse leaves the plot area viewer.removeDynamicLayer("MouseLeavePlotArea") End Sub ' ' Draw track line with data labels ' Private Sub trackLineLabel(ByVal c As XYChart, ByVal mouseX 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, and find its pixel coordinate. Dim xValue As Double = c.getNearestXValue(mouseX) Dim xCoor As Integer = c.getXCoor(xValue) ' Draw a vertical track line at the x-position d.vline(plotArea.getTopY(), plotArea.getBottomY(), xCoor, d.dashLineColor(&H0, &H101)) ' Draw a label on the x-axis to show the track line position. Dim xlabel As String = "<*font,bgColor=000000*> " & c.xAxis().getFormattedLabel(xValue, "mmm dd, yyyy") & " <*/font*>" Dim t As TTFText = d.text(xlabel, "Arial Bold", 8) ' Restrict the x-pixel position of the label to make sure it stays inside the chart image. Dim xLabelPos As Integer = Math.Max(0, Math.Min(xCoor - t.getWidth() / 2, c.getWidth() - t.getWidth( ))) t.draw(xLabelPos, plotArea.getBottomY() + 6, &HFFFFFF) ' Iterate through all layers to draw the data labels 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) ' Get the color and position of the data label Dim color As Integer = dataSet.getDataColor() Dim yCoor As Integer = c.getYCoor(dataSet.getPosition(xIndex), dataSet.getUseYAxis()) ' Draw a track dot with a label next to it for visible data points in the plot area If (yCoor >= plotArea.getTopY()) And (yCoor <= plotArea.getBottomY()) And (color <> Chart.Transparent) And (Not String.IsNullOrEmpty(dataSet.getDataName())) Then d.circle(xCoor, yCoor, 4, 4, color, color) Dim label As String = "<*font,bgColor=" & Hex(color) & "*> " & c.formatValue( dataSet.getValue(xIndex), "{value|P4}") & " <*/font*>" t = d.text(label, "Arial Bold", 8) ' Draw the label on the right side of the dot if the mouse is on the left side the chart, ' and vice versa. This ensures the label will not go outside the chart image. If xCoor <= (plotArea.getLeftX() + plotArea.getRightX()) / 2 Then t.draw(xCoor + 5, yCoor, &HFFFFFF, Chart.Left) Else t.draw(xCoor - 5, yCoor, &HFFFFFF, Chart.Right) End If End If Next Next End Sub Private Sub savePB_Click(sender As Object, 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 Exit Sub End If ' Save the chart If Not IsNothing(winChartViewer1.Chart) Then winChartViewer1.Chart.makeChart(fileDlg.FileName) End If End Sub Private Sub pdfReportPB_Click(sender As Object, e As EventArgs) Handles pdfReportPB.Click ' The standard Save File dialog Dim fileDlg As SaveFileDialog = New SaveFileDialog() fileDlg.Filter = "PDF (*.pdf)|*.pdf" fileDlg.FileName = "chartdirector_report" If fileDlg.ShowDialog() = DialogResult.OK Then createPdfReport(fileDlg.FileName) End If End Sub ' ' Create a multi-page PDF Report ' Private Sub createPdfReport(filename As String) ' The MultiPagePDF object can create PDF from multiple pages, each with one chart ' object. Since a chart object can contain text (eg. using BaseChart.addText) And ' other charts (eg. using MultiChart), that means each page can contain text And ' multiple charts. Dim doc As MultiPagePDF = New MultiPagePDF() ' Page configuration - A4 = 210 x 297mm. The PDF default Is 96 dpi (dot per inch), ' so the A4 size Is equal to 794 x 1123 dots. Dim pageConfig As String = "pagewidth = 794; pageHeight = 1123" ' In this example, we include a cover page with only text. This Is by creating an ' empty pie chart with text only. Dim firstPage As PieChart = New PieChart(720, 960) firstPage.addText(360, 320, "<*size=50*>ChartDirector<*br*><*size=30*>PDF Report Demonstration<*/*>", "Arial Bold", 30, &H0, Chart.Center) firstPage.setOutputOptions(pageConfig) doc.addPage(firstPage) ' We include 2 charts per page, with each chart showing one year of data. Each page ' will also have a header And page number Dim startYear As Integer = timeStamps(0).Year Dim endYear As Integer = timeStamps(timeStamps.Length - 1).AddSeconds(-1).Year Dim pageNumber As Integer = 0 For yyyy As Integer = startYear To endYear Step 2 ' This chart Is the page. Dim m As MultiChart = New MultiChart(760, 920) ' Use addTitle to add a header m.addTitle("ChartDirector PDF Report Demonstration", "Arial Bold", 20) ' Create the first chart Dim c As XYChart = drawXYChart(New DateTime(yyyy, 1, 1), New DateTime(yyyy + 1, 1, 1)) m.addChart((m.getWidth() - c.getWidth()) / 2, 100, c) c.addTitle("Year " & yyyy) Dim c2 As XYChart = Nothing If yyyy < endYear Then ' Create the second chart c2 = drawXYChart(New DateTime(yyyy + 1, 1, 1), New DateTime(yyyy + 2, 1, 1)) c2.addTitle("Year " & (yyyy + 1)) m.addChart((m.getWidth() - c2.getWidth()) / 2, 500, c2) End If ' Add the page number pageNumber += 1 m.addTitle(Chart.BottomCenter, "" & pageNumber, "Arial Bold", 8) m.setOutputOptions(pageConfig) doc.addPage(m) Next ' Output the PDF report doc.outPDF(filename) End Sub End Class

[WPF - XAML] NetWPFCharts\CSharpWPFCharts\ZoomScrollPDFWindow.xaml
<Window x:Class="CSharpWPFCharts.ZoomScrollPDFWindow" 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="PDF Report Demonstration" Loaded="Window_Loaded" SizeToContent="WidthAndHeight" ResizeMode="NoResize" > <DockPanel> <Label Content="Advanced Software Engineering" 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" Padding="5" Click="savePB_Click"> <TextBlock Padding="0,0,1,0">Save Chart Image</TextBlock> </Button> <Button x:Name="pdfReportPB" Margin="0,16,0,0" Padding="5" Click="pdfReportPB_Click"> <TextBlock Padding="0,0,1,0">Create PDF Report </TextBlock> </Button> </StackPanel> <ChartDirector:WPFChartViewer x:Name="WPFChartViewer1" DockPanel.Dock="Top" Width="640" Height="350" Margin="5" ViewPortChanged="WPFChartViewer1_ViewPortChanged" MouseMovePlotArea="WPFChartViewer1_MouseMovePlotArea" /> <ScrollBar x:Name="hScrollBar1" DockPanel.Dock="Top" Orientation="Horizontal" ValueChanged="hScrollBar1_ValueChanged"/> </DockPanel> </Window>

[WPF - C#] NetWPFCharts\CSharpWPFCharts\ZoomScrollPDFWindow.xaml.cs
using System; using System.Windows; using System.Windows.Input; using Microsoft.Win32; using ChartDirector; namespace CSharpWPFCharts { /// <summary> /// Interaction logic for ZoomScrollPDFWindow.xaml /// </summary> public partial class ZoomScrollPDFWindow : Window { // Data arrays private DateTime[] timeStamps; private double[] dataSeriesA; private double[] dataSeriesB; private double[] dataSeriesC; public ZoomScrollPDFWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { // Load the data loadData(); // Initialize the WinChartViewer initChartViewer(WPFChartViewer1); // Trigger the ViewPortChanged event to draw the chart WPFChartViewer1.updateViewPort(true, true); } // // Load the data // private void loadData() { // In this example, we just use random numbers as data. RanSeries r = new RanSeries(127); timeStamps = r.getDateSeries(1827, new DateTime(2015, 1, 1), 86400); dataSeriesA = r.getSeries2(1827, 150, -10, 10); dataSeriesB = r.getSeries2(1827, 200, -10, 10); dataSeriesC = r.getSeries2(1827, 250, -8, 8); } // // Initialize the WinChartViewer // private void initChartViewer(WPFChartViewer viewer) { // Set the full x range to be the duration of the data viewer.setFullRange("x", timeStamps[0], timeStamps[timeStamps.Length - 1]); // Initialize the view port to show the latest 20% of the time range viewer.ViewPortWidth = 0.2; viewer.ViewPortLeft = 1 - viewer.ViewPortWidth; // Set the maximum zoom to 10 points viewer.ZoomInWidthLimit = 10.0 / timeStamps.Length; // Enable mouse wheel zooming by setting the zoom ratio to 1.1 per wheel event viewer.MouseWheelZoomRatio = 1.1; // Initially set the mouse usage to "Pointer" mode (Drag to Scroll mode) pointerPB.IsChecked = true; } // // 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) { // In addition to updating the chart, we may also need to update other controls that // changes based on the view port. updateControls(WPFChartViewer1); // Update the chart if necessary if (e.NeedUpdateChart) drawChart(WPFChartViewer1); } // // Update controls when the view port changed // private void updateControls(WPFChartViewer viewer) { // In this demo, we need to update the scroll bar to reflect the view port position and // width of the view port. hScrollBar1.IsEnabled = viewer.ViewPortWidth < 1; hScrollBar1.LargeChange = viewer.ViewPortWidth * (hScrollBar1.Maximum - hScrollBar1.Minimum); hScrollBar1.SmallChange = hScrollBar1.LargeChange * 0.1; hScrollBar1.ViewportSize = viewer.ViewPortWidth / Math.Max(1E-10, 1 - viewer.ViewPortWidth) * (hScrollBar1.Maximum - hScrollBar1.Minimum); hScrollBar1.Value = viewer.ViewPortLeft / Math.Max(1E-10, 1 - viewer.ViewPortWidth) * (hScrollBar1.Maximum - hScrollBar1.Minimum) + hScrollBar1.Minimum; } // // Draw the chart. // private void drawChart(WPFChartViewer viewer) { // Get the start date and end date that are visible on the chart. DateTime viewPortStartDate = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortLeft)); DateTime viewPortEndDate = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortRight)); // Draw the XYChart XYChart c = drawXYChart(viewPortStartDate, viewPortEndDate); // Add a title to the chart using 18 pts Times New Roman Bold Italic font c.addTitle(" PDF Report Demonstration", "Times New Roman Bold Italic", 18); // 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) trackLineLabel(c, viewer.PlotAreaMouseX); viewer.Chart = c; } // // Draw an XYChart using data from startX to endX // XYChart drawXYChart(DateTime startX, DateTime endX) { // Get the array indexes that corresponds to the visible start and end dates int startIndex = (int)Math.Floor(Chart.bSearch(timeStamps, startX)); int endIndex = (int)Math.Ceiling(Chart.bSearch(timeStamps, endX)); int noOfPoints = endIndex - startIndex + 1; // Extract the part of the data array that are visible. DateTime[] viewPortTimeStamps = Chart.arraySlice(timeStamps, startIndex, noOfPoints); double[] viewPortDataSeriesA = Chart.arraySlice(dataSeriesA, startIndex, noOfPoints); double[] viewPortDataSeriesB = Chart.arraySlice(dataSeriesB, startIndex, noOfPoints); double[] viewPortDataSeriesC = Chart.arraySlice(dataSeriesC, startIndex, noOfPoints); // // At this stage, we have extracted the visible data. We can use those data to plot the chart. // //================================================================================ // Configure overall chart appearance. //================================================================================ // Create an XYChart object of size 640 x 350 pixels XYChart c = new XYChart(640, 350); // Set the plotarea at (55, 50) with width 80 pixels less than chart width, and height 85 pixels // less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff) // as background. Set border to transparent and grid lines to white (ffffff). c.setPlotArea(55, 50, c.getWidth() - 80, c.getHeight() - 85, c.linearGradientColor(0, 50, 0, c.getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart.Transparent, 0xffffff, 0xffffff); // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping. c.setClipping(); // Add a legend box at (55, 25) using horizontal layout. Use 8pts Arial Bold as font. Set the // background and border color to Transparent and use line style legend key. LegendBox b = c.addLegend(55, 25, false, "Arial Bold", 8); b.setBackground(Chart.Transparent); b.setLineStyleKey(); // Set the axis stem to transparent c.xAxis().setColors(Chart.Transparent); c.yAxis().setColors(Chart.Transparent); // Add axis title using 10pts Arial Bold Italic font c.yAxis().setTitle("Ionic Temperature (C)", "Arial Bold Italic", 10); //================================================================================ // Add data to chart //================================================================================ // // In this example, we represent the data by lines. You may modify the code below to use other // representations (areas, scatter plot, etc). // // Add a line layer for the lines, using a line width of 2 pixels LineLayer layer = c.addLineLayer2(); layer.setLineWidth(2); // In this demo, we do not have too many data points. In real code, the chart may contain a lot // of data points when fully zoomed out - much more than the number of horizontal pixels in this // plot area. So it is a good idea to use fast line mode. layer.setFastLineMode(); // Now we add the 3 data series to a line layer, using the color red (ff33333), green (008800) // and blue (3333cc) layer.setXData(viewPortTimeStamps); layer.addDataSet(viewPortDataSeriesA, 0xff3333, "Alpha"); layer.addDataSet(viewPortDataSeriesB, 0x008800, "Beta"); layer.addDataSet(viewPortDataSeriesC, 0x3333cc, "Gamma"); //================================================================================ // Configure axis scale and labelling //================================================================================ // Set the x-axis scale c.xAxis().setDateScale(startX, endX); // // In this demo, the time range can be from a few years to a few days. We demonstrate how to set // up different date/time format based on the time range. // // If all ticks are yearly aligned, then we use "yyyy" as the label format. c.xAxis().setFormatCondition("align", 360 * 86400); c.xAxis().setLabelFormat("{value|yyyy}"); // If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first label of a // year, and "mmm" for other labels. c.xAxis().setFormatCondition("align", 30 * 86400); c.xAxis().setMultiFormat(Chart.StartOfYearFilter(), "<*font=bold*>{value|mmm yyyy}", Chart.AllPassFilter(), "{value|mmm}"); // If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the first // label of a year, and "mmm dd" in bold font as the first label of a month, and "dd" for other // labels. c.xAxis().setFormatCondition("align", 86400); c.xAxis().setMultiFormat(Chart.StartOfYearFilter(), "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", Chart.StartOfMonthFilter(), "<*font=bold*>{value|mmm dd}"); c.xAxis().setMultiFormat2(Chart.AllPassFilter(), "{value|dd}"); // For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of a day, // and "hh:nn" for other labels. c.xAxis().setFormatCondition("else"); c.xAxis().setMultiFormat(Chart.StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}", Chart.AllPassFilter(), "{value|hh:nn}"); return c; } // // 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; } // // The scroll bar event handler // private void hScrollBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { var viewer = WPFChartViewer1; // When the view port is changed (user drags on the chart to scroll), the scroll bar will get // updated. When the scroll bar changes (eg. user drags on the scroll bar), the view port will // get updated. This creates an infinite loop. To avoid this, the scroll bar can update the // view port only if the view port is not updating the scroll bar. if (!viewer.IsInViewPortChangedEvent) { // Set the view port based on the scroll bar viewer.ViewPortLeft = (hScrollBar1.Value - hScrollBar1.Minimum) / (hScrollBar1.Maximum - hScrollBar1.Minimum) * (1 - viewer.ViewPortWidth); // Trigger a view port changed event to update the chart viewer.updateViewPort(true, false); } } // // Draw track cursor when mouse is moving over plotarea // private void WPFChartViewer1_MouseMovePlotArea(object sender, MouseEventArgs e) { WPFChartViewer viewer = (WPFChartViewer)sender; trackLineLabel((XYChart)viewer.Chart, viewer.PlotAreaMouseX); viewer.updateDisplay(); // Hide the track cursor when the mouse leaves the plot area viewer.removeDynamicLayer("MouseLeavePlotArea"); } // // Draw track line with data labels // private void trackLineLabel(XYChart c, int mouseX) { // 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, and find its pixel coordinate. double xValue = c.getNearestXValue(mouseX); int xCoor = c.getXCoor(xValue); // Draw a vertical track line at the x-position d.vline(plotArea.getTopY(), plotArea.getBottomY(), xCoor, d.dashLineColor(0x000000, 0x0101)); // Draw a label on the x-axis to show the track line position. string xlabel = "<*font,bgColor=000000*> " + c.xAxis().getFormattedLabel(xValue, "mmm dd, yyyy") + " <*/font*>"; TTFText t = d.text(xlabel, "Arial Bold", 8); // Restrict the x-pixel position of the label to make sure it stays inside the chart image. int xLabelPos = Math.Max(0, Math.Min(xCoor - t.getWidth() / 2, c.getWidth() - t.getWidth())); t.draw(xLabelPos, plotArea.getBottomY() + 6, 0xffffff); // Iterate through all layers to draw the data labels 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); // Get the color and position of the data label int color = dataSet.getDataColor(); int yCoor = c.getYCoor(dataSet.getPosition(xIndex), dataSet.getUseYAxis()); // Draw a track dot with a label next to it for visible data points in the plot area if ((yCoor >= plotArea.getTopY()) && (yCoor <= plotArea.getBottomY()) && (color != Chart.Transparent) && (!string.IsNullOrEmpty(dataSet.getDataName()))) { d.circle(xCoor, yCoor, 4, 4, color, color); string label = "<*font,bgColor=" + color.ToString("x") + "*> " + c.formatValue( dataSet.getValue(xIndex), "{value|P4}") + " <*/font*>"; t = d.text(label, "Arial Bold", 8); // Draw the label on the right side of the dot if the mouse is on the left side the // chart, and vice versa. This ensures the label will not go outside the chart image. if (xCoor <= (plotArea.getLeftX() + plotArea.getRightX()) / 2) { t.draw(xCoor + 5, yCoor, 0xffffff, Chart.Left); } else { t.draw(xCoor - 5, yCoor, 0xffffff, Chart.Right); } } } } } // // 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); } // // Save button event handler // private void pdfReportPB_Click(object sender, RoutedEventArgs e) { // The standard Save File dialog SaveFileDialog fileDlg = new SaveFileDialog(); fileDlg.Filter = "PDF (*.pdf)|*.pdf"; fileDlg.FileName = "chartdirector_report"; var ret = fileDlg.ShowDialog(this); if (!(ret.HasValue && ret.Value)) return; createPdfReport(fileDlg.FileName); } // // Create a multi-page PDF Report // private void createPdfReport(string filename) { // The MultiPagePDF object can create PDF from multiple pages, each with one chart // object. Since a chart object can contain text (eg. using BaseChart.addText) and // other charts (eg. using MultiChart), that means each page can contain text and // multiple charts. MultiPagePDF doc = new MultiPagePDF(); // Page configuration - A4 = 210 x 297mm. The PDF default is 96 dpi (dot per inch), // so the A4 size is equal to 794 x 1123 dots. string pageConfig = "pagewidth = 794; pageHeight = 1123"; // In this example, we include a cover page with only text. This is by creating an // empty pie chart with text only. PieChart firstPage = new PieChart(720, 960); firstPage.addText(360, 320, "<*size=50*>ChartDirector<*br*><*size=30*>PDF Report Demonstration<*/*>", "Arial Bold", 30, 0x000000, Chart.Center); firstPage.setOutputOptions(pageConfig); doc.addPage(firstPage); // We include 2 charts per page, with each chart showing one year of data. Each page // will also have a header and page number int startYear = timeStamps[0].Year; int endYear = timeStamps[timeStamps.Length - 1].AddSeconds(-1).Year; int pageNumber = 0; for (int yyyy = startYear; yyyy <= endYear; yyyy += 2) { // This chart is the page. MultiChart m = new MultiChart(760, 920); // Use addTitle to add a header m.addTitle("ChartDirector PDF Report Demonstration", "Arial Bold", 20); // Create the first chart XYChart c = drawXYChart(new DateTime(yyyy, 1, 1), new DateTime(yyyy + 1, 1, 1)); m.addChart((m.getWidth() - c.getWidth()) / 2, 100, c); c.addTitle("Year " + yyyy); XYChart c2 = null; if (yyyy < endYear) { // Create the second chart c2 = drawXYChart(new DateTime(yyyy + 1, 1, 1), new DateTime(yyyy + 2, 1, 1)); c2.addTitle("Year " + (yyyy + 1)); m.addChart((m.getWidth() - c2.getWidth()) / 2, 500, c2); } // Add the page number ++pageNumber; m.addTitle(Chart.BottomCenter, "" + pageNumber, "Arial Bold", 8); m.setOutputOptions(pageConfig); doc.addPage(m); } // Output the PDF report doc.outPDF(filename); } } }