{"id":1696,"date":"2012-04-21T11:42:54","date_gmt":"2012-04-21T10:42:54","guid":{"rendered":"https:\/\/www.scichart.com\/2012\/04\/21\/extending-scichart-with-text-annotations-and-screenshots\/"},"modified":"2022-12-09T13:09:05","modified_gmt":"2022-12-09T13:09:05","slug":"extending-scichart-with-text-annotations-and-screenshots","status":"publish","type":"post","link":"https:\/\/www.scichart.com\/extending-scichart-with-text-annotations-and-screenshots\/","title":{"rendered":"Extending SciChart with Text Annotations and Screenshots"},"content":{"rendered":"
This article hails from the days of v1.3.x where Annotations were not native to SciChart. We’re keeping it for posterities sake, however, SciChart now has a much improved Annotations API in v1.5 of SciChart<\/a> and Screenshots \/ Printing API<\/a>. So do take this article as a bit of history and use the new Annotations API instead!<\/p><\/blockquote>\n
One of the more hotly requested features of SciChart is the ability to add custom Annotations. While this feature is currently in Beta and the API will be improved for subsequent versions of SciChart, this is already possible with the current release. This article shows you how to do just that with the current released version of SciChart (v1.2.1).<\/p>\n
SciChart ships with a ChartModifier API, which allows you, the end-user to extend interactivity of the chart as you see fit. The ChartModifier API allows you to subscribe to mouse events, render update events, overlay UIElements on the chart, interact with chart Axes and convert data values to pixel coordinates and back. With these building blocks you can do almost anything that WPF and Silverlight allow.<\/p>\n
The Use-Case<\/h3>\n
User X wants to analyse some data. They want to display a large dataset (>10k points) and interact with the chart by pointing and clicking. In particular they want to highlight areas of interest with text\/lines as if the chart were being edited in a paint program. Finally, they want to export the annotated chart to an image file to include in reports.<\/p>\n
Ok you say, so where shall we start? We start by creating a chart with some data to render.<\/p>\n
Xaml<\/h4>\n
<SciChart:SciChartSurface x:Name=\"sciChart\" Margin=\"10\">\r\n\r\n <SciChart:SciChartSurface.RenderableSeries>\r\n <SciChart:FastLineRenderableSeries SeriesColor=\"SteelBlue\" ResamplingMode=\"None\"\/>\r\n <\/SciChart:SciChartSurface.RenderableSeries>\r\n\r\n <SciChart:SciChartSurface.XAxis>\r\n <SciChart:NumericAxis TextFormatting=\"0.000E+0\"\/>\r\n <\/SciChart:SciChartSurface.XAxis>\r\n\r\n <SciChart:SciChartSurface.YAxis>\r\n <SciChart:NumericAxis\/>\r\n <\/SciChart:SciChartSurface.YAxis>\r\n\r\n <SciChart:SciChartSurface.ChartModifier>\r\n <SciChart:ModifierGroup>\r\n <SciChart:RubberBandXyZoomModifier x:Name=\"zoomModifier\" IsXAxisOnly=\"True\" IsEnabled=\"True\"\/>\r\n <SciChart:ZoomPanModifier x:Name=\"panModifier\" IsEnabled=\"False\"\/>\r\n <SciChart:XAxisDragModifier\/>\r\n <SciChart:YAxisDragModifier\/>\r\n <\/SciChart:ModifierGroup>\r\n <\/SciChart:SciChartSurface.ChartModifier>\r\n<\/SciChart:SciChartSurface>\r\n<\/pre>\nCode<\/h4>\n
private void PopulateChart()\r\n{\r\n\/\/ This example assumes our data has been populated in arrays\r\ndouble[] yValues;\r\ndouble[] xValues;\r\n\r\n\/\/ Suspend drawing\r\nusing (var updateSuspender = sciChart.SuspendUpdates())\r\n{\r\n\/\/ Create dataset\r\nvar dataSeriesSet = new DataSeriesSet();\r\nsciChart.DataSet = dataSeriesSet;\r\n\r\n\/\/ Add series\r\nvar tdSeries = dataSeriesSet.AddSeries();\r\ntdSeries.SeriesName = "Time Domain";\r\n\r\n\/\/ Append data\r\ntdSeries.Append(xValues, yValues);\r\n\r\n\/\/ On every zoom extents, the Y Range will expand by 10% below and 10% above the data range\r\nsciChart.YAxis.GrowBy = new DoubleRange(0.1, 0.1);\r\n\r\n\/\/ Zoom extents\r\nsciChart.ZoomExtents();\r\n}\r\n\/\/ redraws on exit of this block (disposal of ISuspendable type)\r\n}\r\n<\/pre>\nThe above code creates a SciChartSurface instance with Numeric X and Y Axes. It creates a single line series and adds a dataset with single DataSeries. The DataSeries is populated with double values. Notice that the SciChartSurface.ChartModifier property is filled with a ModifierGroup containing multiple chart modifiers. These provide our zooming, panning and axis dragging behavior.<\/p>\n
<!-- ... -->\r\n <SciChart:SciChartSurface.ChartModifier>\r\n <SciChart:ModifierGroup>\r\n <WpfApplication1:TextAnnotationModifier x:Name=\"annotationModifier\"\/>\r\n <SciChart:RubberBandXyZoomModifier x:Name=\"zoomModifier\" IsXAxisOnly=\"True\" IsEnabled=\"True\"\/>\r\n <SciChart:ZoomPanModifier x:Name=\"panModifier\" IsEnabled=\"False\"\/>\r\n <SciChart:XAxisDragModifier\/>\r\n <SciChart:YAxisDragModifier\/>\r\n <\/SciChart:ModifierGroup>\r\n <\/SciChart:SciChartSurface.ChartModifier>\r\n<\/pre>\nWhat we will do in the next section is create a custom modifier to add our annotations.<\/p>\n
Creating the custom AnnotationModifier<\/h3>\n
First of all, we need to create a base class for our annotations. This will be AnnotationControl and simply inherits FrameworkElement. Later we will override OnRender to perform the drawing, but for now leave this class as-is. AnnotationControl stores the X,Y data values (not mouse coordinates) where we wish to draw our annotation. These will be used to update the annotation position as the chart is zoomed, panned or otherwise redrawn.<\/p>\n
public class AnnotationControl : FrameworkElement\r\n{\r\npublic double XData1 { get; set; }\r\npublic double XData2 { get; set; }\r\npublic double YData1 { get; set; }\r\npublic double YData2 { get; set; }\r\npublic string Text { get; set; }\r\n}\r\n<\/pre>\nNext we need to create a custom chart modifier to draw our annotations. This will be called AnnotationsModifier and extends ChartModifierBase.<\/p>\n
public class AnnotationsModifier : ChartModifierBase\r\n{\r\nprivate readonly IList _annotations = new List();\r\n\r\n\/\/ Occurs when the modifier is attached to the parent surface\r\npublic override void OnAttached()\r\n{\r\nbase.OnAttached();\r\nServices.GetService().Subscribe(OnScichartSurfaceRendered);\r\n}\r\n\r\npublic override void OnModifierDoubleClick(ModifierMouseArgs e)\r\n{\r\nbase.OnModifierDoubleClick(e);\r\nAddAnnotation(e.MousePoint);\r\n}\r\n\r\n\/\/ Called each time the SciChartSurface is rendered\r\nprivate void OnScichartSurfaceRendered(SciChartRenderedMessage obj)\r\n{\r\nUpdateAnnotations();\r\n}\r\n\r\nprivate void UpdateAnnotations()\r\n{\r\nforeach(var annotation in _annotations)\r\n{\r\ndouble xCoord1 = XAxis.GetCoordinate(annotation.XData1);\r\ndouble yCoord1 = YAxis.GetCoordinate(annotation.YData1);\r\n\r\nCanvas.SetLeft(annotation, xCoord1);\r\nCanvas.SetTop(annotation, yCoord1);\r\n\r\nannotation.InvalidateVisual();\r\n}\r\n}\r\n\r\nprivate void AddAnnotation(Point mousePoint)\r\n{\r\nvar annotation = new AnnotationControl();\r\n\r\nannotation.XData1 = (double)XAxis.HitTest(mousePoint).DataValue;\r\nannotation.YData1 = (double)YAxis.HitTest(mousePoint).DataValue;\r\n\r\nannotation.Text = "Hello World!";\r\n\r\n_annotations.Add(annotation);\r\nModifierSurface.Children.Add(annotation);\r\nUpdateAnnotations();\r\n}\r\n}\r\n<\/pre>\n