Index: Core/Common/src/Core.Common.Controls.Charting/Chart.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Chart.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Chart.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,443 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Series; +using Core.Common.Controls.Charting.Properties; +using Core.Common.Utils.Events; + +using log4net; +using Steema.TeeChart.Drawing; +using Steema.TeeChart.Export; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting +{ + /// + /// Dataobject for chartcontrol containing set of series. + /// Facade for TeeChart Chart. + /// + public class Chart : IChart + { + internal readonly Steema.TeeChart.Chart chart; + private readonly ILog log = LogManager.GetLogger(typeof(Chart)); + + private readonly List seriesList; + private bool chartSeriesStacked; + private ChartGraphics graphics; + + public Chart() + { + chart = new Steema.TeeChart.Chart(); + seriesList = new List(); + + SetDefaultValues(); + } + + public bool TitleVisible + { + get + { + return chart.Header.Visible; + } + set + { + chart.Header.Visible = value; + } + } + + public string Title + { + get + { + return chart.Header.Text; + } + set + { + chart.Header.Text = value; + } + } + + public Font Font + { + get + { + return chart.Header.Font.DrawingFont; + } + set + { + chart.Header.Font.Bold = value.Bold; + chart.Header.Font.Italic = value.Italic; + chart.Header.Font.Name = value.Name; + chart.Header.Font.SizeFloat = value.SizeInPoints; + chart.Header.Font.Strikeout = value.Strikeout; + chart.Header.Font.Underline = value.Underline; + } + } + + public Color BackGroundColor + { + get + { + return chart.Walls.Back.Brush.Color; + } + set + { + chart.Walls.Back.Brush = new ChartBrush(chart, value); + } + } + + public Color SurroundingBackGroundColor + { + get + { + return chart.Panel.Brush.Color; + } + set + { + chart.Panel.Brush.Color = value; + } + } + + public bool StackSeries + { + get + { + return chartSeriesStacked; + } + set + { + chartSeriesStacked = value; + var stackType = (chartSeriesStacked) + ? CustomStack.Stack + : CustomStack.None; + + foreach (var serie in chart.Series.OfType()) + { + serie.Stacked = stackType; + } + } + } + + public IEnumerable Series + { + get + { + return seriesList.AsReadOnly(); + } + } + + public IChartAxis LeftAxis + { + get + { + return new ChartAxis(chart.Axes.Left); + } + } + + public IChartAxis RightAxis + { + get + { + return new ChartAxis(chart.Axes.Right); + } + } + + public IChartAxis BottomAxis + { + get + { + return new ChartAxis(chart.Axes.Bottom); + } + } + + public Rectangle ChartBounds + { + get + { + return chart.ChartRect; + } + } + + public IChartLegend Legend + { + get + { + return new ChartLegend(chart.Legend); + } + } + + public ChartGraphics Graphics + { + get + { + return graphics ?? (graphics = new ChartGraphics(chart)); + } + } + + public Control ParentControl + { + get + { + return (Control) chart.Parent; + } + } + + public bool CancelMouseEvents + { + set + { + chart.CancelMouse = value; + } + } + + public bool AllowSeriesTypeChange { get; set; } + + public int GetIndexOfChartSeries(ChartSeries series) + { + return seriesList.IndexOf(series); + } + + public void AddChartSeries(ChartSeries series) + { + if (!seriesList.Contains(series)) + { + series.Chart = this; + seriesList.Add(series); + chart.Series.Add(series.series); + } + } + + public void InsertChartSeries(ChartSeries series, int index) + { + if (seriesList.Contains(series)) + { + chart.Series.MoveTo(series.series, index); + } + else + { + series.Chart = this; + seriesList.Insert(index, series); + chart.Series.Add(series.series); + chart.Series.MoveTo(series.series, index); + } + } + + public bool RemoveChartSeries(ChartSeries series) + { + if (seriesList.Remove(series)) + { + chart.Series.Remove(series.series); + return true; + } + return false; + } + + public void RemoveAllChartSeries() + { + foreach (var series in seriesList.ToArray()) + { + RemoveChartSeries(series); + } + } + + public void ExportAsImage(IWin32Window owner) + { + var dialog = new SaveFileDialog + { + Filter = GetSupportedFormatsFilter(), + FilterIndex = 2, + }; + + var dialogResult = dialog.ShowDialog(owner); + if (dialogResult == DialogResult.OK) + { + ExportAsImage(dialog.FileName, null, null); + } + } + + public void ExportAsImage(string filename, int? width, int? height) + { + if (string.IsNullOrEmpty(filename)) + { + throw new ArgumentException(Resources.Chart_ExportAsImage_Argument_should_not_be_null, "filename"); + } + + var dir = Path.GetDirectoryName(filename); + var filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); + var ext = Path.GetExtension(filename); + + if (string.IsNullOrEmpty(ext)) + { + throw new ArgumentException(Resources.Chart_ExportAsImage_Argument_should_have_an_extension, "filename"); + } + + if (string.IsNullOrEmpty(filenameWithoutExtension)) + { + throw new ArgumentException(Resources.Chart_ExportAsImage_Argument_did_not_contain_a_filename, "filename"); + } + + if (ext == ".svg") + { + var hatchStyleIgnored = seriesList.OfType().Any(cs => cs.UseHatch) || + seriesList.OfType().Any(cs => cs.UseHatch); + if (hatchStyleIgnored) + { + log.WarnFormat(Resources.Chart_ExportAsImage_Hatch_style_is_not_supported_for_exports_and_will_be_ignored_); + } + + chart.Export.Image.SVG.Save(filename); + return; + } + + var filenameToExport = Path.Combine(dir, filenameWithoutExtension) + ext; + + var oldColor = SurroundingBackGroundColor; + SurroundingBackGroundColor = Color.FromArgb(255, Color.White); + + // use our own bitmap / graphics stuff because TeeChart's contains a leak + var realWidth = width.HasValue ? width.Value : chart.Width == 0 ? 400 : chart.Width; + var realHeight = height.HasValue ? height.Value : chart.Height == 0 ? 300 : chart.Height; + + using (var bitmap = new Bitmap(realWidth, realHeight)) + { + using (var g = System.Drawing.Graphics.FromImage(bitmap)) + { + var oldWidth = chart.Width; + var oldHeight = chart.Height; + try + { + chart.Width = realWidth; + chart.Height = realHeight; + chart.Draw(g); + } + finally + { + chart.Width = oldWidth; + chart.Height = oldHeight; + } + bitmap.Save(filenameToExport, GetImageFormatByExtension(ext)); + } + } + SurroundingBackGroundColor = oldColor; + } + + public Bitmap Bitmap() + { + return chart.Bitmap(); + } + + /// + /// Returns a filter string with the supported export file types to be used in a dialog + /// + /// + private static string GetSupportedFormatsFilter() + { + return "Bitmap (*.bmp)|*.bmp|JPG (*.jpg)|*.jpg|PNG (*.png)|*.png|GIF (*.gif)|*.gif|Tiff (*.tiff)|*.tiff|Scalable Vector Graphics (*.svg)|*.svg"; + } + + private ImageFormat GetImageFormatByExtension(string ext) + { + switch (ext) + { + case ".jpg": + case ".jpeg": + return ImageFormat.Jpeg; + case ".gif": + return ImageFormat.Gif; + case ".png": + return ImageFormat.Png; + case ".tiff": + return ImageFormat.Tiff; + case ".bmp": + return ImageFormat.Bmp; + default: + throw new ArgumentException(string.Format(Resources.Chart_GetImageFormatByExtension_Extension_0_not_supported, ext), "filename"); + } + } + + private ImageExportFormat GetImageExportFormatByExtension(string ext) + { + ImageExportFormat format; + switch (ext) + { + case ".pdf": + format = chart.Export.Image.PDF; + break; + case ".jpg": + case ".jpeg": + format = chart.Export.Image.JPEG; + break; + case ".gif": + format = chart.Export.Image.GIF; + break; + case ".png": + format = chart.Export.Image.PNG; + break; + case ".tiff": + format = chart.Export.Image.TIFF; + break; + case ".bmp": + format = chart.Export.Image.Bitmap; + break; + case ".eps": + format = chart.Export.Image.EPS; + break; + default: + throw new ArgumentException(string.Format(Resources.Chart_GetImageFormatByExtension_Extension_0_not_supported, ext), "filename"); + } + return format; + } + + private void SetDefaultValues() + { + chart.Aspect.View3D = false; + chart.Aspect.ZOffset = 0; + chart.Axes.Bottom.MaximumOffset = 2; + chart.Axes.Bottom.MinimumOffset = 1; + chart.Axes.Left.MaximumOffset = 1; + chart.Axes.Left.MinimumOffset = 2; + chart.Header.Visible = false; + chart.Legend.Visible = false; + chart.Panel.Brush.Color = Color.FromArgb(0, 212, 208, 200); + chart.Panel.Brush.Gradient.Visible = false; + chart.Zoom.Animated = true; + + AllowSeriesTypeChange = true; + } + + private void SeriesCollectionChanged(object sender, NotifyCollectionChangeEventArgs e) + { + var chartSeries = e.Item as ChartSeries; + if (chartSeries == null) + { + return; + } + + switch (e.Action) + { + case NotifyCollectionChangeAction.Replace: + throw new NotImplementedException(); + + case NotifyCollectionChangeAction.Add: + chartSeries.Chart = this; + if (e.Index != -1) + { + chart.Series.Add(chartSeries.series); + chart.Series.MoveTo(chartSeries.series, e.Index); + } + else + { + chart.Series.Add(chartSeries.series); + } + break; + case NotifyCollectionChangeAction.Remove: + chart.Series.Remove(chartSeries.series); + break; + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartAxis.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartAxis.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartAxis.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,191 @@ +using System.Drawing; +using Steema.TeeChart; + +namespace Core.Common.Controls.Charting +{ + public class ChartAxis : IChartAxis + { + private readonly Axis teeChartAxis; + + public ChartAxis(Axis axis) + { + teeChartAxis = axis; + } + + public string LabelsFormat + { + get + { + return teeChartAxis.Labels.ValueFormat; + } + set + { + teeChartAxis.Labels.ValueFormat = value; + } + } + + public bool Labels + { + get + { + return teeChartAxis.Labels.Visible; + } + set + { + teeChartAxis.Labels.Visible = value; + } + } + + public bool Visible + { + get + { + return teeChartAxis.Visible; + } + set + { + teeChartAxis.Visible = value; + } + } + + public bool Automatic + { + get + { + return teeChartAxis.Automatic; + } + set + { + teeChartAxis.Automatic = value; + } + } + + public double Minimum + { + get + { + // teechart tends to update the min and max of an axis when needed but this is to late + // if we want to respond to viewport changed events. + teeChartAxis.AdjustMaxMin(); + return teeChartAxis.Minimum; + } + set + { + teeChartAxis.Minimum = value; + } + } + + public double Maximum + { + get + { + teeChartAxis.AdjustMaxMin(); + return teeChartAxis.Maximum; + } + set + { + teeChartAxis.Maximum = value; + } + } + + public int MinimumOffset + { + get + { + return teeChartAxis.MinimumOffset; + } + set + { + teeChartAxis.MinimumOffset = value; + } + } + + public int MaximumOffset + { + get + { + return teeChartAxis.MaximumOffset; + } + set + { + teeChartAxis.MaximumOffset = value; + } + } + + public string Title + { + get + { + return teeChartAxis.Title.Caption; + } + set + { + teeChartAxis.Title.Caption = value; + } + } + + public Font TitleFont + { + get + { + return teeChartAxis.Title.Font.DrawingFont; + } + set + { + teeChartAxis.Title.Font.Bold = value.Bold; + teeChartAxis.Title.Font.Italic = value.Italic; + teeChartAxis.Title.Font.Name = value.Name; + teeChartAxis.Title.Font.SizeFloat = value.SizeInPoints; + teeChartAxis.Title.Font.Strikeout = value.Strikeout; + teeChartAxis.Title.Font.Underline = value.Underline; + } + } + + public Font LabelsFont + { + get + { + return teeChartAxis.Labels.Font.DrawingFont; + } + set + { + teeChartAxis.Labels.Font.Bold = value.Bold; + teeChartAxis.Labels.Font.Italic = value.Italic; + teeChartAxis.Labels.Font.Name = value.Name; + teeChartAxis.Labels.Font.SizeFloat = value.SizeInPoints; + teeChartAxis.Labels.Font.Strikeout = value.Strikeout; + teeChartAxis.Labels.Font.Underline = value.Underline; + } + } + + public bool Logaritmic + { + get + { + return teeChartAxis.Logarithmic; + } + set + { + teeChartAxis.Logarithmic = value; + } + } + + public bool IsDateTime + { + get + { + return teeChartAxis.IsDateTime; + } + } + + public double CalcPosPoint(int position) + { + return teeChartAxis.CalcPosPoint(position); + } + + public int CalcPosValue(double value) + { + return teeChartAxis.CalcPosValue(value); + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartCoordinateService.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartCoordinateService.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartCoordinateService.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,120 @@ +using System; +using Core.GIS.GeoAPI.Geometries; +using Core.GIS.NetTopologySuite.Geometries; + +namespace Core.Common.Controls.Charting +{ + public class ChartCoordinateService + { + public ChartCoordinateService(IChart chart) + { + Chart = chart; + } + + public static int ToDeviceX(IChart chart, double x) + { + return HorizontalReferenceAxis(chart).CalcPosValue(x); + } + + public static int ToDeviceY(IChart chart, double y) + { + return VerticalReferenceAxis(chart).CalcPosValue(y); + } + + public static int ToDeviceWidth(IChart chart, double x) + { + var xAxis = HorizontalReferenceAxis(chart); + return Math.Abs(xAxis.CalcPosValue(x) - xAxis.CalcPosValue(0)); + } + + public static int ToDeviceHeight(IChart chart, double y) + { + var yAxis = VerticalReferenceAxis(chart); + return Math.Abs(yAxis.CalcPosValue(y) - yAxis.CalcPosValue(0)); + } + + public static double ToWorldX(IChart chart, int x) + { + return HorizontalReferenceAxis(chart).CalcPosPoint(x); + } + + public static double ToWorldY(IChart chart, int y) + { + return VerticalReferenceAxis(chart).CalcPosPoint(y); + } + + public static double ToWorldWidth(IChart chart, int x) + { + var xAxis = HorizontalReferenceAxis(chart); + return Math.Abs(xAxis.CalcPosPoint(x) - xAxis.CalcPosPoint(0)); + } + + public static double ToWorldHeight(IChart chart, int y) + { + var yAxis = VerticalReferenceAxis(chart); + return Math.Abs(yAxis.CalcPosPoint(y) - yAxis.CalcPosPoint(0)); + } + + public static ICoordinate ToCoordinate(IChart chart, int x, int y) + { + return new Coordinate(ToWorldX(chart, x), ToWorldY(chart, y)); + } + + public int ToDeviceX(double x) + { + return ToDeviceX(Chart, x); + } + + public int ToDeviceY(double y) + { + return ToDeviceY(Chart, y); + } + + public int ToDeviceWidth(double x) + { + return ToDeviceWidth(Chart, x); + } + + public int ToDeviceHeight(double y) + { + return ToDeviceHeight(Chart, y); + } + + public double ToWorldX(int x) + { + return ToWorldX(Chart, x); + } + + public double ToWorldY(int y) + { + return ToWorldY(Chart, y); + } + + public double ToWorldWidth(int x) + { + return ToWorldWidth(Chart, x); + } + + public double ToWorldHeight(int y) + { + return ToWorldHeight(Chart, y); + } + + public ICoordinate ToCoordinate(int x, int y) + { + return ToCoordinate(Chart, x, y); + } + + private IChart Chart { get; set; } + + private static IChartAxis VerticalReferenceAxis(IChart chart) + { + return chart.LeftAxis; + } + + private static IChartAxis HorizontalReferenceAxis(IChart chart) + { + return chart.BottomAxis; + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartGraphics.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartGraphics.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartGraphics.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,198 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Steema.TeeChart.Drawing; + +namespace Core.Common.Controls.Charting +{ + public class ChartGraphics + { + private readonly Steema.TeeChart.Chart chart; + + public ChartGraphics(Steema.TeeChart.Chart chart) + { + this.chart = chart; + Graphics3D = chart.Graphics3D; // Needed since TeeChart returns a new graphics object each time the get is called!! + } + + /// + /// Background color used when drawing + /// + public Color BackColor + { + get + { + return Graphics3D.BackColor; + } + set + { + Graphics3D.BackColor = value; + } + } + + /// + /// Pen color used when drawing + /// + public Color PenColor + { + get + { + return Graphics3D.Pen.Color; + } + set + { + Graphics3D.Pen.Color = value; + } + } + + /// + /// Pen width used when drawing + /// + public int PenWidth + { + get + { + return Graphics3D.Pen.Width; + } + set + { + Graphics3D.Pen.Width = value; + } + } + + /// + /// Dash style of the pen used when drawing + /// + public DashStyle PenStyle + { + get + { + return Graphics3D.Pen.Style; + } + set + { + Graphics3D.Pen.Style = value; + } + } + + /// + /// Font to use when drawing text + /// + public Font Font + { + get + { + return Graphics3D.Font.DrawingFont; + } + set + { + Graphics3D.Font.DrawingFont = value; + } + } + + /// + /// Draws an ellipse within the supplied region + /// + /// Region to draw the ellipse in + public void Ellipse(Rectangle rectangle) + { + DrawWithPenEnabled(() => Graphics3D.Ellipse(rectangle)); + } + + /// + /// Draws a rectangle within the supplied region + /// + /// Region to draw the rectangle in + public void Rectangle(Rectangle rectangle) + { + DrawWithPenEnabled(() => Graphics3D.Rectangle(rectangle)); + } + + /// + /// Draws an image within the supplied region + /// + /// Region to draw the image in + /// Image to draw + /// Use transparent color + public void Draw(Rectangle rectangle, Image image, bool transparent) + { + DrawWithPenEnabled(() => Graphics3D.Draw(rectangle, image, transparent)); + } + + /// + /// Sets the position of the pen to provided x and y location (Used in ) + /// + /// x position + /// y position + public void MoveTo(int x, int y) + { + Graphics3D.MoveTo(x, y); + } + + /// + /// Draws a line to from the pen position (set by ) to the provided x and y location + /// + /// x position + /// y position + public void LineTo(int x, int y) + { + Graphics3D.LineTo(x, y); + } + + /// + /// Draws a graphics path according to the supplied + /// + /// Pen to use for drawing + /// Graphics path to follow + public void DrawPath(Pen pen, GraphicsPath graphicsPath) + { + DrawWithPenEnabled(() => Graphics3D.DrawPath(pen, graphicsPath)); + } + + /// + /// Calculates the size needed to draw the sting + /// + /// String to calculate for + public SizeF MeasureString(string label) + { + return Graphics3D.MeasureString(Graphics3D.Font, label); + } + + /// + /// Draws the provided at the provided location + /// + /// X position + /// Y position + /// Text to draw + public void TextOut(int xpos, int ypos, string label) + { + Graphics3D.TextOut(xpos, ypos, label); + } + + /// + /// Draws a polygon using the provided + /// + /// Points that make up the polygon + public void Polygon(Point[] points) + { + DrawWithPenEnabled(() => Graphics3D.Polygon(points)); + } + + /// + /// Graphics object used for drawing. + /// + private Graphics3D Graphics3D { get; set; } + + private void DrawWithPenEnabled(Action drawAction) + { + var originalPenVisible = Graphics3D.Pen.Visible; + Graphics3D.Pen.Visible = true; + + Graphics3D.ClipRectangle(chart.ChartBounds); + drawAction(); + Graphics3D.UnClip(); + + Graphics3D.Pen.Visible = originalPenVisible; + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartLegend.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartLegend.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartLegend.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,127 @@ +using System; +using System.Drawing; +using Steema.TeeChart; + +namespace Core.Common.Controls.Charting +{ + /// + /// A TeeChart Legend wrapper class + /// + public class ChartLegend : IChartLegend + { + private readonly Legend legend; + + /// + /// Creates a TeeChart Legend wrapper class + /// + /// + public ChartLegend(Legend legend) + { + this.legend = legend; + legend.LegendStyle = LegendStyles.Series; + } + + public bool Visible + { + get + { + return legend.Visible; + } + set + { + legend.Visible = value; + } + } + + public LegendAlignment Alignment + { + get + { + string enumName = Enum.GetName(typeof(LegendAlignments), legend.Alignment); + return (LegendAlignment) Enum.Parse(typeof(LegendAlignment), enumName); + } + set + { + string enumName = Enum.GetName(typeof(LegendAlignment), value); + legend.Alignment = (LegendAlignments) Enum.Parse(typeof(LegendAlignments), enumName); + } + } + + /// + /// Enables checkboxes in the legend + /// + public bool ShowCheckBoxes + { + get + { + return legend.CheckBoxes; + } + set + { + legend.CheckBoxes = value; + } + } + + public Font Font + { + get + { + return legend.Font.DrawingFont; + } + set + { + legend.Font.Bold = value.Bold; + legend.Font.Italic = value.Italic; + legend.Font.Name = value.Name; + legend.Font.SizeFloat = value.SizeInPoints; + legend.Font.Strikeout = value.Strikeout; + legend.Font.Underline = value.Underline; + } + } + + /// + /// Maximum width of the legend + /// + public int Width + { + get + { + return legend.Width; + } + set + { + legend.Width = value; + } + } + + /// + /// Distance between the upper left corner of the legend and the top of the axes canvas (in pixels) + /// + public int Top + { + get + { + return legend.Top; + } + set + { + legend.Top = value; + } + } + + /// + /// Distance between the upper left corner of the legend and the left side of the axes canvas (in pixels) + /// + public int Left + { + get + { + return legend.Left; + } + set + { + legend.Left = value; + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartStyleHelper.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartStyleHelper.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartStyleHelper.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,188 @@ +using System.Collections.Generic; +using System.Linq; +using Core.Common.Controls.Charting.Series; + +namespace Core.Common.Controls.Charting +{ + public static class ChartStyleHelper + { + /// + /// Copies chart, axis and series styles from a chart to a chart. + /// To copy styles from series this method uses the Tag property of the ChartSeries to identify series to copy a style to. + /// + /// Source chart + /// Target chart + public static void CopyStyle(IChart source, IChart target) + { + // First copy chart styles + CopyChartStyle(source, target); + + // Copy axis styles + CopyAxisStyle(source.LeftAxis, target.LeftAxis); + CopyAxisStyle(source.BottomAxis, target.BottomAxis); + + // Copy series styles + CopySeriesStyles(source.Series, target.Series); + } + + /// + /// Copies only the chart related styles from to . + /// + /// Source chart + /// Target chart + public static void CopyChartStyle(IChart source, IChart target) + { + target.Legend.Visible = source.Legend.Visible; + target.Legend.Alignment = source.Legend.Alignment; + target.Legend.Font = source.Legend.Font; + target.TitleVisible = source.TitleVisible; + target.Title = source.Title; + target.Font = source.Font; + target.StackSeries = source.StackSeries; + target.SurroundingBackGroundColor = source.SurroundingBackGroundColor; + target.BackGroundColor = source.BackGroundColor; + target.AllowSeriesTypeChange = source.AllowSeriesTypeChange; + } + + /// + /// Copies the style of a axis to a axis. This includes legend style, label style and axis scaling. + /// + /// Source chart axis + /// Target chart axis + public static void CopyAxisStyle(IChartAxis source, IChartAxis target) + { + target.Labels = source.Labels; + target.Automatic = source.Automatic; + target.Maximum = source.Maximum; + target.Minimum = source.Minimum; + target.Title = source.Title; + target.Visible = source.Visible; + target.LabelsFont = source.LabelsFont; + target.TitleFont = source.TitleFont; + target.LabelsFormat = source.LabelsFormat; + target.Logaritmic = source.Logaritmic; + } + + /// + /// Copies style of all chart series in the collection to styles in the collection. Tag is used to match series (series in the target collection with a tag that is also in one of ther series of the source collection get the style of the first item witht the same Tag in the source collection). + /// + public static void CopySeriesStyles(IEnumerable source, IEnumerable target) + { + foreach (var sourceSeries in source) + { + var series = sourceSeries; + foreach (var targetSeries in target.Where(s => series.Tag == s.Tag)) + { + CopyStyle(series, targetSeries); + + var polygonChartSeries = series as IPolygonChartSeries; + if (polygonChartSeries != null) + { + CopyStyle(polygonChartSeries, (IPolygonChartSeries) targetSeries); + } + var pointChartSeries = series as IPointChartSeries; + if (pointChartSeries != null) + { + CopyStyle(pointChartSeries, (IPointChartSeries) targetSeries); + } + var areaChartSeries = series as IAreaChartSeries; + if (areaChartSeries != null) + { + CopyStyle(areaChartSeries, (IAreaChartSeries) targetSeries); + } + var lineChartSeries = series as ILineChartSeries; + if (lineChartSeries != null) + { + CopyStyle(lineChartSeries, (ILineChartSeries) targetSeries); + } + } + } + } + + /// + /// Copies the style of one chart series to another. + /// + public static void CopySeriesStyles(IChartSeries sourceSeries, IChartSeries targetSeries) + { + CopyStyle(sourceSeries, targetSeries); + if (sourceSeries is IPointChartSeries && targetSeries is IPointChartSeries) + { + CopyStyle((IPointChartSeries) sourceSeries, (IPointChartSeries) targetSeries); + } + if (sourceSeries is ILineChartSeries && targetSeries is ILineChartSeries) + { + CopyStyle((ILineChartSeries) sourceSeries, (ILineChartSeries) targetSeries); + } + if (sourceSeries is IAreaChartSeries && targetSeries is IAreaChartSeries) + { + CopyStyle((IAreaChartSeries) sourceSeries, (IAreaChartSeries) targetSeries); + } + if (sourceSeries is IPolygonChartSeries && targetSeries is IPolygonChartSeries) + { + CopyStyle((IPolygonChartSeries) sourceSeries, (IPolygonChartSeries) targetSeries); + } + } + + private static void CopyStyle(IPolygonChartSeries series, IPolygonChartSeries targetSeries) + { + targetSeries.HatchStyle = series.HatchStyle; + targetSeries.HatchColor = series.HatchColor; + targetSeries.UseHatch = series.UseHatch; + targetSeries.Transparency = series.Transparency; + targetSeries.LineColor = series.LineColor; + targetSeries.LineWidth = series.LineWidth; + targetSeries.LineVisible = series.LineVisible; + targetSeries.LineStyle = series.LineStyle; + targetSeries.AutoClose = series.AutoClose; + } + + private static void CopyStyle(IAreaChartSeries series, IAreaChartSeries targetSeries) + { + targetSeries.Transparency = series.Transparency; + targetSeries.InterpolationType = series.InterpolationType; + targetSeries.UseHatch = series.UseHatch; + targetSeries.HatchStyle = series.HatchStyle; + targetSeries.HatchColor = series.HatchColor; + targetSeries.LineColor = series.LineColor; + targetSeries.LineWidth = series.LineWidth; + targetSeries.LineVisible = series.LineVisible; + targetSeries.PointerColor = series.PointerColor; + targetSeries.PointerStyle = series.PointerStyle; + targetSeries.PointerVisible = series.PointerVisible; + targetSeries.PointerSize = series.PointerSize; + targetSeries.PointerLineColor = series.PointerLineColor; + targetSeries.PointerLineVisible = series.PointerLineVisible; + } + + private static void CopyStyle(ILineChartSeries series, ILineChartSeries targetSeries) + { + targetSeries.Width = series.Width; + targetSeries.InterpolationType = series.InterpolationType; + targetSeries.TitleLabelVisible = series.TitleLabelVisible; + targetSeries.DashStyle = series.DashStyle; + targetSeries.PointerSize = series.PointerSize; + targetSeries.PointerColor = series.PointerColor; + targetSeries.PointerStyle = series.PointerStyle; + targetSeries.PointerVisible = series.PointerVisible; + targetSeries.PointerLineColor = series.PointerLineColor; + targetSeries.PointerLineVisible = series.PointerLineVisible; + } + + private static void CopyStyle(IPointChartSeries series, IPointChartSeries targetSeries) + { + targetSeries.LineColor = series.LineColor; + targetSeries.LineVisible = series.LineVisible; + targetSeries.Size = series.Size; + targetSeries.Style = series.Style; + } + + private static void CopyStyle(IChartSeries series, IChartSeries targetSeries) + { + targetSeries.ShowInLegend = series.ShowInLegend; + targetSeries.Title = series.Title; + targetSeries.Color = series.Color; + targetSeries.Visible = series.Visible; + targetSeries.VertAxis = series.VertAxis; + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartView.Designer.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartView.Designer.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartView.Designer.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,69 @@ +using Core.Common.Controls.Charting.Customized; + +namespace Core.Common.Controls.Charting +{ + partial class ChartView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components; + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify the contents of + /// this method with the code editor. + /// UPDATE: + /// All chart related properties are moved to IChart and Chart.SetDefaultValues + /// InitializeComponent + /// Important change: the TChart is encapsulated inside Chart class and + /// this.teeChart = new Steema.TeeChart.TChart() removed from ChartViewInitializeComponent + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.teeChart = new RingtoetsTChart(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.SuspendLayout(); + // + // teeChart + // + this.teeChart.Aspect.View3D = false; + this.teeChart.Aspect.ZOffset = 0D; + this.teeChart.Axes.Bottom.Title.Transparent = true; + this.teeChart.Axes.Depth.Title.Transparent = true; + this.teeChart.Axes.DepthTop.Title.Transparent = true; + this.teeChart.Axes.Left.Title.Transparent = true; + this.teeChart.Axes.Right.Title.Transparent = true; + this.teeChart.Axes.Top.Title.Transparent = true; + this.teeChart.BackColor = System.Drawing.Color.Transparent; + this.teeChart.Cursor = System.Windows.Forms.Cursors.Default; + this.teeChart.Dock = System.Windows.Forms.DockStyle.Fill; + this.teeChart.Header.Lines = new string[] {""}; + this.teeChart.Location = new System.Drawing.Point(0, 0); + this.teeChart.Name = "Chart"; + this.teeChart.Size = new System.Drawing.Size(542, 368); + this.teeChart.TabIndex = 0; + this.teeChart.Walls.Visible = false; + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Interval = 500; + this.timer1.Tick += new System.EventHandler(this.Timer1Tick); + // + // ChartView + // + this.Controls.Add(this.teeChart); + this.Name = "ChartView"; + this.Size = new System.Drawing.Size(542, 368); + this.ResumeLayout(false); + + } + #endregion + + private RingtoetsTChart teeChart; + private System.Windows.Forms.Timer timer1; + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartView.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartView.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartView.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,659 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +using Core.Common.Controls.Charting.Customized; +using Core.Common.Controls.Charting.Tools; +using Core.Common.Controls.Charting.Properties; + +using Steema.TeeChart; +using Steema.TeeChart.Drawing; +using Steema.TeeChart.Tools; + +namespace Core.Common.Controls.Charting +{ + /// + /// Displays series data on a chart + /// + public partial class ChartView : UserControl, IChartView + { + /// + /// Selected point of the active series has been changed + /// + public event EventHandler SelectionPointChanged; + + /// + /// The visible viewport of the chart has changed either due to a zoom, pan or scroll event + /// + public event EventHandler ViewPortChanged; + + public event EventHandler GraphResized; + + public event EventHandler ToolsActiveChanged; + private const int DisabledBackgroundAlpha = 20; + private readonly ICollection tools; + private int selectedPointIndex = -1; + private bool wheelZoom = true; + private bool afterResize = true; + private bool chartScrolled; + + private LegendScrollBar legendScrollBarTool; + private ZoomUsingMouseWheelTool zoomUsingMouseWheelTool; + + private IChart chart; + + /// + /// Displays series data on chart + /// + public ChartView() + { + InitializeComponent(); + + Chart = new Chart(); + tools = new HashSet(); + + teeChart.Zoomed += TeeChartZoomed; + teeChart.UndoneZoom += TeeChartUndoneZoom; + teeChart.Scroll += TeeChartScroll; + teeChart.BeforeDraw += TeeChartBeforeDraw; + teeChart.Resize += ChartViewResize; + teeChart.BeforeDrawSeries += ChartBeforeDrawSeries; + + teeChart.MouseDown += TeeChartMouseDown; + teeChart.MouseUp += OnMouseUp; + teeChart.MouseLeave += delegate { IsMouseDown = false; }; + teeChart.MouseMove += (s, e) => OnMouseMove(e); + teeChart.MouseClick += (s, e) => OnMouseClick(e); + + teeChart.AfterDraw += TeeChartAfterDraw; + teeChart.GetAxisLabel += OnGetAxisLabel; + teeChart.BeforeDrawAxes += OnBeforeDrawAxes; + teeChart.KeyUp += (s, e) => OnKeyUp(e); //bubble the keyup events from the chart..otherwise it does not work.. + + DateTimeLabelFormatProvider = new TimeNavigatableLabelFormatProvider(); + + InitializeWheelZoom(); + + teeChart.Legend.Alignment = LegendAlignments.Bottom; + teeChart.Chart.Header.Color = Color.Black; // To avoid blue titles everywhere + + AddTool(new ExportChartAsImageChartTool + { + Active = true, Enabled = true + }); + } + + public bool IsMouseDown { get; private set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public TimeNavigatableLabelFormatProvider DateTimeLabelFormatProvider { get; set; } + + public IWin32Window Owner { get; set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IChart Chart + { + get + { + return chart; + } + set + { + if (chart == value) + { + return; + } + + if (legendScrollBarTool != null) + { + teeChart.Tools.Remove(legendScrollBarTool); + legendScrollBarTool = null; + } + + chart = value; + // Todo: change + teeChart.Chart = ((Chart)chart).chart; + + if (zoomUsingMouseWheelTool != null) + { + teeChart.Tools.Remove(zoomUsingMouseWheelTool); + InitializeWheelZoom(); + } + + AddLegendScrollBarTool(); + } + } + + public string Title + { + get + { + return chart.Title; + } + set + { + chart.TitleVisible = !string.IsNullOrEmpty(value); + chart.Title = value ?? ""; + } + } + + /// + /// Data: in this case + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object Data + { + get + { + return Chart; + } + set + { + if (value == null) + { + //unbind series to prevent updates and memory leaks + foreach (var series in Chart.Series) + { + series.DataSource = null; + } + CleanAnnotations(); + Chart.RemoveAllChartSeries(); + } + else + { + Chart = value as IChart; + } + } + } + + /// + /// Enables zoom using mousewheel + /// + public bool WheelZoom + { + get + { + return wheelZoom; + } + set + { + wheelZoom = value; + if (zoomUsingMouseWheelTool != null) + { + zoomUsingMouseWheelTool.Active = wheelZoom; + } + } + } + + public bool AllowPanning + { + get + { + return teeChart.Panning.Allow == ScrollModes.Both; + } + set + { + teeChart.Panning.Allow = (value) + ? ScrollModes.Both + : ScrollModes.None; + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ChartCoordinateService ChartCoordinateService + { + get + { + return new ChartCoordinateService(chart); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IEnumerable Tools + { + get + { + return tools; + } + } + + /// + /// Set and get the selected Point Index (of the active series) + /// + public int SelectedPointIndex + { + get + { + return selectedPointIndex; + } + set + { + if (selectedPointIndex == value) + { + return; + } + + selectedPointIndex = value; + + if (SelectionPointChanged != null) + { + SelectionPointChanged(this, new EventArgs()); + } + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ViewInfo ViewInfo { get; set; } + + /// + /// Gives the range of the bottom axis (assuming the axis is a of type DateTime) + /// + public TimeSpan GetBottomAxisRangeAsDateTime() + { + return TeeChart2DateTime(teeChart.Axes.Bottom.Maximum) - + TeeChart2DateTime(teeChart.Axes.Bottom.Minimum); + } + + public void ZoomToValues(DateTime min, DateTime max) + { + teeChart.Chart.Axes.Bottom.SetMinMax(min, max); + } + + public void ZoomToValues(double min, double max) + { + teeChart.Chart.Axes.Bottom.SetMinMax(min, max); + } + + public T GetTool() where T : IChartViewTool + { + return (T) tools.FirstOrDefault(t => t is T); + } + + /// + /// Opens an export dialog. + /// + public void ExportAsImage() + { + Chart.ExportAsImage(Owner); + } + + public void EnableDelete(bool enable) + { + foreach (var selectTool in tools.OfType()) + { + selectTool.HandleDelete = enable; + } + } + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + + base.Dispose(disposing); + } + + internal void InternalUpdate() + { + if (teeChart.Width > 0 && teeChart.Height > 0) + { + teeChart.Draw(); + } + } + + private void AddTool(IChartViewTool tool) + { + tool.ChartView = this; + tools.Add(tool); + + tool.ActiveChanged += OnToolsActiveChanged; + } + + private void TeeChartMouseDown(object sender, MouseEventArgs e) + { + IsMouseDown = true; + OnMouseDown(e); + } + + private void CleanAnnotations() + { + if (teeChart == null) + { + return; + } + for (var i = 0; i < teeChart.Axes.Count; i++) + { + var axis = teeChart.Axes[i]; + if (axis.Tag is Annotation) + { + var annotation = (Annotation)axis.Tag; + teeChart.Tools.Remove(annotation); + axis.Tag = null; + } + } + } + + private void OnBeforeDrawAxes(object sender, Graphics3D e) + { + var senderChart = sender as RingtoetsTChart; + + if (senderChart == null) + { + return; + } + + for (int i = 0; i < senderChart.Axes.Count; i++) + { + var axis = senderChart.Axes[i]; + + if (!axis.IsDateTime) + { + //Use Number format, huge range. Specific (rounded) format is applied in OnGetAxisLabel due to TeeChart issue: TOOLS-4310 + axis.Labels.ValueFormat = "N20"; + continue; + } + + axis.Labels.DateTimeFormat = DateTimeLabelFormatProvider.CustomDateTimeFormatInfo.FullDateTimePattern; + DateTime min = Steema.TeeChart.Utils.DateTime(axis.Minimum); + DateTime max = Steema.TeeChart.Utils.DateTime(axis.Maximum); + + // TODO: move this logic completely out to FunctioBindingList and use DisplayName per property descriptor there + if (senderChart.Series.Count > 0 && senderChart.Series[0].DataSource is IChartSeries) + { + var chartSeries = senderChart.Series[0].DataSource as IChartSeries; + + // HACK: parse XValuesDataMember, search for [units] and remove it + string oldAxisTitel = chartSeries.XValuesDataMember; + + int indexOfUnitInString = axis.Title.Text.IndexOf("["); + string axisTitle = indexOfUnitInString != -1 + ? oldAxisTitel.Substring(0, indexOfUnitInString) + : oldAxisTitel; + + axis.Title.Caption = DateTimeLabelFormatProvider.ShowUnits + ? axisTitle + string.Format("[{0}]", DateTimeLabelFormatProvider.GetUnits(max - min)) + : axisTitle; + } + + var annotation = axis.Tag as Annotation; + + if (DateTimeLabelFormatProvider.ShowRangeLabel) + { + if (annotation == null) + { + annotation = new Annotation(senderChart.Chart) + { + AllowEdit = false, + AutoSize = true, + Left = senderChart.Padding.Left + }; + + annotation.Shape.Shadow.Visible = false; + senderChart.Tools.Add(annotation); + axis.Tag = annotation; + } + + annotation.Shape.Color = senderChart.BackColor; + + if (annotation.Shape.Pen.Color != senderChart.BackColor) + { + annotation.Shape.Pen.Dispose(); + annotation.Shape.Pen = new ChartPen(senderChart.BackColor); + } + + annotation.Top = (senderChart.Height - annotation.Bounds.Height) + senderChart.Padding.Bottom; + + //make sure space is reserved for the axis label / annotation + if (string.IsNullOrEmpty(axis.Title.Caption)) + { + axis.Title.Caption = " "; + } + + string title = DateTimeLabelFormatProvider.GetRangeLabel(min, max); + + if (title != null) + { + annotation.Text = title; + } + } + else + { + if (annotation == null) + { + return; + } + + senderChart.Tools.Remove(annotation); + axis.Tag = null; + } + } + } + + private void OnGetAxisLabel(object sender, GetAxisLabelEventArgs e) + { + if (sender is Axis) + { + var axis = sender as Axis; + if (axis.IsDateTime) + { + DateTime res; + DateTime.TryParse(e.LabelText, out res); + + TimeSpan axisRange = GetAxisRange(axis); + + e.LabelText = DateTimeLabelFormatProvider.GetLabel(res, axisRange); + } + else + { + //Done here 'manually' per label, due to TeeChart issue: TOOLS-4310 + double labelValue; + if (Double.TryParse(e.LabelText, out labelValue)) + { + labelValue = Math.Round(labelValue, 13); //do some rounding to prevent TeeChart problem (TOOLS-4310) + e.LabelText = labelValue.ToString(); + } + } + } + } + + private void TeeChartBeforeDraw(object sender, Graphics3D g) + { + if (teeChart.Chart.Axes.Left.Visible && + (double.IsInfinity(teeChart.Chart.Axes.Left.Maximum - teeChart.Chart.Axes.Left.Minimum))) + { + // check for all axes? + // extra error check to prevent stackoverflow in teechart + throw new InvalidOperationException(Resources.ChartView_TeeChartBeforeDraw_Can_not_draw_chart); + } + } + + private void TeeChartAfterDraw(object sender, Graphics3D g) + { + if (!Enabled) + { + g.FillRectangle(new SolidBrush(Color.FromArgb(DisabledBackgroundAlpha, Color.Black)), 0, 0, ClientRectangle.Width, ClientRectangle.Height); + } + } + + private void InitializeWheelZoom() + { + zoomUsingMouseWheelTool = new ZoomUsingMouseWheelTool(teeChart.Chart) + { + Active = wheelZoom + }; + } + + private void TeeChartScroll(object sender, EventArgs e) + { + if (ViewPortChanged != null) + { + ViewPortChanged(sender, e); + } + chartScrolled = true; + } + + private void TeeChartZoomed(object sender, EventArgs e) + { + if (ViewPortChanged != null) + { + ViewPortChanged(sender, e); + } + } + + private void TeeChartUndoneZoom(object sender, EventArgs e) + { + if (ViewPortChanged != null) + { + ViewPortChanged(sender, e); + } + } + + private void ToolSelectionChanged(object sender, PointEventArgs e) + { + SelectedPointIndex = e.Index; + } + + private void Timer1Tick(object sender, EventArgs e) + { + //check if any series requires an update and get it done.. + foreach (var series in Chart.Series) + { + if (series.RefreshRequired) + { + series.Refresh(); + } + } + } + + private void OnMouseUp(object sender, MouseEventArgs e) + { + base.OnMouseUp(e); + + if (Chart == null) + { + return; + } + + if (chartScrolled) + { + chartScrolled = false; + return; + } + + var contextMenu = new ContextMenuStrip(); + if (e.Button == MouseButtons.Right) + { + foreach (IChartViewContextMenuTool tool in tools.OfType().Where(tool => tool.Active)) + { + tool.OnBeforeContextMenu(contextMenu); + } + } + + contextMenu.Show(PointToScreen(e.Location)); + + IsMouseDown = false; + } + + private void OnToolsActiveChanged(object sender, EventArgs e) + { + if (ToolsActiveChanged != null) + { + ToolsActiveChanged(this, EventArgs.Empty); + } + } + + private void ChartViewResize(object sender, EventArgs e) + { + afterResize = true; + } + + private void ChartBeforeDrawSeries(object sender, Graphics3D g) + { + if (!afterResize) + { + return; + } + + if (GraphResized != null) + { + GraphResized(this, new EventArgs()); + } + + afterResize = false; + } + + private void AddLegendScrollBarTool() + { + legendScrollBarTool = new LegendScrollBar(teeChart.Chart) + { + Active = true, + DrawStyle = ScrollBarDrawStyle.WhenNeeded + }; + + legendScrollBarTool.Pen.Color = Color.Transparent; + legendScrollBarTool.ArrowBrush.Color = Color.DarkGray; + legendScrollBarTool.Brush.Color = SystemColors.Control; + legendScrollBarTool.ThumbBrush.Color = Color.LightGray; + } + + private static TimeSpan GetAxisRange(Axis axis) + { + DateTime min = TeeChart2DateTime(axis.Minimum); + DateTime max = TeeChart2DateTime(axis.Maximum); + return max - min; + } + + private static DateTime TeeChart2DateTime(double axisValue) + { + return Steema.TeeChart.Utils.DateTime(axisValue); + } + + #region TeeChart Factory Methods + + public EditPointTool NewEditPointTool() + { + var tool = new EditPointTool(teeChart); + AddTool(tool); + return tool; + } + + public IAddPointTool NewAddPointTool() + { + var tool = new AddPointTool(teeChart.Chart); + AddTool(tool); + return tool; + } + + public RulerTool NewRulerTool() + { + var tool = new RulerTool(teeChart); + AddTool(tool); + return tool; + } + + public SelectPointTool NewSelectPointTool() + { + var tool = new SelectPointTool(teeChart.Chart); + AddTool(tool); + tool.SelectionChanged += ToolSelectionChanged; + return tool; + } + + #endregion + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/ChartView.resx =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/ChartView.resx (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/ChartView.resx (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Core.Common.Controls.Charting.csproj =================================================================== diff -u -r78b769cdeacaa9a56939825e527e7fe71c3a0281 -r3bfa4dc5fb5ea3560752479de86cb843419f8fe3 --- Core/Common/src/Core.Common.Controls.Charting/Core.Common.Controls.Charting.csproj (.../Core.Common.Controls.Charting.csproj) (revision 78b769cdeacaa9a56939825e527e7fe71c3a0281) +++ Core/Common/src/Core.Common.Controls.Charting/Core.Common.Controls.Charting.csproj (.../Core.Common.Controls.Charting.csproj) (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -31,26 +31,126 @@ prompt + + ..\..\..\..\packages\log4net.2.0.4\lib\net40-full\log4net.dll + True + + + + + + False + ..\..\..\..\lib\TeeChart.dll + Properties\GlobalAssembly.cs + + + + + + + + UserControl + + + + Component + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + Component + + + + + Component + + + Component + + + Component + + + + + + Component + + + Component + + + {ffb69466-79de-466a-ada7-5c47c5c5ca3a} + Core.GIS.GeoAPI + + + {5770daa9-84e5-4770-af43-f6b815894368} + Core.GIS.NetTopologySuite + + + {9a2d67e6-26ac-4d17-b11a-2b4372f2f572} + Core.Common.Controls + + + {f49bd8b2-332a-4c91-a196-8cce0a2c7d98} + Core.Common.Utils + {c90b77da-e421-43cc-b82e-529651bc21ac} Core.Common.Version + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Transparantie moet tussen 0 en 100 liggen. + + + Argument kan niet de waarde 'null' hebben. + + + Bestandsnaam moet een extensie hebben. + + + Argument bevat geen bestandsnaam. + + + Nieuwe tabel + + + Kopiëren + + + Plakken + + + Verwijderen + + + Gearceerde stijl wordt niet ondersteund voor exporteren en zal genegeerd worden. + + + Extensie ({0}) wordt niet ondersteund. + + + Invoerformaat wordt niet ondersteund. Y moet numeriek zijn maar is van type: {0}. + + + Ongeldig argument voor databron van gegevensreeks. Biedt u een IEnumerable aan? IList en IListSource worden ondersteund. + + + Dit programma accepteert alleen {0} als gegevensreeks. + + + Geen cursor toegewezen aan {0}. + + + Verschil: +horizontaal: {0} +verticaal: {1} + + + seconden + + + Actie {0} wordt niet ondersteund door de TableView. + + + Ongebonden kolommen van type {0} worden niet ondersteund. + + + Ongeldig rijnummer {0}. + + + Weet u zeker dat u het volgende item wilt verwijderen: {0} + + + Kan INodePresenter voor {0} niet vinden. Zorg ervoor dat u de ontbrekende INodePresenter aan de NodePresenters eigenschap van een TreeView heeft toegevoegd. + + + Kan diagram niet tekenen. + + + Aantal X-waarden moet gelijk zijn aan het aantal Y-waarden. + + + Zou geen indices lager dan -1 moeten teruggeven! + + + Geselecteerde index is buiten bereik van de gevensreeks. + + + 'LastSelectedSeries' moet gezet zijn voordat 'SelectedPointIndex' wordt gezet. + + + Onbekende TeeChart gegevensreeks: houdt geen verband met een bekende ChartSeries. + + + Verwijderen is niet geïmplementeerd voor dit type databron. + + + X-waarde wordt vastgelegd (linker limiet) {0} => {1}. + + + X-waarde wordt vastgelegd (rechter limiet) {0} => {1}. + + + Niet te klonen subitems in menu-item. + + + De eigenschap 'DockStyle' van de inklapbare splitser kan geen waarde 'Filled' of 'None' hebben. + + + Er is een kritieke fout opgetreden. Ringtoets moet herstart worden. + + + {0} tot {1} + + + Er is een fout opgetreden. Verifieer de invoer alstublieft. + +Fout: +{0} + + + Fout opgetreden + + + OnNodeRenamed moet zijn geïmplementeerd in de afgeleide klasse. + + + Structuurweergave mag niet leeg zijn. + + + Fout tijdens slepen/neerzetten: {0}. + + + Het geselecteerde item kan niet worden verwijderd. + + + De methode of operatie is niet geïmplementeerd. + + + Datumtijd notatie niet zoals verwacht. + + + Validatie van cel is mislukt: {0}. + + + Validatie van rij is mislukt: {0}. + + + Ongeldige rij: {0}. + + + Plakken waarden mislukt: {0}. + + + Ongeldig geplakte rij {0} wordt overgeslagen. + + + Kan de waarde in cel [{0}, {1}] niet plakken. Rij {0} wordt overgeslagen. + + + Kan niet plakken in een gefilterde tableview. + + + Kan niet plakken in gesorteerde kolom. + + + Er zijn geen waardes om te plakken (kopteksten worden overgeslagen). + + + Kan alleen in rechthoekige selectie plakken. + + + Klembord bevat geen tekst. Hierdoor kan het niet in de tabel worden geplakt. + + + Wilt u de waarde corrigeren? + +Kies Ja om de waarde zelf te corrigeren. Kies Nee als u de oorspronkelijke waarde wilt terugkrijgen. + + + <aangepast> + + + Het is niet toegestaan om rijen aan deze tabel toe te voegen. + + + Niet in staat om cellen te selecteren wanneer tableView RowSelect ingeschakeld is. Gebruik in plaats daarvan SelectRow. + + + Kan tekenreeks {0} niet omzetten naar {1} om te plakken. + + + Kan waarde in cel [{0}, {1}] niet instellen. Reden: {2}. + + + Kan waarde voor rij {0} niet instellen. Reden: {1}. + + + Kopiëer alle kopteksten + + + Maak kolom los + + + Pin kolom vast + + + SelectionChanged event afgevuurd. + + + {0} exemplaren van {1} zijn met {2} vervangen. + + + Er is geen tekst om te zoeken. + + + Einde van document is bereikt. + + + Mijn Computer + + + Een optionele randstijl om te tekenen. Zet de waarde op 'Flat' om geen rand te tekenen. + + + Geen conversie van het type 'string' mogelijk. + + + Niet in staat om een presentatieobject te vinden voor niet geïnitialiseerd object. + + + Knoop moet opgegeven zijn om diens toestand op te kunnen nemen. + + + Knoop data moet aanwezig zijn om de toestand van diens knoop op te kunnen nemen. + + + Knoop heeft niet dezelfde data als de opgenomen knoop. + + + tot + + \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/AreaChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/AreaChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/AreaChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,214 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Core.Common.Utils; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting.Series +{ + public class AreaChartSeries : ChartSeries, IAreaChartSeries + { + private readonly Area areaSeries; + private InterpolationType interpolationType; + + public AreaChartSeries(IChartSeries chartSeries) : this() + { + CopySettings(chartSeries); + } + + public AreaChartSeries() : base(new Area()) + { + areaSeries = (Area) series; + areaSeries.AreaLines.Visible = false; + areaSeries.Pointer.Visible = false; + } + + public override Color Color + { + get + { + return areaSeries.Color; + } + set + { + areaSeries.Color = value; + + // also need to set AreaBrush color because TeeChart will otherwise + // throw an error (brush == null) + areaSeries.AreaBrush.Color = value; + } + } + + public HatchStyle HatchStyle + { + get + { + return areaSeries.AreaBrush.Style; + } + set + { + areaSeries.AreaBrush.Style = value; + } + } + + public Color HatchColor + { + get + { + return areaSeries.AreaBrush.ForegroundColor; + } + set + { + areaSeries.AreaBrush.ForegroundColor = value; + } + } + + public bool UseHatch + { + get + { + return !areaSeries.AreaBrush.Solid; + } + set + { + areaSeries.AreaBrush.Solid = !value; + } + } + + public int Transparency + { + get + { + return areaSeries.Transparency; + } + set + { + areaSeries.Transparency = value; + } + } + + public InterpolationType InterpolationType + { + get + { + return interpolationType; + } + set + { + interpolationType = value; + areaSeries.Stairs = (value == InterpolationType.Constant); + } + } + + public Color LineColor + { + get + { + return areaSeries.LinePen.Color; + } + set + { + areaSeries.LinePen.Color = value; + } + } + + public int LineWidth + { + get + { + return areaSeries.LinePen.Width; + } + set + { + areaSeries.LinePen.Width = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public bool LineVisible + { + get + { + return areaSeries.LinePen.Visible; + } + set + { + areaSeries.LinePen.Visible = value; + } + } + + public Color PointerColor + { + get + { + return areaSeries.Pointer.Brush.Color; + } + set + { + areaSeries.Pointer.Brush.Color = value; + } + } + + public bool PointerVisible + { + get + { + return areaSeries.Pointer.Visible; + } + set + { + areaSeries.Pointer.Visible = value; + } + } + + public int PointerSize + { + get + { + return areaSeries.Pointer.VertSize; + } + set + { + areaSeries.Pointer.VertSize = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + areaSeries.Pointer.HorizSize = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public PointerStyles PointerStyle + { + get + { + string pointerStyleName = Enum.GetName(typeof(Steema.TeeChart.Styles.PointerStyles), areaSeries.Pointer.Style); + return (PointerStyles) Enum.Parse(typeof(PointerStyles), pointerStyleName); + } + set + { + string pointerStyleName = Enum.GetName(typeof(PointerStyles), value); + areaSeries.Pointer.Style = (Steema.TeeChart.Styles.PointerStyles) Enum.Parse(typeof(Steema.TeeChart.Styles.PointerStyles), pointerStyleName); + } + } + + public Color PointerLineColor + { + get + { + return areaSeries.Pointer.Pen.Color; + } + set + { + areaSeries.Pointer.Pen.Color = value; + } + } + + public bool PointerLineVisible + { + get + { + return areaSeries.Pointer.Pen.Visible; + } + set + { + areaSeries.Pointer.Pen.Visible = value; + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/BarSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/BarSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/BarSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,86 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using Core.Common.Utils; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting.Series +{ + public class BarSeries : ChartSeries + { + private readonly Bar barSeries; + + public BarSeries() : base(new Bar()) + { + barSeries = (Bar) series; + barSeries.Marks.Visible = false; + barSeries.MultiBar = MultiBars.None; + barSeries.BarWidthPercent = 100; + barSeries.OffsetPercent = 50; + } + + public BarSeries(IChartSeries chartSeries) : this() + { + CopySettings(chartSeries); + } + + public override Color Color + { + get + { + return barSeries.Color; + } + set + { + barSeries.Color = value; + } + } + + public DashStyle DashStyle + { + get + { + return barSeries.Pen.Style; + } + set + { + barSeries.Pen.Style = value; + } + } + + public bool LineVisible + { + get + { + return barSeries.Pen.Visible; + } + set + { + barSeries.Pen.Visible = value; + } + } + + public int LineWidth + { + get + { + return barSeries.Pen.Width; + } + set + { + barSeries.Pen.Width = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public Color LineColor + { + get + { + return barSeries.Pen.Color; + } + set + { + barSeries.Pen.Color = value; + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,454 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Properties; +using Steema.TeeChart.Drawing; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting.Series +{ + public abstract class ChartSeries : IChartSeries + { + protected const int MaximumAllowedSize = 999999; + protected const int MinimumAllowedSize = 0; + internal readonly Steema.TeeChart.Styles.Series series; + + private string dataMember; + private object dataSource; + private PropertyDescriptor xPropertyDescriptor; + private PropertyDescriptor yPropertyDescriptor; + private BindingContext bindingContext; + + private IList noDataValues = new List(); + + protected ChartSeries(CustomPoint series) : this((Steema.TeeChart.Styles.Series) series) + { + series.LinePen.Width = 1; + + SetDefaultPointerStyle(series.Pointer); + series.TreatNulls = TreatNullsStyle.DoNotPaint; + } + + protected ChartSeries(Steema.TeeChart.Styles.Series series) + { + this.series = series; + + series.bBrush = new ChartBrush(); + series.ColorEach = false; + series.XValues.Order = ValueListOrder.None; + + //synchronized is the default + UpdateASynchronously = false; + } + + /// + /// Set to force the x-axis to use date-time formatting, even if no data is present. + /// + public bool XAxisIsDateTime + { + get + { + return series.XValues.DateTime; + } + set + { + series.XValues.DateTime = value; + } + } + + /// + /// The title of the series + /// + /// Changes null to an empty string to avoid TeeChart problems (TeeChart cannot cope with null titles) + public string Title + { + get + { + return series.Title; + } + set + { + series.Title = value ?? ""; + } + } + + public object DataSource + { + get + { + return dataSource; + } + set + { + Unbind(); + dataSource = value; + + series.DataSource = series; // set TeeChart specific data source to call us to get values + try + { + Bind(); + } + catch (InvalidCastException ex) + { + throw new ArgumentException(Resources.ChartSeries_DataSource_Invalid_argument_for_series_datasource_Are_you_passing_IEnumerable_IList_and_IListSource_are_supported, ex); + } + } + } + + public string XValuesDataMember + { + get + { + return series.XValues.DataMember; + } + set + { + series.XValues.DataMember = value; + Unbind(); + Bind(); + } + } + + public string YValuesDataMember + { + get + { + return series.YValues.DataMember; + } + set + { + series.YValues.DataMember = value; + Unbind(); + Bind(); + } + } + + public VerticalAxis VertAxis + { + get + { + string verticalAxisName = Enum.GetName(typeof(Steema.TeeChart.Styles.VerticalAxis), series.VertAxis); + return (VerticalAxis) Enum.Parse(typeof(VerticalAxis), verticalAxisName); + } + set + { + string verticalAxisName = Enum.GetName(typeof(VerticalAxis), value); + series.VertAxis = (Steema.TeeChart.Styles.VerticalAxis) Enum.Parse(typeof(Steema.TeeChart.Styles.VerticalAxis), verticalAxisName); + } + } + + public bool Visible + { + get + { + return series.Visible; + } + set + { + series.Visible = value; + } + } + + public bool ShowInLegend + { + get + { + return series.ShowInLegend; + } + set + { + series.ShowInLegend = value; + } + } + + public double DefaultNullValue + { + get + { + return series.DefaultNullValue; + } + set + { + series.DefaultNullValue = value; + } + } + + public IList NoDataValues + { + get + { + return noDataValues; + } + set + { + noDataValues = value; + } + } + + public bool UpdateASynchronously { get; set; } + + public object Tag { get; set; } + + public bool RefreshRequired { get; private set; } + + public IChart Chart { get; set; } + + public abstract Color Color { get; set; } + + public void CheckDataSource() + { + if (DataSource != null && (CurrencyManager == null || CurrencyManager.Count == 0)) + { + return; + } + + series.CheckDataSource(); + } + + public double XScreenToValue(int x) + { + return series.XScreenToValue(x); + } + + public void Refresh() + { + FillValues(); + CheckDataSource(); + RefreshRequired = false; + } + + public void Add(DateTime dateTime, double value) + { + series.Add(dateTime, value); + } + + public void Add(double? x, double? y) + { + series.Add(x, y); + } + + public void Add(double?[] xValues, double?[] yValues) + { + series.Add(xValues, yValues); + } + + public void Clear() + { + series.Clear(); + } + + protected void CopySettings(IChartSeries chartSeries) + { + Title = chartSeries.Title; + + if (chartSeries.DataSource == null) + { + var teeChartSeries = ((ChartSeries) chartSeries).series; + + for (int i = 0; i < teeChartSeries.XValues.Count; i++) + { + series.Add(teeChartSeries.XValues[i], teeChartSeries.YValues[i]); + } + } + else + { + DataSource = chartSeries.DataSource; + XValuesDataMember = chartSeries.XValuesDataMember; + YValuesDataMember = chartSeries.YValuesDataMember; + RefreshRequired = true; + } + + VertAxis = chartSeries.VertAxis; + Visible = chartSeries.Visible; + ShowInLegend = chartSeries.ShowInLegend; + DefaultNullValue = chartSeries.DefaultNullValue; + noDataValues = new List(chartSeries.NoDataValues); + UpdateASynchronously = chartSeries.UpdateASynchronously; + Color = chartSeries.Color; + + if (series is CustomPoint && chartSeries.Chart != null && chartSeries.Chart.StackSeries) + { + ((CustomPoint) series).Stacked = CustomStack.Stack; + } + } + + private BindingContext BindingContext + { + get + { + if (bindingContext == null) + { + bindingContext = new BindingContext(); + } + + return bindingContext; + } + } + + private CurrencyManager CurrencyManager + { + get + { + if (dataMember == null) + { + dataMember = String.Empty; + } + + if (DataSource == null) + { + return null; + } + + return (CurrencyManager) BindingContext[DataSource, dataMember]; + } + } + + private void SetDefaultPointerStyle(SeriesPointer seriesPointer) + { + seriesPointer.Pen.Color = Color.LimeGreen; + seriesPointer.Brush = new ChartBrush(series.Chart, Color.White); + seriesPointer.Pen.Width = 1; + seriesPointer.Style = Steema.TeeChart.Styles.PointerStyles.Circle; + seriesPointer.VertSize = 1; + seriesPointer.HorizSize = 1; + seriesPointer.Visible = true; + } + + private void Bind() + { + if (DataSource == null || CurrencyManager == null) + { + return; // no binding + } + + foreach (PropertyDescriptor property in CurrencyManager.GetItemProperties()) + { + if (!property.IsBrowsable) + { + continue; + } + + if (property.DisplayName == XValuesDataMember) + { + xPropertyDescriptor = property; + + if (property.PropertyType == typeof(DateTime)) + { + //log.Warn("TODO: set axis type to date time"); + } + } + + if (property.DisplayName == YValuesDataMember) + { + yPropertyDescriptor = property; + } + } + + if (xPropertyDescriptor == null || yPropertyDescriptor == null) + { + return; // nothing to bind to yet + } + + FillValues(); + + //synchronize the changed code since it should run in the UI thread. + CurrencyManager.ListChanged += CurrencyManagerListChanged; //OnListChanged; + + CheckDataSource(); + } + + private void CurrencyManagerListChanged(object sender, ListChangedEventArgs e) + { + OnListChanged(); + } + + private void FillValues() + { + series.Clear(); + foreach (object element in CurrencyManager.List) + { + Add(xPropertyDescriptor.GetValue(element), yPropertyDescriptor.GetValue(element)); + } + } + + private void Add(object x, object y) + { + if (x == null || y == null) + { + return; + } + + int addedIndex; + + bool xIsNumeric = xPropertyDescriptor.PropertyType == typeof(double) || + xPropertyDescriptor.PropertyType == typeof(float) || + xPropertyDescriptor.PropertyType == typeof(short) || + xPropertyDescriptor.PropertyType == typeof(int) || + xPropertyDescriptor.PropertyType == typeof(bool); + + bool yIsNumeric = yPropertyDescriptor.PropertyType == typeof(double) || + yPropertyDescriptor.PropertyType == typeof(float) || + yPropertyDescriptor.PropertyType == typeof(short) || + yPropertyDescriptor.PropertyType == typeof(int) || + yPropertyDescriptor.PropertyType == typeof(bool); + + bool xIsDateTime = xPropertyDescriptor.PropertyType == typeof(DateTime); + + if (!yIsNumeric) + { + throw new NotSupportedException(String.Format(Resources.ChartSeries_Add_Input_format_not_supported_y_must_be_numeric_but_is_of_type_0_, y.GetType())); + } + + double yValue = Convert.ToDouble(y); + double yValueToSet = Double.IsNaN(yValue) ? 0.0 : yValue; + + if (xIsNumeric) + { + double xValue = Convert.ToDouble(x); + addedIndex = series.Add(xValue, yValueToSet); + } + else if (xIsDateTime) + { + addedIndex = series.Add((DateTime) x, yValueToSet); + } + else //x is something non-numeric, so use the index here + { + addedIndex = series.Add((double) series.XValues.Count - 1, yValueToSet, x.ToString()); + } + + if (NoDataValues.Contains(yValue) || Double.IsNaN(yValue)) + { + series.SetNull(addedIndex); //make sure we don't actually display the added point + } + } + + private void Unbind() + { + if (DataSource == null) + { + return; + } + + CurrencyManager.ListChanged -= CurrencyManagerListChanged; + + xPropertyDescriptor = null; + yPropertyDescriptor = null; + } + + private void OnListChanged() + { + if (UpdateASynchronously) + { + RefreshRequired = true; + } + else + { + //synchronous update immediately + FillValues(); + CheckDataSource(); + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeriesFactory.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeriesFactory.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeriesFactory.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,43 @@ +using System; +using Core.Common.Controls.Charting.Properties; + +namespace Core.Common.Controls.Charting.Series +{ + public static class ChartSeriesFactory + { + public static ILineChartSeries CreateLineSeries(IChartSeries baseSeries = null) + { + return baseSeries != null + ? new LineChartSeries(baseSeries) + : new LineChartSeries(); + } + + public static IPointChartSeries CreatePointSeries(IChartSeries baseSeries = null) + { + return baseSeries != null + ? new PointChartSeries(baseSeries) + : new PointChartSeries(); + } + + public static IAreaChartSeries CreateAreaSeries(IChartSeries baseSeries = null) + { + return baseSeries != null + ? new AreaChartSeries(baseSeries) + : new AreaChartSeries(); + } + + public static IChartSeries CreateBarSeries(IChartSeries baseSeries = null) + { + return baseSeries != null + ? new BarSeries(baseSeries) + : new BarSeries(); + } + + public static IPolygonChartSeries CreatePolygonSeries(IChartSeries baseSeries = null) + { + return baseSeries != null + ? new PolygonChartSeries(baseSeries) + : new PolygonChartSeries(); + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeriesType.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeriesType.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/ChartSeriesType.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,11 @@ +namespace Core.Common.Controls.Charting.Series +{ + public enum ChartSeriesType + { + PointSeries, + LineSeries, + AreaSeries, + BarSeries, + PolygonSeries + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/IAreaChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/IAreaChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/IAreaChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,78 @@ +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Core.Common.Controls.Charting.Series +{ + public interface IAreaChartSeries : IChartSeries + { + /// + /// Style to use for the hatch brush + /// + HatchStyle HatchStyle { get; set; } + + /// + /// Color used for drawing with Hatch + /// + Color HatchColor { get; set; } + + /// + /// Use hatch brush or solid brush + /// + bool UseHatch { get; set; } + + /// + /// Transparency of the series + /// + int Transparency { get; set; } + + /// + /// Type of interpolation to use (constant, linear or none) + /// + InterpolationType InterpolationType { get; set; } + + /// + /// Color of the line above the area + /// + Color LineColor { get; set; } + + /// + /// Width of the line above the area + /// + int LineWidth { get; set; } + + /// + /// Visibility of the line above the area + /// + bool LineVisible { get; set; } + + /// + /// Pointer color + /// + Color PointerColor { get; set; } + + /// + /// Visibility of pointer + /// + bool PointerVisible { get; set; } + + /// + /// Size of the points + /// + int PointerSize { get; set; } + + /// + /// Figure (style) to use for the points + /// + PointerStyles PointerStyle { get; set; } + + /// + /// Color of the line around the points + /// + Color PointerLineColor { get; set; } + + /// + /// Show a line around the points + /// + bool PointerLineVisible { get; set; } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/ILineChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/ILineChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/ILineChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,58 @@ +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Core.Common.Controls.Charting.Series +{ + public interface ILineChartSeries : IChartSeries + { + /// + /// Width of the line + /// + int Width { get; set; } + + /// + /// Dash style of the line + /// + DashStyle DashStyle { get; set; } + + /// + /// Pointer color + /// + Color PointerColor { get; set; } + + /// + /// Visibility of pointer + /// + bool PointerVisible { get; set; } + + /// + /// Size of the line points + /// + int PointerSize { get; set; } + + /// + /// Figure (style) to use for the points + /// + PointerStyles PointerStyle { get; set; } + + /// + /// Type of interpolation to use (constant, linear or none) + /// + InterpolationType InterpolationType { get; set; } + + /// + /// Show title of the series as label + /// + bool TitleLabelVisible { get; set; } + + /// + /// Color of the line around the points + /// + Color PointerLineColor { get; set; } + + /// + /// Show a line around the points + /// + bool PointerLineVisible { get; set; } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/IPointChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/IPointChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/IPointChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,27 @@ +using System.Drawing; + +namespace Core.Common.Controls.Charting.Series +{ + public interface IPointChartSeries : IChartSeries + { + /// + /// Size of the points + /// + int Size { get; set; } + + /// + /// Figure (style) to use for the points + /// + PointerStyles Style { get; set; } + + /// + /// Show a line around the points + /// + bool LineVisible { get; set; } + + /// + /// Color of the line around the points + /// + Color LineColor { get; set; } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/IPolygonChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/IPolygonChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/IPolygonChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,78 @@ +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Core.Common.Controls.Charting.Series +{ + public interface IPolygonChartSeries : IChartSeries + { + // TODO: Add support for points + /// + /// Style to use for the hatch brush + /// + HatchStyle HatchStyle { get; set; } + + /// + /// Color used for drawing with Hatch + /// + Color HatchColor { get; set; } + + /// + /// Use hatch brush or solid brush + /// + bool UseHatch { get; set; } + + /// + /// Transparency of the series (percentage) + /// + int Transparency { get; set; } + + /// + /// Color of the line above the area + /// + Color LineColor { get; set; } + + /// + /// Width of the line above the area + /// + int LineWidth { get; set; } + + /// + /// Visibility of the line above the area + /// + bool LineVisible { get; set; } + + DashStyle LineStyle { get; set; } + + bool AutoClose { get; set; } + + /*/// + /// Pointer color + /// + Color PointerColor { get; set; } + + /// + /// Visibility of pointer + /// + bool PointerVisible { get; set; } + + /// + /// Size of the points + /// + int PointerSize { get; set; } + + /// + /// Figure (style) to use for the points + /// + PointerStyles PointerStyle { get; set; } + + /// + /// Color of the line around the points + /// + Color PointerLineColor { get; set; } + + /// + /// Show a line around the points + /// + bool PointerLineVisible { get; set; }*/ + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/LineChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/LineChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/LineChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,228 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Core.Common.Utils; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting.Series +{ + /// + /// LineChartSeries is represented by a line in a chart. + /// + public class LineChartSeries : ChartSeries, ILineChartSeries + { + private readonly Line lineSeries; + private InterpolationType interpolationType; + + public LineChartSeries() : base(new Line()) + { + lineSeries = (Line) series; + + lineSeries.ColorEachLine = true; + lineSeries.ClickableLine = true; + + lineSeries.GetSeriesMark += (s, e) => e.MarkText = Title; + } + + public LineChartSeries(IChartSeries chartSeries) : this() + { + CopySettings(chartSeries); + } + + public override Color Color + { + get + { + return lineSeries.Color; + } + set + { + lineSeries.Color = value; + } + } + + public int Width + { + get + { + return lineSeries.LinePen.Width; + } + set + { + lineSeries.LinePen.Width = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public DashStyle DashStyle + { + get + { + return lineSeries.LinePen.Style; + } + set + { + lineSeries.LinePen.Style = value; + } + } + + public float[] DashPattern + { + get + { + return lineSeries.LinePen.DashPattern; + } + set + { + lineSeries.LinePen.DashPattern = value; + } + } + + public Color PointerColor + { + get + { + return lineSeries.Pointer.Brush.Color; + } + set + { + lineSeries.Pointer.Brush.Color = value; + } + } + + public bool PointerVisible + { + get + { + return lineSeries.Pointer.Visible; + } + set + { + lineSeries.Pointer.Visible = value; + } + } + + public int PointerSize + { + get + { + return lineSeries.Pointer.VertSize; + } + set + { + // just keep it square at this moment. + lineSeries.Pointer.VertSize = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + lineSeries.Pointer.HorizSize = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public PointerStyles PointerStyle + { + get + { + string enumName = Enum.GetName(typeof(Steema.TeeChart.Styles.PointerStyles), lineSeries.Pointer.Style); + return (PointerStyles) Enum.Parse(typeof(PointerStyles), enumName); + } + set + { + string enumName = Enum.GetName(typeof(PointerStyles), value); + lineSeries.Pointer.Style = + (Steema.TeeChart.Styles.PointerStyles) + Enum.Parse(typeof(Steema.TeeChart.Styles.PointerStyles), enumName); + } + } + + public InterpolationType InterpolationType + { + get + { + return interpolationType; + } + set + { + interpolationType = value; + lineSeries.Stairs = (value == InterpolationType.Constant); + } + } + + public bool TitleLabelVisible + { + get + { + return lineSeries.Marks.Visible; + } + set + { + lineSeries.Marks.Visible = value; + lineSeries.Marks.Text = Title; + } + } + + public bool XValuesDateTime + { + get + { + return lineSeries.XValues.DateTime; + } + set + { + lineSeries.XValues.DateTime = value; + } + } + + public Color PointerLineColor + { + get + { + return lineSeries.Pointer.Pen.Color; + } + set + { + lineSeries.Pointer.Pen.Color = value; + } + } + + public bool PointerLineVisible + { + get + { + return lineSeries.Pointer.Pen.Visible; + } + set + { + lineSeries.Pointer.Pen.Visible = value; + } + } + + public int Transparency + { + get + { + return lineSeries.Transparency; + } + set + { + lineSeries.Transparency = value; + } + } + + public double MaxYValue() + { + return lineSeries.MaxYValue(); + } + + public double MinYValue() + { + return lineSeries.MinYValue(); + } + + public double CalcXPos(int index) + { + return lineSeries.CalcXPos(index); + } + + public double CalcYPos(int index) + { + return lineSeries.CalcYPos(index); + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/PointChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/PointChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/PointChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,90 @@ +using System; +using System.Drawing; +using Core.Common.Utils; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting.Series +{ + /// + /// Series to display points on a chart + /// + public class PointChartSeries : ChartSeries, IPointChartSeries + { + private readonly Points pointSeries; + + public PointChartSeries() : base(new Points()) + { + pointSeries = (Points) series; + LineVisible = false; + } + + public PointChartSeries(IChartSeries chartSeries) : this() + { + CopySettings(chartSeries); + } + + public override Color Color + { + get + { + return pointSeries.Pointer.Brush.Color; + } + set + { + pointSeries.Pointer.Brush.Color = value; + } + } + + public int Size + { + get + { + return pointSeries.Pointer.VertSize; + } + set + { + // just keep it square at this moment. + pointSeries.Pointer.VertSize = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + pointSeries.Pointer.HorizSize = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public PointerStyles Style + { + get + { + string enumName = Enum.GetName(typeof(Steema.TeeChart.Styles.PointerStyles), pointSeries.Pointer.Style); + return (PointerStyles) Enum.Parse(typeof(PointerStyles), enumName); + } + set + { + string enumName = Enum.GetName(typeof(PointerStyles), value); + pointSeries.Pointer.Style = (Steema.TeeChart.Styles.PointerStyles) Enum.Parse(typeof(Steema.TeeChart.Styles.PointerStyles), enumName); + } + } + + public bool LineVisible + { + get + { + return pointSeries.Pointer.Pen.Visible; + } + set + { + pointSeries.Pointer.Pen.Visible = value; + } + } + + public Color LineColor + { + get + { + return pointSeries.Pointer.Pen.Color; + } + set + { + pointSeries.Pointer.Pen.Color = value; + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/PolygonChartSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/PolygonChartSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/PolygonChartSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,163 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Core.Common.Controls.Charting.Properties; +using Core.Common.Utils; + +namespace Core.Common.Controls.Charting.Series +{ + public class PolygonChartSeries : ChartSeries, IPolygonChartSeries + { + private readonly PolygonSeries polygonSeries; + + public PolygonChartSeries(IChartSeries chartSeries) : this() + { + CopySettings(chartSeries); + ChartStyleHelper.CopySeriesStyles(chartSeries, this); + Tag = chartSeries.Tag; + } + + public PolygonChartSeries() : base(new PolygonSeries()) + { + polygonSeries = (PolygonSeries) series; + DefaultNullValue = double.NaN; + } + + public override Color Color + { + get + { + return series.Color; + } + set + { + series.Color = value; + + // also need to set AreaBrush color because TeeChart will otherwise + // throw an error (brush == null) + (polygonSeries).bBrush.Color = value; + } + } + + public Color LineColor + { + get + { + return polygonSeries.Pen.Color; + } + set + { + polygonSeries.Pen.Color = value; + } + } + + public int LineWidth + { + get + { + return polygonSeries.Pen.Width; + } + set + { + polygonSeries.Pen.Width = MathUtils.ClipValue(value, MinimumAllowedSize, MaximumAllowedSize); + } + } + + public bool LineVisible + { + get + { + return polygonSeries.Pen.Visible; + } + set + { + polygonSeries.Pen.Visible = value; + polygonSeries.Repaint(); + } + } + + public DashStyle LineStyle + { + get + { + return polygonSeries.Pen.Style; + } + set + { + polygonSeries.Pen.Style = value; + } + } + + public bool AutoClose + { + get + { + return polygonSeries.AutoClose; + } + set + { + polygonSeries.AutoClose = value; + } + } + + # region Hatch + + public HatchStyle HatchStyle + { + get + { + return polygonSeries.bBrush.Style; + } + set + { + polygonSeries.bBrush.Style = value; + } + } + + public Color HatchColor + { + get + { + return polygonSeries.bBrush.ForegroundColor; + } + set + { + polygonSeries.bBrush.ForegroundColor = value; + } + } + + public bool UseHatch + { + get + { + return !polygonSeries.bBrush.Solid; + } + set + { + polygonSeries.bBrush.Solid = !value; + } + } + + /// + /// Percentage transparancy. This should be between 0 and 100. + /// + public int Transparency + { + get + { + return polygonSeries.Pen.Transparency; + } + set + { + if (value < 0 || value > 100) + { + throw new ArgumentOutOfRangeException("value", Resources.PolygonChartSeries_Transparency_Transparancy_should_be_between_0_and_100); + } + polygonSeries.Pen.Transparency = value; + polygonSeries.bBrush.Transparency = value; + } + } + + # endregion + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Series/PolygonSeries.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Series/PolygonSeries.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Series/PolygonSeries.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using Core.Common.Controls.Charting.Properties; +using Steema.TeeChart.Drawing; + +namespace Core.Common.Controls.Charting.Series +{ + /// + /// TeeChart wrapper class to fix PolygonSeries when drawn into a 2D axes (was meant for drawing on a Map). + /// + internal class PolygonSeries : Steema.TeeChart.Styles.PolygonSeries + { + private bool autoClose; + + /// + /// Closes the polygon automatically when true. + /// + public bool AutoClose + { + get + { + return autoClose; + } + set + { + autoClose = value; + Repaint(); + } + } + + /// + /// Override Draw method and use the TeeChart Graphics3D object to draw the polygon shape + /// + public override void Draw() + { + if (XValues.Count < 2) + { + return; + } + + if (XValues.Count != YValues.Count) + { + // Just to be sure. I think TeeChart already accounts for this in Add methods. + throw new Exception(Resources.PolygonSeries_Draw_Number_of_X_values_should_be_equal_to_the_number_of_Y_values); + } + + var g = Chart.Graphics3D; + PrepareGraphics3D(g); + + g.ClipRectangle(Chart.ChartBounds); + + // Draw polygon groups: + foreach (var pointList in GetDrawingPoints()) + { + g.Polygon(pointList); + } + + g.UnClip(); + } + + /// + /// Override DrawLegendShape because it includes a call to Polygon().ParentShape.... which is null if we do not use Polygon to create it. + /// + /// + /// + /// + protected override void DrawLegendShape(Graphics3D g, int valueIndex, Rectangle rect) + { + PrepareGraphics3D(g); + + var oldWidth = 1; + if (g.Pen.Visible) + { + oldWidth = g.Pen.Width; + g.Pen.Width = 1; + } + + base.DrawLegendShape(g, valueIndex, rect); + + if (g.Pen.Visible) + { + g.Pen.Width = oldWidth; + } + } + + /// + /// Override SetActive because it includes a call to Polygon().ParentShape.... which is null if we do not use Polygon to create it. + /// + /// + protected override void SetActive(bool value) + { + SetBooleanProperty(ref bActive, value); + } + + /// + /// Override PrepareLegendCanvas because it includes a call to Polygon().ParentShape.... which is null if we do not use Polygon to create it. + /// + /// + /// + /// + /// + protected override void PrepareLegendCanvas(Graphics3D g, int valueIndex, ref Color backColor, ref ChartBrush aBrush) {} + + private void PrepareGraphics3D(Graphics3D g) + { + g.Pen = Pen; + g.Brush = bBrush; + g.Pen.DrawingPen.LineJoin = LineJoin.Round; + } + + private IEnumerable GetDrawingPoints() + { + var pointGroupsToRender = new List(); + var currentPointsGroup = new List(); + for (int i = 0; i < XValues.Count; i++) + { + var xValue = XValues[i]; + var yValue = YValues[i]; + if (double.IsNaN(xValue) || double.IsNaN(yValue)) + { + continue; + } + // TODO: How to distinguish null?? + // Can cause collision when a regular value has the same value! + if (xValue == DefaultNullValue || yValue == DefaultNullValue) + { + AddToPointsList(pointGroupsToRender, currentPointsGroup); + continue; + } + + currentPointsGroup.Add(new Point(CalcXPosValue(xValue), CalcYPosValue(yValue))); + } + + AddToPointsList(pointGroupsToRender, currentPointsGroup); + return pointGroupsToRender; + } + + private void AddToPointsList(ICollection pointGroupsToRender, List currentPointsGroup) + { + if (!currentPointsGroup.Any()) + { + return; // Do nothing for empty list + } + + if (autoClose && (currentPointsGroup[0].X != currentPointsGroup.Last().X || currentPointsGroup[0].Y != currentPointsGroup.Last().Y)) + { + currentPointsGroup.Add(currentPointsGroup[0]); + } + pointGroupsToRender.Add(currentPointsGroup.ToArray()); + currentPointsGroup.Clear(); + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/TeeChartHelper.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/TeeChartHelper.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/TeeChartHelper.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,67 @@ +using System; +using System.Drawing; +using System.Reflection; +using Core.Common.Controls.Charting.Properties; +using Core.GIS.NetTopologySuite.Utilities; +using Steema.TeeChart.Tools; + +namespace Core.Common.Controls.Charting +{ + /// + /// Utility class for common functions used by chart tools + /// + public class TeeChartHelper + { + public static int GetNearestPoint(Steema.TeeChart.Styles.Series series, Point p, double tolerance) + { + int tmpMin = 0; + int tmpMax = 0; + + int result = -1; + int Dif = 10000; + + // Tool.GetFirstLastSeries is changed from public to internal and we do not want to modify TeeChart + object[] args = new object[] + { + series, + tmpMin, + tmpMax + }; + MethodInfo getFirstLastSeries = typeof(Tool).GetMethod("GetFirstLastSeries", BindingFlags.Static | BindingFlags.NonPublic); + bool getFirstLastSeriesResult = (bool) getFirstLastSeries.Invoke(null, args); + // retrieve the out parameters + tmpMin = (int) args[1]; + tmpMax = (int) args[2]; + if (getFirstLastSeriesResult) + { + for (int t = tmpMin; t <= tmpMax; t++) + { + int tmpX = series.CalcXPos(t); + int tmpY = series.CalcYPos(t); + Rectangle r = series.Chart.ChartRect; + + if (r.Contains(tmpX, tmpY)) + { + int Dist = + Steema.TeeChart.Utils.Round( + Math.Sqrt(Steema.TeeChart.Utils.Sqr(p.X - tmpX) + Steema.TeeChart.Utils.Sqr(p.Y - tmpY))); + + if (Dist < Dif && Dist < tolerance) + { + Dif = Dist; + result = t; + } + } + } + } + //THIS is done because Tool.GetFirstLastSeries(series, out tmpMin, out tmpMax)) is not documented (unable to find) + Assert.IsTrue(result >= -1, Resources.TeeChartHelper_GetNearestPoint_Should_not_return_indexes_below_1_); + return result; + } + + public static Color DarkenColor(Color color, int percentage) + { + return Steema.TeeChart.Utils.DarkenColor(color, percentage); + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/TimeNavigatableLabelFormatProvider.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/TimeNavigatableLabelFormatProvider.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/TimeNavigatableLabelFormatProvider.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,112 @@ +using System; +using System.Globalization; + +using Core.Common.Controls.Charting.Properties; + +namespace Core.Common.Controls.Charting +{ + /// + /// Provides custom DateTime formatting (e.g. for use on the chart axes based on the zoom level). More detail (eg time) when zoomed in etc. + /// Also sets the axis title with the current zoom range. This class contains two methods that can be overwritten to implement + /// custom DateTime formatting behavior (eg, show Quarters instead of Months, etc). + /// + public class TimeNavigatableLabelFormatProvider + { + private static readonly string strTill = Resources.strTill; + + /// + /// Initializes the provider, sets the default CustomDateTimeFormatInfo. + /// + public TimeNavigatableLabelFormatProvider() + { + ShowRangeLabel = true; //default + ShowUnits = true; + CustomDateTimeFormatInfo = CultureInfo.CurrentCulture.DateTimeFormat; + } + + /// + /// The culture-specific CustomDateTimeFormatInfo to use when rendering the date times. + /// Default is the CurrentCulture.CustomDateTimeFormatInfo. + /// + public DateTimeFormatInfo CustomDateTimeFormatInfo { get; set; } + + /// + /// Indicates whether the range label (typically with the date/time range) is shown. Default true. + /// + public virtual bool ShowRangeLabel { get; set; } + + /// + /// Gets or sets flag indicating whether units are shown for time. + /// TODO: move it to FunctionBindingList (presentation facade) + /// + public bool ShowUnits { get; set; } + + /// + /// Should be overwritten to implement custom label text for the labelValue. + /// + /// The datetime value to provide a formatted string for. + /// The current (zoomed) axis range. Typically if the range is large, + /// you can omit details such as hours and seconds. + /// + public virtual string GetLabel(DateTime labelValue, TimeSpan duration) + { + return labelValue.ToString(GetUnits(duration)); + } + + /// + /// Units of measure for a given duration. Not included in labels! Client code should show this units depending on ShowUnits flag. + /// + /// The current (zoomed) axis range. Typically if the range is large, + /// you can omit details such as hours and seconds. + /// + public virtual string GetUnits(TimeSpan duration) + { + string format; + + if (duration.TotalHours < 1) + { + format = CustomDateTimeFormatInfo.LongTimePattern; + } + else if (duration.TotalDays < 1) + { + format = CustomDateTimeFormatInfo.ShortTimePattern; + } + else if (duration.TotalDays < 5) + { + format = CustomDateTimeFormatInfo.ShortDatePattern + " " + CustomDateTimeFormatInfo.ShortTimePattern; + } + else if (duration.TotalDays < 30) + { + format = CustomDateTimeFormatInfo.ShortDatePattern; + } + else + { + format = CustomDateTimeFormatInfo.YearMonthPattern; + } + + return format; + } + + /// + /// Should be overwritten to implement a custom axis title text. Requires ShowRangeLabel to be true. + /// If you return null, the original title is kept. Default is "date / time ([long date min] till [long date max])" + /// + /// Minimum value of axis + /// Maximum value of axis + /// + public virtual string GetRangeLabel(DateTime min, DateTime max) + { + TimeSpan span = max - min; + + if (max.DayOfYear == min.DayOfYear && max.Year == min.Year) //same day: show only one day + { + return "(" + max.ToString(CustomDateTimeFormatInfo.LongDatePattern) + ")"; + } + else + { + return "(" + min.ToString(CustomDateTimeFormatInfo.LongDatePattern) + " " + strTill + " " + + max.ToString(CustomDateTimeFormatInfo.LongDatePattern) + ")"; + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/AddPointTool.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/AddPointTool.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/AddPointTool.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,291 @@ +using System; +using System.Drawing; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Series; +using Core.Common.Controls.Charting.Properties; +using log4net; +using Steema.TeeChart; +using Steema.TeeChart.Styles; +using Steema.TeeChart.Tools; + +namespace Core.Common.Controls.Charting.Tools +{ + /// + /// Tool that enables interactive adding of points to a series. + /// + public class AddPointTool : ChartViewSeriesToolBase, IAddPointTool + { + public event EventHandler PointAdded; + private static readonly ILog log = LogManager.GetLogger(typeof(AddPointTool)); + + private MouseButtons button = MouseButtons.Left; + private bool dragging = false; + + /// + /// Tool that enables interactive adding of points to a series. + /// + /// + public AddPointTool(Steema.TeeChart.Chart c) : base(c) + { + Cursor = Cursors.Hand; //default + } + + /// + /// Tool that enables interactive adding of points to a series. + /// + public AddPointTool() : this(null) {} + + /// + /// Gets descriptive text. + /// + public override string Description + { + get + { + return "AddPoint"; + } + } + + /// + /// Gets detailed descriptive text. + /// + public override string Summary + { + get + { + return Texts.DragPointSummary; + } + } + + public bool Insert { get; set; } + + /// + /// Sets which mousebutton activates DragPoint. + /// + public MouseButtons Button + { + get + { + return button; + } + set + { + button = value; + } + } + + /// + /// Determines the type of DragPoint Cursor displayed. + /// + public Cursor Cursor { get; set; } + + public bool AddOnlyIfOnLine { get; set; } + + public ILineChartSeries Series + { + get + { + return (ILineChartSeries) base.Series; + } + set + { + base.Series = value; + } + } + + protected override void Assign(Tool t) + { + base.Assign(t); + AddPointTool tmp = t as AddPointTool; + tmp.Button = Button; + tmp.Cursor = Cursor; + //tmp.Style = Style; + } + + protected override void OnMouseMove(MouseEventArgs e, ref Cursor c) + { + Point P = new Point(e.X, e.Y); + if (!dragging) + { + Steema.TeeChart.Styles.Series s = ClickedSeries(P); + + if (s != null) + { + int abovePoint = s.Clicked(P); + if (abovePoint > -1) + { + c = Cursor; + //new Cursor(Assembly.GetExecutingAssembly().GetManifestResourceStream("Steema.TeeChart.Cursors.moveTracker.cur")); + } + Chart.CancelMouse = true; + } + else + { + c = Cursors.Default; + } + } + } + + protected override void OnMouseUp(MouseEventArgs e) + { + dragging = false; + } + + protected override void OnMouseDown(MouseEventArgs e) + { + Point p = new Point(e.X, e.Y); + //button == Button.Left + if (Steema.TeeChart.Utils.GetMouseButton(e) == button) + { + dragging = false; + + if (LastSelectedSeries == null) + { + foreach (Steema.TeeChart.Styles.Series s in Chart.Series) + { + if (s.Active) + { + dragging = (s.Clicked(p) != -1); + if (dragging) + { + LastSelectedSeries = s; + break; + } + } + } + } + + if (LastSelectedSeries != null) + { + { + if (AddOnlyIfOnLine && !IsPointOnLine(new Point(e.X, e.Y))) + { + return; + } + + //check if user clicked near enough to line. + //calculate real x value from screen value + + double x = LastSelectedSeries.XScreenToValue(e.X); + + //interpolate y value + double y = LastSelectedSeries.YScreenToValue(e.Y); + + //add/insert point to the series + string oldOrderName = Enum.GetName(typeof(ValueListOrder), LastSelectedSeries.XValues.Order); + + LastSelectedSeries.XValues.Order = ValueListOrder.None; // .Ascending; + if (x > Chart.Axes.Bottom.MinXValue + && x < Chart.Axes.Bottom.MaxXValue + && y > Chart.Axes.Left.MinYValue + && y < Chart.Axes.Left.MaxYValue + ) + { + // y = LinearInterPolation(theSeries.XValues, theSeries.YValues, x); + if (Insert) + { + // do not use nearest point but try to calculate nearest line segment + double minDistance = double.MaxValue; + int minIndex = -1; + for (int i = 1; i < LastSelectedSeries.XValues.Count; i++) + { + double dist = LinePointDistance(LastSelectedSeries.XValues[i - 1], LastSelectedSeries.YValues[i - 1], + LastSelectedSeries.XValues[i], LastSelectedSeries.YValues[i], + x, y); + if (dist < minDistance) + { + minDistance = dist; + minIndex = i; + } + } + LastSelectedSeries.Add(x, y); + if (PointAdded != null) + { + PointAdded(this, new PointEventArgs(GetChartSeriesFromInternalSeries(LastSelectedSeries), minIndex, x, y)); + } + } + else + { + int index = LastSelectedSeries.Add(x, y); + + //notify listeners a point was inserted. + + if (PointAdded != null) + { + PointAdded(this, new PointEventArgs(GetChartSeriesFromInternalSeries(LastSelectedSeries), index, x, y)); + } + } + } + LastSelectedSeries.XValues.Order = (ValueListOrder) Enum.Parse(typeof(ValueListOrder), oldOrderName); + } + } + } + } + + private bool IsPointOnLine(Point clickedPoint) + { + Steema.TeeChart.Styles.Series s = ClickedSeries(clickedPoint); + + if (s != null) + { + int abovePoint = s.Clicked(clickedPoint); + if (abovePoint > -1) + { + return true; + } + } + return false; + } + + // HACK + private static double Distance(double x1, double y1, double X2, double Y2) + { + return Math.Sqrt((x1 - X2)*(x1 - X2) + (y1 - Y2)*(y1 - Y2)); + } + + private static double CrossProduct(double Ax, double Ay, double Bx, double By, + double cx, double cy) + { + return (Bx - Ax)*(cy - Ay) - (By - Ay)*(cx - Ax); + } + + private static double Dot(double Ax, double Ay, double Bx, double By, + double cx, double cy) + { + return (Bx - Ax)*(cx - Bx) + (By - Ay)*(cy - By); + } + + private static double LinePointDistance(double Ax, double Ay, double Bx, double By, + double cx, double cy) + { + var dist = Distance(Ax, Ay, Bx, By); + if (dist < 0.000001) + { + return double.MaxValue; + } + dist = CrossProduct(Ax, Ay, Bx, By, cx, cy)/dist; + // if (isSegment) always true + var dot1 = Dot(Ax, Ay, Bx, By, cx, cy); + if (dot1 > 0) + { + return Distance(Bx, By, cx, cy); + } + var dot2 = Dot(Bx, By, Ax, Ay, cx, cy); + if (dot2 > 0) + { + return Distance(Ax, Ay, cx, cy); + } + return Math.Abs(dist); + } + } + + public interface IAddPointTool : IChartViewTool + { + event EventHandler PointAdded; + MouseButtons Button { get; set; } + Cursor Cursor { get; set; } + bool Insert { get; set; } + bool AddOnlyIfOnLine { get; set; } + ILineChartSeries Series { get; set; } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/ChartViewSeriesToolBase.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/ChartViewSeriesToolBase.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/ChartViewSeriesToolBase.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,244 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Series; +using Core.Common.Controls.Charting.Properties; +using Steema.TeeChart; +using Steema.TeeChart.Styles; +using Steema.TeeChart.Tools; + +namespace Core.Common.Controls.Charting.Tools +{ + /// + /// Base class for EditPointTool,SelectPointTool and AddPointTool + /// + public abstract class ChartViewSeriesToolBase : ToolSeries, IChartViewSeriesTool + { + public event SelectionChangedEventHandler SelectionChanged; + + public event EventHandler ActiveChanged; + + protected int selectedPointIndex = -1; + private bool enabled = true; + + private ChartSeries series; + + private Steema.TeeChart.Styles.Series previousSelectedSeries; + + private Steema.TeeChart.Styles.Series lastSelectedSeries; + + protected ChartViewSeriesToolBase(Steema.TeeChart.Chart chart) : base(chart) {} + + protected ChartViewSeriesToolBase(Steema.TeeChart.Styles.Series series) : base(series) {} + + /// + /// Selects a point in the chart. + /// + public int SelectedPointIndex + { + get + { + return selectedPointIndex; + } + protected set + { + if (selectedPointIndex != value || !ReferenceEquals(previousSelectedSeries, LastSelectedSeries)) + { + previousSelectedSeries = LastSelectedSeries; + + if ((LastSelectedSeries != null)) + { + if ((value > LastSelectedSeries.Count - 1)) + { + throw new ArgumentException(Resources.ChartViewSeriesToolBase_SelectedPointIndex_Selected_index_outside_range_of_series); + } + + selectedPointIndex = value; + if (SelectionChanged != null) + { + if (selectedPointIndex != -1) + { + SelectionChanged(this, + new PointEventArgs( + GetChartSeriesFromInternalSeries(LastSelectedSeries), + selectedPointIndex, + LastSelectedSeries.XValues[selectedPointIndex], + LastSelectedSeries.YValues[selectedPointIndex])); + } + else + { + SelectionChanged(this, + new PointEventArgs( + GetChartSeriesFromInternalSeries(LastSelectedSeries), + selectedPointIndex, double.NaN, double.NaN)); + } + } + Invalidate(); + } + else + { + throw new InvalidOperationException(Resources.ChartViewSeriesToolBase_SelectedPointIndex_LastSelectedSeries_needs_to_be_set_before_SelectedPointIndex_is_set); + } + } + } + } + + public IChartView ChartView { get; set; } + + public bool Enabled + { + get + { + return enabled; + } + set + { + enabled = value; + } + } + + public new bool Active + { + get + { + return base.Active; + } + set + { + base.Active = value; + if (ActiveChanged != null) + { + ActiveChanged(this, null); + } + } + } + + public new IChartSeries Series + { + get + { + return series; + } + set + { + series = (ChartSeries) value; + iSeries = series.series; + } + } + + protected Steema.TeeChart.Styles.Series LastSelectedSeries + { + get + { + return iSeries ?? lastSelectedSeries; //if internal series is set, use that (tool is only for that series), otherwise return last selected + } + set + { + if (iSeries != null && iSeries != value) + { + throw new ArgumentException(String.Format(Resources.ChartViewSeriesToolBase_LastSelectedSeries_This_tool_only_accepts_0_as_series, iSeries)); + } + lastSelectedSeries = value; + } + } + + protected override void SetSeries(Steema.TeeChart.Styles.Series series) + { + var customPoint = ((ChartSeries) Series).series as CustomPoint; + if (customPoint != null) + { + customPoint.GetPointerStyle -= OnCustomSeriesGetPointerStyle; + } + + base.SetSeries(series); + + customPoint = series as CustomPoint; + if (customPoint != null) + { + customPoint.GetPointerStyle += OnCustomSeriesGetPointerStyle; + } + } + + protected virtual void OnCustomSeriesGetPointerStyle(CustomPoint customSeries, GetPointerStyleEventArgs e) {} + + protected Steema.TeeChart.Styles.Series ClickedSeries(Point p) + { + return ClickedSeries(p.X, p.Y); + } + + protected Steema.TeeChart.Styles.Series ClickedSeries(int x, int y) + { + if (iSeries == null) + { + var seriesInRenderOrder = Chart.Series.OfType().Reverse(); + + return seriesInRenderOrder.FirstOrDefault(s => (s.Active) && (s.Clicked(x, y) != -1)); + } + else if (iSeries.Clicked(x, y) != -1) + { + return iSeries; + } + + return null; + } + + protected IChartSeries GetChartSeriesFromInternalSeries(Steema.TeeChart.Styles.Series internalSeries) + { + if (ChartView != null && ChartView.Chart != null) + { + var matchingSeries = + ChartView.Chart.Series.FirstOrDefault( + cs => ReferenceEquals(cs.series, internalSeries)); + + if (matchingSeries != null) + { + return matchingSeries; + } + + matchingSeries = + ChartView.Chart.Series.FirstOrDefault(cs => cs.Title == internalSeries.Title); + + if (matchingSeries != null) + { + return matchingSeries; + } + } + throw new ArgumentException(Resources.ChartViewSeriesToolBase_GetChartSeriesFromInternalSeries_Unknown_TeeChart_series_Not_related_to_any_known_ChartSeries); + } + + /// + /// Handles mouse events for the tools and chart of Teechart. Teechart uses a special + /// mechanism to let tools cooperate via chart.CancelMouse. Therefor do not use + /// control mouse events directly. + /// + /// + /// + /// + protected override void MouseEvent(MouseEventKinds kind, MouseEventArgs e, ref Cursor c) + { + if (!Enabled) + { + return; + } + switch (kind) + { + case MouseEventKinds.Up: + OnMouseUp(e); + break; + case MouseEventKinds.Move: + OnMouseMove(e, ref c); + break; + case MouseEventKinds.Down: + OnMouseDown(e); + break; + } + } + + protected virtual void OnMouseDown(MouseEventArgs mouseEventArgs) {} + + protected virtual void OnMouseMove(MouseEventArgs mouseEventArgs, ref Cursor cursor) {} + + protected virtual void OnMouseUp(MouseEventArgs mouseEventArgs) {} + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/EditPointTool.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/EditPointTool.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/EditPointTool.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,438 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Drawing; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Customized; +using Core.Common.Controls.Charting.Series; +using Core.Common.Controls.Charting.Properties; +using Core.Common.Utils.Extensions; + +using log4net; +using Steema.TeeChart.Styles; + +namespace Core.Common.Controls.Charting.Tools +{ + /// + /// Tools for moving and deleting points (responds to 'DEL'button) + /// + public class EditPointTool : ChartViewSeriesToolBase + { + public event EventHandler BeforeDrag; + + public event EventHandler MouseHoverPoint; + + public event EventHandler AfterPointEdit; + private const double clippingTolerance = 1e-4; + private static readonly ILog log = LogManager.GetLogger(typeof(EditPointTool)); + private readonly ToolTip toolTip; + private readonly RingtoetsTChart tChart; + private DragStyle style = DragStyle.Both; + private bool pointHasMoved; + private Color selectedPointerColor = Color.LimeGreen; + private readonly Steema.TeeChart.Styles.PointerStyles selectedPointerStyle = Steema.TeeChart.Styles.PointerStyles.Diamond; + private bool clipXValues = true; + private bool restoreZoom; + + /// + /// Allows user to interactively change points in a chart + /// + /// + public EditPointTool(Steema.TeeChart.Chart c) : base(c) + { + toolTip = new ToolTip + { + ShowAlways = false + }; + } + + /// + /// Allows user to interactively change points in a chart + /// + /// + public EditPointTool(Steema.TeeChart.Styles.Series s) : base(s) {} + + /// + /// Allows user to interactively change points in a chart + /// + public EditPointTool() : this(((Steema.TeeChart.Chart) null)) {} + + /// + /// Allows user to interactively change points in a chart + /// Constructor for schowing a toolTip displaying the coordinates in drga mode. + /// + /// + public EditPointTool(RingtoetsTChart c) + : base(c.Chart) + { + tChart = c; + toolTip = new ToolTip(); + toolTip.ShowAlways = false; + } + + /// + /// Color of the point selected in the chart + /// + public Color SelectedPointerColor + { + get + { + return selectedPointerColor; + } + set + { + selectedPointerColor = value; + } + } + + /// + /// True if series line can represent a polygon, otherwise x-coordinate of every point will be limited by x value of it's neighbours. + /// + public bool IsPolygon { get; set; } + + /// + /// Clip means that a point cannot be dragged PASSED another in X direction + /// + public bool ClipXValues + { + get + { + return clipXValues; + } + set + { + clipXValues = value; + } + } + + /// + /// Clip means that a point cannot be dragged PASSED another in Y direction + /// + public bool ClipYValues { get; set; } + + public DragStyle DragStyles + { + get + { + return style; + } + set + { + style = value; + } + } + + public ILineChartSeries Series + { + get + { + return (ILineChartSeries) base.Series; + } + set + { + base.Series = value; + } + } + + protected override void KeyEvent(KeyEventArgs e) + { + base.KeyEvent(e); + if (e.KeyCode == Keys.Delete && selectedPointIndex != -1) + { + // try to delete point from datasource so event is fired + DataTable dataTable = LastSelectedSeries.DataSource as DataTable; + + if (dataTable != null) + { + dataTable.Rows[selectedPointIndex].Delete(); + return; + } + + var lineChartSeries = LastSelectedSeries.DataSource as LineChartSeries; + if (lineChartSeries != null) + { + lineChartSeries.series.Delete(selectedPointIndex); + SelectedPointIndex = -1; + return; + } + throw new NotImplementedException(Resources.KeyEvent_Deletion_not_implemented_for_this_type_of_datasource); + } + } + + protected override void OnMouseDown(MouseEventArgs e) + { + Point p = new Point(e.X, e.Y); + + //why not e.Button? + if (Steema.TeeChart.Utils.GetMouseButton(e) != MouseButtons.Left) + { + return; + } + + // in case of mouseclick, select point nearest to mouse + if (LastSelectedSeries != null) + { + var tolerance = Series != null ? Series.PointerSize : 3; + int index = TeeChartHelper.GetNearestPoint(LastSelectedSeries, p, tolerance); + if (-1 != index) + { + if (BeforeDrag != null) + { + var args = new PointEventArgs(GetChartSeriesFromInternalSeries(LastSelectedSeries), index, + LastSelectedSeries.XValues[index], + LastSelectedSeries.YValues[index]); + + BeforeDrag(this, args); + + if (args.Cancel) + { + return; + } + } + + SelectedPointIndex = index; + Chart.CancelMouse = true; + restoreZoom = Chart.Zoom.Active; + Chart.Zoom.Active = false; + Chart.Zoom.Allow = false; + pointHasMoved = false; + Invalidate(); + } + } + } + + protected override void OnMouseMove(MouseEventArgs e, ref Cursor c) + { + var p = new Point(e.X, e.Y); + + Steema.TeeChart.Styles.Series s = ClickedSeries(p); + if (s != null) + { + var tolerance = Series != null ? Series.PointerSize : 3; + int abovePoint = TeeChartHelper.GetNearestPoint(s, p, tolerance); + if (abovePoint > -1) + { + c = GetCursorIcon(style); + Chart.CancelMouse = true; + + if (MouseHoverPoint != null) + { + MouseHoverPoint(this, new HoverPointEventArgs(GetChartSeriesFromInternalSeries(s), abovePoint, 0.0, 0.0, true)); + } + } + } + else + { + c = Cursors.Default; + + if (MouseHoverPoint != null) + { + MouseHoverPoint(this, new HoverPointEventArgs(null, -1, 0.0, 0.0, false)); + } + } + + if (Steema.TeeChart.Utils.GetMouseButton(e) != MouseButtons.Left) + { + return; + } + + if (SelectedPointIndex > -1) + { + if ((style == DragStyle.X) || (style == DragStyle.Both)) + { + if (LastSelectedSeries != null) + { + LastSelectedSeries.XValues[SelectedPointIndex] = CalculateXValue(p); + } + pointHasMoved = true; + } + + if ((style == DragStyle.Y) || (style == DragStyle.Both)) + { + if (LastSelectedSeries != null) + { + LastSelectedSeries.YValues[SelectedPointIndex] = CalculateYValue(p); + } + pointHasMoved = true; + } + if (IsPolygon) + { + SynchronizeFirstAndLastPointOfPolygon(); + } + + if (pointHasMoved) + { + Invalidate(); + } + } + } + + protected override void OnMouseUp(MouseEventArgs e) + { + if (Steema.TeeChart.Utils.GetMouseButton(e) != MouseButtons.Left) + { + return; + } + + if (SelectedPointIndex > -1) + { + //int selectedIndex = SelectedPointIndex; + if (pointHasMoved) + { + if (AfterPointEdit != null) + { + // AfterPointEdit can change underlying and reset the SelectedPointIndex + // MouseMove and MouseUp can run out of sync; return the values set by mousemove = what is visible to the user + var chartSeries = GetChartSeriesFromInternalSeries(LastSelectedSeries); + AfterPointEdit(this, new PointEventArgs(chartSeries, SelectedPointIndex, + LastSelectedSeries.XValues[selectedPointIndex], LastSelectedSeries.YValues[selectedPointIndex])); + if (IsPolygon) + { + if (0 == SelectedPointIndex) + { + if (LastSelectedSeries != null) + { + AfterPointEdit(this, new PointEventArgs(chartSeries, LastSelectedSeries.XValues.Count - 1, + LastSelectedSeries.XValues[selectedPointIndex], LastSelectedSeries.YValues[selectedPointIndex])); + } + } + else if (LastSelectedSeries != null) + { + if ((LastSelectedSeries.XValues.Count - 1) == SelectedPointIndex) + { + AfterPointEdit(this, new PointEventArgs(chartSeries, 0, LastSelectedSeries.XValues[selectedPointIndex], LastSelectedSeries.YValues[selectedPointIndex])); + } + } + } + } + pointHasMoved = false; + } + + Chart.Zoom.Active = restoreZoom; + Chart.Zoom.Allow = true; + + //hide tooltip + if (tChart != null) + { + toolTip.ShowAlways = false; + toolTip.Hide(tChart); + } + selectedPointIndex = -1; + } + } + + protected override void OnCustomSeriesGetPointerStyle(CustomPoint series, GetPointerStyleEventArgs e) + { + if (e.ValueIndex == selectedPointIndex) + { + e.Color = selectedPointerColor; + e.Style = selectedPointerStyle; + } + } + + private static Cursor GetCursorIcon(DragStyle dragStyle) + { + switch (dragStyle) + { + case DragStyle.Both: + return Cursors.SizeAll; + case DragStyle.X: + return Cursors.SizeWE; + case DragStyle.Y: + return Cursors.SizeNS; + default: + throw new NotImplementedException(String.Format(Resources.EditPointTool_GetCursorIcon_No_cursor_assigned_for_0_, dragStyle)); + } + } + + private double CalculateXValue(Point P) + { + double xValue = LastSelectedSeries.XScreenToValue(P.X); + //log.DebugFormat("Clicked at point (x): {0}", xValue); + if (!IsPolygon && ClipXValues) + { + if (SelectedPointIndex > 0) + { + //do not allow to horizontally drag before a neigbouring point. + double lowerLimit = LastSelectedSeries.XValues[SelectedPointIndex - 1]; + if (xValue < lowerLimit) + { + log.DebugFormat(Resources.EditPointTool_CalculateXValue_left_limit, xValue, lowerLimit + clippingTolerance); + xValue = lowerLimit + clippingTolerance; + } + } + if (SelectedPointIndex < LastSelectedSeries.Count - 1) + { + //do not allow to horizontally drag past a neigbouring point. + double upperLimit = LastSelectedSeries.XValues[SelectedPointIndex + 1]; + if (xValue > upperLimit) + { + log.DebugFormat(Resources.EditPointTool_CalculateXValue_right_limit, xValue, upperLimit - clippingTolerance); + xValue = upperLimit - clippingTolerance; + } + } + } + return xValue; + } + + private double CalculateYValue(Point P) + { + double yValue = LastSelectedSeries.YScreenToValue(P.Y); + + if (!IsPolygon && ClipYValues) + { + //do not allow to vertical drag beyond a neigbouring point. + + double current = LastSelectedSeries.YValues[SelectedPointIndex]; + var limits = new List(); + + if (SelectedPointIndex > 0) + { + limits.Add(LastSelectedSeries.YValues[SelectedPointIndex - 1]); + } + if (SelectedPointIndex < LastSelectedSeries.Count - 1) + { + limits.Add(LastSelectedSeries.YValues[SelectedPointIndex + 1]); + } + + return Clip(yValue, current, clippingTolerance, limits); + } + return yValue; + } + + private static double Clip(double newValue, double oldValue, double margin, IList limits) + { + foreach (double limit in limits) //note: should these be ordered? + { + if (limit.IsInRange(newValue, oldValue)) + { + return oldValue < limit ? limit - margin : limit + margin; + } + } + + return newValue; + } + + private void SynchronizeFirstAndLastPointOfPolygon() + { + //make sure that the last point is equal to the first. If we moved the first we should move the last and vice-versa + if (0 == SelectedPointIndex) + { + LastSelectedSeries.XValues[LastSelectedSeries.XValues.Count - 1] = LastSelectedSeries.XValues[0]; + LastSelectedSeries.YValues[LastSelectedSeries.YValues.Count - 1] = LastSelectedSeries.YValues[0]; + } + else if ((LastSelectedSeries.XValues.Count - 1) == SelectedPointIndex) + { + LastSelectedSeries.XValues[0] = LastSelectedSeries.XValues[LastSelectedSeries.XValues.Count - 1]; + LastSelectedSeries.YValues[0] = LastSelectedSeries.YValues[LastSelectedSeries.YValues.Count - 1]; + } + } + } + + public enum DragStyle + { + Both, + X, + Y + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/ExportChartAsImageChartTool.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/ExportChartAsImageChartTool.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/ExportChartAsImageChartTool.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,47 @@ +using System; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Properties; + +namespace Core.Common.Controls.Charting.Tools +{ + public class ExportChartAsImageChartTool : IChartViewContextMenuTool + { + public event EventHandler ActiveChanged; + private bool active; + + public IChartView ChartView { get; set; } + + public bool Active + { + get + { + return active; + } + set + { + active = value; + if (ActiveChanged != null) + { + ActiveChanged(this, null); + } + } + } + + public bool Enabled { get; set; } + + public void OnBeforeContextMenu(ContextMenuStrip menu) + { + if (menu.Items.Count > 0) + { + menu.Items.Add(new ToolStripSeparator()); + } + + menu.Items.Add(new ToolStripMenuItem(Resources.KeyEvent_Deletion_not_implemented_for_this_type_of_datasource, null, ExportChartEventHandler)); + } + + private void ExportChartEventHandler(object sender, EventArgs e) + { + ChartView.ExportAsImage(); + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/PointEventArgs.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/PointEventArgs.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/PointEventArgs.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,32 @@ +using System.ComponentModel; + +namespace Core.Common.Controls.Charting.Tools +{ + public delegate void SelectionChangedEventHandler(object sender, PointEventArgs e); + + public class PointEventArgs : CancelEventArgs + { + public PointEventArgs(IChartSeries series, int index, double x, double y) + { + Series = series; + Index = index; + X = x; + Y = y; + } + + public IChartSeries Series { get; private set; } + + public int Index { get; private set; } + + public double X { get; private set; } + + public double Y { get; private set; } + } + + public class HoverPointEventArgs : PointEventArgs + { + public HoverPointEventArgs(IChartSeries series, int index, double x, double y, bool entering) : base(series, index, x, y) {} + + public bool Entering { get; set; } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/RulerTool.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/RulerTool.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/RulerTool.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,444 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Windows.Forms; +using Core.Common.Controls.Charting.Customized; +using Core.Common.Controls.Charting.Properties; +using log4net; +using Steema.TeeChart.Drawing; + +namespace Core.Common.Controls.Charting.Tools +{ + public class RulerTool : IChartViewTool + { + private static readonly ILog Log = LogManager.GetLogger(typeof(RulerTool)); + private readonly RingtoetsTChart teeChart; + private string toolTip; + private Steema.TeeChart.Chart.ChartToolTip annotationToolTip; + private Point measuringStartPoint; + private Point measuringEndPoint; + private bool measuring; + private object horizontalValueStartPoint; + private object verticalValueStartPoint; + private bool active; + private object verticalValueEndPoint; + private object horizontalValueEndPoint; + private bool showToolTip; + private Point lastShowPoint; + private Cursor cursor; + private bool selectToolOldState = true; + + public RulerTool(RingtoetsTChart teeChart) + { + var chartView = teeChart.Parent as ChartView; + + this.teeChart = teeChart; + teeChart.AfterDraw += TeeChartAfterDraw; + teeChart.MouseWheel += SizeChanged; + teeChart.MouseDown += MouseDown; + teeChart.MouseMove += MouseMove; + + if (chartView != null) + { + chartView.GraphResized += SizeChanged; + chartView.ViewPortChanged += SizeChanged; + } + + LineColor = Color.DarkSlateGray; + LineWidth = 2; + DashStyle = DashStyle.Dash; + } + + private void StartMeasuring(Point point) + { + var chartView = teeChart.Parent as ChartView; + if (chartView == null) + { + return; + } + + var bottomMinValue = chartView.Chart.BottomAxis.Minimum; + var bottomMaxValue = chartView.Chart.BottomAxis.Maximum; + var leftMinValue = chartView.Chart.LeftAxis.Minimum; + var leftMaxValue = chartView.Chart.LeftAxis.Maximum; + + measuringStartPoint = point; + + var chartRect = teeChart.Chart.ChartRect; + + horizontalValueStartPoint = GetAxesValue(bottomMinValue, + bottomMaxValue, + chartRect.Left, + (chartRect.Left + chartRect.Width), + point.X); + verticalValueStartPoint = GetAxesValue(leftMinValue, + leftMaxValue, + (chartRect.Top + chartRect.Height), + chartRect.Top, + point.Y); + horizontalValueEndPoint = double.NaN; + verticalValueEndPoint = double.NaN; + + measuringEndPoint = measuringStartPoint; + RemoveToolTip(); + measuring = true; + } + + private void StopMeasuring(Point point) + { + var chartView = teeChart.Parent as ChartView; + if (chartView == null) + { + return; + } + + var bottomMinValue = chartView.Chart.BottomAxis.Minimum; + var bottomMaxValue = chartView.Chart.BottomAxis.Maximum; + var leftMinValue = chartView.Chart.LeftAxis.Minimum; + var leftMaxValue = chartView.Chart.LeftAxis.Maximum; + + var chartRect = teeChart.Chart.ChartRect; + + horizontalValueEndPoint = GetAxesValue(bottomMinValue, + bottomMaxValue, + chartRect.Left, + (chartRect.Left + chartRect.Width), + point.X); + verticalValueEndPoint = GetAxesValue(leftMinValue, + leftMaxValue, + (chartRect.Top + chartRect.Height), + chartRect.Top, + point.Y); + + var dx = teeChart.Chart.Axes.Bottom.IsDateTime + ? (object) + (DateTime.FromOADate((double) horizontalValueEndPoint) - + DateTime.FromOADate((double) horizontalValueStartPoint)) + : (double) horizontalValueEndPoint - (double) horizontalValueStartPoint; + var dy = teeChart.Chart.Axes.Left.IsDateTime + ? (object) + (DateTime.FromOADate((double) verticalValueEndPoint) - DateTime.FromOADate((double) verticalValueStartPoint)) + : (double) verticalValueEndPoint - (double) verticalValueStartPoint; + toolTip = DifferenceToString != null ? DifferenceToString(dx, dy) : GetDefaultDifferenceString(dx, dy); + showToolTip = true; + + RemoveToolTip(); + lastShowPoint = new Point(measuringEndPoint.X - 20, measuringEndPoint.Y - 20); + AddToolTip(point); + AddLogMessage(); + + measuring = false; + } + + private void TeeChartAfterDraw(object sender, Graphics3D graphics3D) + { + if (Active && measuringStartPoint != measuringEndPoint && ChartView.Chart.Series.Any(s => s.Visible)) + { + if (measuringStartPoint.X == int.MinValue || measuringStartPoint.Y == int.MinValue || measuringEndPoint.X == int.MinValue || measuringEndPoint.Y == int.MinValue) + { + return; + } + using (Graphics3D gr = teeChart.Graphics3D) + { + gr.Pen = new ChartPen(teeChart.Chart, LineColor) + { + Width = LineWidth, Style = DashStyle + }; + gr.ClipRectangle(teeChart.Chart.ChartRect); + gr.Line(measuringStartPoint, measuringEndPoint); + } + } + } + + private void AddToolTip(Point point) + { + if (!showToolTip) + { + return; + } + + var distanceToLine = LineToPointDistance2D(measuringStartPoint, measuringEndPoint, point, true); + if (!string.IsNullOrEmpty(toolTip) && distanceToLine < 5 && Distance(point, lastShowPoint) > 5) + { + lastShowPoint = point; + annotationToolTip = teeChart.Chart.ToolTip; + annotationToolTip.Text = toolTip; + annotationToolTip.Show(); + } + } + + private void RemoveToolTip() + { + if (annotationToolTip != null) + { + annotationToolTip.Hide(); + annotationToolTip = null; + showToolTip = false; + toolTip = ""; + } + } + + private void AddLogMessage() + { + Log.Info(toolTip); + } + + private string GetDefaultDifferenceString(object dx, object dy) + { + var dxText = dx is TimeSpan + ? ((TimeSpan) dx).TotalSeconds.ToString("0.## ") + Resources.RulerTool_GetDefaultDifferenceString_seconds + : ((double) dx).ToString("0.##"); + var dyText = dy is TimeSpan + ? ((TimeSpan) dy).TotalSeconds.ToString("0.## ") + Resources.RulerTool_GetDefaultDifferenceString_seconds + : ((double) dy).ToString("0.##"); + + return string.Format(Resources.RulerTool_GetDefaultDifferenceString_Difference_horizontal_0_vertical_1_, dxText, dyText); + } + + private object GetAxesValue(double minValue, double maxValue, int minPixel, int maxPixel, int pixel) + { + return minValue + ((maxValue - minValue)/(maxPixel - minPixel))*(pixel - minPixel); + } + + private int GetPixelValue(double minValue, double maxValue, int minPixel, int maxPixel, double value) + { + return (int) (minPixel + ((maxPixel - minPixel)/(maxValue - minValue))*(value - minValue)); + } + + # region IChartViewTool members + + public IChartView ChartView { get; set; } + + public event EventHandler ActiveChanged; + + public bool Active + { + get + { + return active; + } + set + { + if (active == value) + { + return; + } + active = value; + SetActive(); + if (ActiveChanged != null) + { + ActiveChanged(this, null); + } + } + } + + private void SetActive() + { + measuring = false; + teeChart.Chart.Zoom.Allow = !active; + RemoveToolTip(); + teeChart.Chart.Invalidate(); + + var selectTool = ChartView.GetTool(); + if (active) + { + measuringStartPoint = new Point(); + measuringEndPoint = new Point(); + horizontalValueStartPoint = double.NaN; + verticalValueStartPoint = double.NaN; + horizontalValueEndPoint = double.NaN; + verticalValueEndPoint = double.NaN; + if (selectTool != null) + { + selectToolOldState = selectTool.Active; + selectTool.Active = false; + } + } + else + { + if (selectTool != null) + { + selectTool.Active = selectToolOldState; + } + } + teeChart.Cursor = active ? Cursor : Cursors.Default; + } + + public bool Enabled { get; set; } + + # endregion + + # region IRulerTool members + + public int LineWidth { get; set; } + + public DashStyle DashStyle { get; set; } + + public Color LineColor { get; set; } + + public Cursor Cursor + { + get + { + return cursor ?? Cursors.Default; + } + set + { + cursor = value; + teeChart.Cursor = active ? Cursor : Cursors.Default; + } + } + + public Func DifferenceToString { get; set; } + + public void Cancel() + { + Active = false; + } + + # endregion + + # region event handlers + + private void MouseMove(object sender, MouseEventArgs e) + { + var currentPoint = new Point(e.X, e.Y); + if (Active && measuring) + { + // Local copy to prevent compiler warning CS1690 (https://msdn.microsoft.com/en-us/library/x524dkh4.aspx) + var chartRect = teeChart.Chart.ChartRect; + if (chartRect.Contains(currentPoint)) + { + measuringEndPoint = currentPoint; + } + teeChart.Chart.Invalidate(); + } + + AddToolTip(currentPoint); + } + + private void MouseDown(object sender, MouseEventArgs e) + { + if (Active && Steema.TeeChart.Utils.GetMouseButton(e) == MouseButtons.Left) + { + var clickedPoint = new Point(e.X, e.Y); + // Local copy to prevent compiler warning CS1690 (https://msdn.microsoft.com/en-us/library/x524dkh4.aspx) + var chartRect = teeChart.Chart.ChartRect; + if (chartRect.Contains(clickedPoint)) + { + if (!measuring) + { + StartMeasuring(clickedPoint); + } + else + { + StopMeasuring(clickedPoint); + } + } + } + } + + private void SizeChanged(object sender, EventArgs e) + { + var chartView = teeChart.Parent as ChartView; + if (chartView == null) + { + return; + } + + var bottomMinValue = chartView.Chart.BottomAxis.Minimum; + var bottomMaxValue = chartView.Chart.BottomAxis.Maximum; + var leftMinValue = chartView.Chart.LeftAxis.Minimum; + var leftMaxValue = chartView.Chart.LeftAxis.Maximum; + + var chartRect = teeChart.Chart.ChartRect; + + if (horizontalValueStartPoint != null && !double.IsNaN((double) horizontalValueStartPoint)) + { + var pixelValueStartX = GetPixelValue(bottomMinValue, bottomMaxValue, + chartRect.Left, + (chartRect.Left + chartRect.Width), + (double) horizontalValueStartPoint); + var pixelValueStartY = GetPixelValue(leftMinValue, leftMaxValue, + (chartRect.Top + chartRect.Height), + chartRect.Top, + (double) verticalValueStartPoint); + measuringStartPoint = new Point(pixelValueStartX, pixelValueStartY); + } + if (horizontalValueEndPoint != null && !double.IsNaN((double) horizontalValueEndPoint)) + { + var pixelValueEndX = GetPixelValue(bottomMinValue, bottomMaxValue, + chartRect.Left, + (chartRect.Left + chartRect.Width), + (double) horizontalValueEndPoint); + var pixelValueEndY = GetPixelValue(leftMinValue, leftMaxValue, + (chartRect.Top + chartRect.Height), + chartRect.Top, + (double) verticalValueEndPoint); + measuringEndPoint = new Point(pixelValueEndX, pixelValueEndY); + } + } + + #endregion + + #region Distace calculation + + //Compute the dot product AB . AC + private double DotProduct(Point pointA, Point pointB, Point pointC) + { + var ab = new double[2]; + var bc = new double[2]; + ab[0] = pointB.X - pointA.X; + ab[1] = pointB.Y - pointA.Y; + bc[0] = pointC.X - pointB.X; + bc[1] = pointC.Y - pointB.Y; + + return ab[0]*bc[0] + ab[1]*bc[1]; + } + + //Compute the cross product AB x AC + private double CrossProduct(Point pointA, Point pointB, Point pointC) + { + var ab = new double[2]; + var ac = new double[2]; + ab[0] = pointB.X - pointA.X; + ab[1] = pointB.Y - pointA.Y; + ac[0] = pointC.X - pointA.X; + ac[1] = pointC.Y - pointA.Y; + + return ab[0]*ac[1] - ab[1]*ac[0]; + } + + //Compute the distance from A to B + private static double Distance(Point pointA, Point pointB) + { + double d1 = pointA.X - pointB.X; + double d2 = pointA.Y - pointB.Y; + + return Math.Sqrt(d1*d1 + d2*d2); + } + + //Compute the distance from AB to C + //if isSegment is true, AB is a segment, not a line. + private double LineToPointDistance2D(Point pointA, Point pointB, Point pointC, bool isSegment) + { + var dist = CrossProduct(pointA, pointB, pointC)/Distance(pointA, pointB); + if (isSegment) + { + if (DotProduct(pointA, pointB, pointC) > 0) + { + return Distance(pointB, pointC); + } + + if (DotProduct(pointB, pointA, pointC) > 0) + { + return Distance(pointA, pointC); + } + } + return Math.Abs(dist); + } + + #endregion + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/SelectPointTool.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/SelectPointTool.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/SelectPointTool.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,491 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; + +using Core.Common.Controls.Charting.Series; +using Core.Common.Controls.Charting.Properties; + +using Steema.TeeChart; +using Steema.TeeChart.Drawing; +using Steema.TeeChart.Styles; +using Steema.TeeChart.Tools; + +namespace Core.Common.Controls.Charting.Tools +{ + /// + /// Describes the possible values of the property. + /// + public enum NearestPointStyles + { + /// + /// No shape is drawn. + /// + None, + + /// + /// Shape is a circle. + /// + Circle, + + /// + /// Shape is a rectangle. + /// + Rectangle, + + /// + /// Shape is a diamond. + /// + Diamond + }; + + public class SelectPoint + { + internal SelectPoint(Steema.TeeChart.Styles.Series series, int pointIndex) + { + Series = series; + PointIndex = pointIndex; + } + + /// + /// The index of the point in the datasource. + /// + public int PointIndex { get; private set; } + + internal Steema.TeeChart.Styles.Series Series { get; private set; } + } + + /// + /// Interactive selection of points of a specific series on the chart. + /// + public class SelectPointTool : ChartViewSeriesToolBase + { + public event EventHandler SelectedChartPointsChanged; + private readonly ICollection selectedPoints = new HashSet(); + + private readonly bool dragging = false; + private readonly int Point = -1; + private readonly Steema.TeeChart.Styles.PointerStyles selectedPointerStyle = Steema.TeeChart.Styles.PointerStyles.Diamond; + private bool drawLine = true; + private bool fullRepaint = true; + private Point mouseLocation; + + private Color selectedPointerColor = SystemColors.Highlight; + + private int size = 10; + private NearestPointStyles style = NearestPointStyles.Circle; + + internal SelectPointTool(Steema.TeeChart.Chart c) : base(c) + { + Pen.Style = DashStyle.Dot; + Pen.Color = Color.White; + HandleDelete = true; + } + + /// + /// Gets descriptive text. + /// + public override string Description + { + get + { + return Texts.NearestTool; + } + } + + /// + /// Gets detailed descriptive text. + /// + public override string Summary + { + get + { + return Texts.NearestPointSummary; + } + } + + /// + /// Draws a temporary line from the mouse coordinates to the nearest point. + /// + public bool DrawLine + { + get + { + return drawLine; + } + set + { + SetBooleanProperty(ref drawLine, value); + } + } + + /// + /// Allows the whole Parent Chart to repainted when true. + /// + public bool FullRepaint + { + get + { + return fullRepaint; + } + set + { + SetBooleanProperty(ref fullRepaint, value); + } + } + + /// + /// Determines whether the selectTool should delete point when DEL is pressed + /// + public bool HandleDelete { get; set; } + + /// + /// Color of the point selected in the chart + /// + public Color SelectedPointerColor + { + get + { + return selectedPointerColor; + } + set + { + selectedPointerColor = value; + } + } + + /// + /// Defines the Size of the NearestTool shape. + /// + public int Size + { + get + { + return size; + } + set + { + SetIntegerProperty(ref size, value); + } + } + + /// + /// Sets the shape of the NearestTool. + /// + public NearestPointStyles Style + { + get + { + return style; + } + set + { + if (style != value) + { + style = value; + Invalidate(); + } + } + } + + public Cursor Cursor { get; set; } + + /// + /// Add point a pointIndex for series to selectedPoints + /// + /// + /// + public void AddPointAtIndexToSelection(IChartSeries series, int pointIndex) + { + var teeChartSeries = ((ChartSeries)series).series; + SelectChartPoint(teeChartSeries, pointIndex); + } + + public void ClearSelection() + { + selectedPoints.Clear(); + FireSelectedChartPointsChangedEvent(); + SelectedPointIndex = -1; + } + + protected override void OnCustomSeriesGetPointerStyle(CustomPoint series, GetPointerStyleEventArgs e) + { + if (e.ValueIndex == selectedPointIndex) + { + e.Color = selectedPointerColor; + e.Style = selectedPointerStyle; + } + } + + protected override void Assign(Tool t) + { + base.Assign(t); + SelectPointTool tmp = t as SelectPointTool; + tmp.Brush = Brush.Clone() as ChartBrush; + tmp.DrawLine = DrawLine; + tmp.FullRepaint = FullRepaint; + tmp.Pen = Pen.Clone() as ChartPen; + tmp.Style = Style; + } + + protected override void ChartEvent(EventArgs e) + { + base.ChartEvent(e); + if (e is AfterDrawEventArgs) + { + PaintHint(); + } + } + + protected override void KeyEvent(KeyEventArgs e) + { + base.KeyEvent(e); + + if (HandleDelete && (e.KeyCode == Keys.Delete) && (selectedPointIndex != -1) && (null != iSeries)) + { + // try to delete point from datasource + DataTable dataTable = iSeries.DataSource as DataTable; + if (dataTable != null) + { + dataTable.Rows[selectedPointIndex].Delete(); + } + else + { + throw new NotImplementedException(Resources.KeyEvent_Deletion_not_implemented_for_this_type_of_datasource); + } + + //// delete selected point from the series + //Series.Delete(selectedPointIndex); + //selectedPointIndex = -1; + //// todo make sure event is fired, either from tool or series object. + } + } + + protected override void OnMouseUp(MouseEventArgs e) + { + ClearSelection(); + AddClickedPoint(e.X, e.Y); + Invalidate(); + } + + protected override void OnMouseMove(MouseEventArgs e, ref Cursor c) + { + Point P = new Point(e.X, e.Y); + if (!dragging) + { + Steema.TeeChart.Styles.Series s = ClickedSeries(P); + + if (s != null) + { + int abovePoint = s.Clicked(P); + if (abovePoint > -1) + { + c = Cursor ?? Cursors.Cross; + //new Cursor(Assembly.GetExecutingAssembly().GetManifestResourceStream("Steema.TeeChart.Cursors.moveTracker.cur")); + } + Chart.CancelMouse = true; + } + else + { + c = Cursors.Default; + } + } + } + + /// + /// Element Pen characteristics. + /// + private ChartPen Pen + { + get + { + if (pPen == null) + { + pPen = new ChartPen(Chart, Color.Blue); + } + return pPen; + } + set + { + pPen = value; + } + } + + /// + /// Element Brush characteristics. + /// + private ChartBrush Brush + { + get + { + return bBrush ?? (bBrush = new ChartBrush(Chart, Color.HotPink, false)); + } + set + { + bBrush = value; + } + } + + private void SelectChartPoint(Steema.TeeChart.Styles.Series teeChartSeries, int pointIndex) + { + if (!selectedPoints.Any(p => p.Series == teeChartSeries && p.PointIndex == pointIndex)) + { + var selectPoint = new SelectPoint(teeChartSeries, pointIndex); + selectedPoints.Add(selectPoint); + FireSelectedChartPointsChangedEvent(); + } + } + + private void FireSelectedChartPointsChangedEvent() + { + if (SelectedChartPointsChanged != null) + { + SelectedChartPointsChanged(this, EventArgs.Empty); + } + } + + private void PaintHint() + { + if (selectedPoints.Count > 0) + { + var brush = new SolidBrush(selectedPointerColor); + Graphics3D g = Chart.Graphics3D; + foreach (var point in selectedPoints) + { + //the points might have been deleted (multi threading crap) + if (point.PointIndex > point.Series.XValues.Count - 1) + { + return; + } + + //no selection, don't draw + if (point.PointIndex < 0) + { + continue; + } + + int x = point.Series.CalcXPos(point.PointIndex); + int y = point.Series.CalcYPos(point.PointIndex); + + // Local copy to prevent compiler warning CS1690 (https://msdn.microsoft.com/en-us/library/x524dkh4.aspx) + var chartRect = Chart.ChartRect; + if (!chartRect.Contains(x, y)) //outside chart area + { + continue; + } + + g.Pen = Pen; + g.Brush = Brush; + + g.FillRectangle(brush, (int)(x - size * 0.5), (int)(y - size * 0.5), size, size); + + /* + + g.Rectangle(new Rectangle(2, 2, totalWidth - 1, totaHeight - 1)); + + g.FillRectangle(brush, new Rectangle(2, 2, width, height)); + g.Rectangle(pen, new Rectangle(2, 2, width - 1, height - 1)); + + g.FillRectangle(brush, new Rectangle(totalWidth - width + 2, 2, width, height)); + g.Rectangle(pen, new Rectangle(totalWidth - width + 2, 2, width - 1, height - 1)); + + g.FillRectangle(brush, new Rectangle(totalWidth - width + 2, totaHeight - height + 2, width, height)); + g.Rectangle(pen, new Rectangle(totalWidth - width + 2, totaHeight - height + 2, width - 1, height - 1)); + + g.FillRectangle(brush, new Rectangle(2, totaHeight - height + 2, width, height)); + g.Rectangle(pen, new Rectangle(2, totaHeight - height + 2, width - 1, height - 1)); +*/ + } + brush.Dispose(); + //g.Dispose(); + } + if ((iSeries != null) && (Point != -1)) + { + Graphics3D g = Chart.Graphics3D; + g.Pen = Pen; + + int x = iSeries.CalcXPos(Point); + int y = iSeries.CalcYPos(Point); + + if (style != NearestPointStyles.None) + { + g.Brush = Brush; + Rectangle r = Rectangle.FromLTRB(x - size, y - size, x + size, y + size); + switch (style) + { + case NearestPointStyles.Circle: + if (Chart.Aspect.View3D) + { + g.Ellipse(r, iSeries.StartZ); + } + else + { + g.Ellipse(r); + } + break; + + case NearestPointStyles.Rectangle: + { + if (Chart.Aspect.View3D) + { + g.Rectangle(r, iSeries.StartZ); + } + else + { + g.Rectangle(r); + } + } + break; + + case NearestPointStyles.Diamond: + { + Point[] P = new Point[4]; + P[0] = new Point(x, y - size); + P[1] = new Point(x + size, y); + P[2] = new Point(x, y + size); + P[3] = new Point(x - size, y); + g.Polygon(iSeries.StartZ, P); + } + break; + } + } + + if (drawLine) + { + g.Pen.Style = DashStyle.Solid; + g.MoveTo(mouseLocation); + g.LineTo(x, y); + } + } + } + + private void AddClickedPoint(int x, int y) + { + var mouseLocation = new Point(x, y); + //don't remove this..otherwise firstlastvisibleindex will be -1 so the selecttool doesn't work + if (iSeries != null) + { + //// iSeries.CalcFirstLastVisibleIndex is changed from public to internal and we do not want to modify TeeChart + MethodInfo calcFirstLastVisibleIndex = iSeries.GetType().GetMethod("CalcFirstLastVisibleIndex", BindingFlags.NonPublic | BindingFlags.Instance); + calcFirstLastVisibleIndex.Invoke(iSeries, new object[] + {}); + } + + var clickedSeries = ClickedSeries(mouseLocation); + if (clickedSeries != null) + { + LastSelectedSeries = clickedSeries; + + SelectedPointIndex = TeeChartHelper.GetNearestPoint(clickedSeries, mouseLocation, 4); + if (SelectedPointIndex > -1) + { + SelectChartPoint(clickedSeries, SelectedPointIndex); + } + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/Tools/ZoomUsingMouseWheelTool.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/Tools/ZoomUsingMouseWheelTool.cs (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/Tools/ZoomUsingMouseWheelTool.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,83 @@ +using System.Windows.Forms; +using Steema.TeeChart; +using Steema.TeeChart.Tools; + +namespace Core.Common.Controls.Charting.Tools +{ + /// + /// Zooms in / out on mouse wheel. + /// + public class ZoomUsingMouseWheelTool : ToolSeries + { + /// + /// default constructor + /// + /// + public ZoomUsingMouseWheelTool(Steema.TeeChart.Chart chart) : base(chart) {} + + /// + /// check wether mousewheel is used together with ctrl button + /// + /// + /// + /// + protected override void MouseEvent(MouseEventKinds kind, MouseEventArgs e, ref Cursor c) + { + /* if(kind==MouseEventKinds.Up) + { + chart.Axes.Left.Automatic = true; + chart.Axes.Bottom.Automatic = true; + }*/ + if ((Control.ModifierKeys & Keys.Control) == Keys.Control && kind == MouseEventKinds.Wheel) + { + //TODO fix zoom behavior: zooming out does not work as expected. + //this.chart.Zoom.ZoomPercent(100 + e.Delta/20); + + var zoomFraction = (100.0 + e.Delta/20.0)/100.0; + + var xmin = Chart.Axes.Bottom.Minimum; + var xmax = Chart.Axes.Bottom.Maximum; + var ymin = Chart.Axes.Left.Minimum; + var ymax = Chart.Axes.Left.Maximum; + + //center of map + //var xcenter = xmin + (xmax - xmin) / 2; + //var ycenter = ymin + (ymax - ymin) / 2; + + ////retrieve series and calculate mouseposition expressed in world coordinates + //Steema.TeeChart.Styles.Series theSeries = null; + //foreach (Steema.TeeChart.Styles.Series s in chart.Series) + //{ + // if (s.Active) + // { + // theSeries = s; + // break; + // } + //} + //if (theSeries ==null ) return; + + //var xNewCenter = theSeries.XScreenToValue(e.X); + //var yNewCenter = theSeries.YScreenToValue(e.Y); + + ////move to new center + //xmin += xNewCenter - xcenter; + //xmax += xNewCenter - xcenter; + //ymin += yNewCenter - ycenter; + //ymax += yNewCenter - ycenter; + + var d2x = (xmax - xmin)*(1 - 1/zoomFraction); + var d2y = (ymax - ymin)*(1 - 1/zoomFraction); + + Chart.Axes.Left.SetMinMax(ymin + d2y/2, ymax - d2y/2); + Chart.Axes.Bottom.SetMinMax(xmin + d2x/2, xmax - d2x/2); + +/* + chart.Axes.Bottom.Minimum = xmin + d2x / 2; + chart.Axes.Bottom.Maximum = xmax - d2x / 2; + chart.Axes.Left.Minimum = ymin + d2y / 2; + chart.Axes.Left.Maximum = ymax - d2y / 2; +*/ + } + } + } +} \ No newline at end of file Index: Core/Common/src/Core.Common.Controls.Charting/packages.config =================================================================== diff -u --- Core/Common/src/Core.Common.Controls.Charting/packages.config (revision 0) +++ Core/Common/src/Core.Common.Controls.Charting/packages.config (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,4 @@ + + + + \ No newline at end of file Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Chart.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartAxis.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartCoordinateService.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartGraphics.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartLegend.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartStyleHelper.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartView.Designer.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartView.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/ChartView.resx'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Customized/RingtoetsTChart.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Enumerators.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChart.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartAxis.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartLegend.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartView.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartViewContextMenuTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartViewSeriesTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/IChartViewTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/InterpolationType.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/AreaChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/BarSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/ChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/ChartSeriesFactory.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/ChartSeriesType.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/IAreaChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/ILineChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/IPointChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/IPolygonChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/LineChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/PointChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/PolygonChartSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Series/PolygonSeries.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/TeeChartHelper.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/TimeNavigatableLabelFormatProvider.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/AddPointTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/ChartViewSeriesToolBase.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/EditPointTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/ExportChartAsImageChartTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/PointEventArgs.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/RulerTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/SelectPointTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 3bfa4dc5fb5ea3560752479de86cb843419f8fe3 refers to a dead (removed) revision in file `Core/Common/src/Core.Common.Controls.Swf/Charting/Tools/ZoomUsingMouseWheelTool.cs'. Fisheye: No comparison available. Pass `N' to diff? Index: Core/Common/src/Core.Common.Controls.Swf/Core.Common.Controls.Swf.csproj =================================================================== diff -u -r73d88cd5f6df8e9a3d47808ecd588ae42591c4d8 -r3bfa4dc5fb5ea3560752479de86cb843419f8fe3 --- Core/Common/src/Core.Common.Controls.Swf/Core.Common.Controls.Swf.csproj (.../Core.Common.Controls.Swf.csproj) (revision 73d88cd5f6df8e9a3d47808ecd588ae42591c4d8) +++ Core/Common/src/Core.Common.Controls.Swf/Core.Common.Controls.Swf.csproj (.../Core.Common.Controls.Swf.csproj) (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -108,66 +108,6 @@ Properties\GlobalAssembly.cs - - - - - - - Component - - - - - - - - - - Component - - - - - - UserControl - - - ChartView.cs - - - - - - - - - - - Component - - - - - - - - - - - - Component - - - Component - - - - Component - - - Component - Component @@ -243,10 +183,6 @@ - - ChartView.cs - Designer - FindAndReplaceControl.cs Index: Core/Common/test/Core.Common.Controls.Charting.Test/ChartTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Controls.Charting.Test/ChartTest.cs (revision 0) +++ Core/Common/test/Core.Common.Controls.Charting.Test/ChartTest.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,101 @@ +using System; +using System.IO; +using Core.Common.Controls.Charting.Series; +using Core.Common.TestUtil; +using NUnit.Framework; + +namespace Core.Common.Controls.Charting.Test +{ + [TestFixture] + public class ChartTest + { + [Test] + public void ChartReferenceSetToChartSeriesAfterAddingItToChart() + { + var chart = new Chart(); + var chartSeries = new AreaChartSeries(); + + Assert.IsNull(chartSeries.Chart); + + chart.AddChartSeries(chartSeries); + + Assert.AreSame(chart, chartSeries.Chart); + } + + [Test] + public void ExportAsImageWorks() + { + SaveDeleteAndAssertExport("test.png"); + } + + [Test] + public void ExportAsVectorGraphicsImageWorks() + { + SaveDeleteAndAssertExport("test.svg"); + } + + [Test] + public void ExportAsVectorGraphicsImagesGivesWarningForIgnoringHatchStyle() + { + var chart = new Chart(); + var areaSeries = new AreaChartSeries + { + UseHatch = true + }; + chart.AddChartSeries(areaSeries); + + TestHelper.AssertLogMessageIsGenerated(() => SaveDeleteAndAssertExport("test.svg", chart), "Gearceerde stijl wordt niet ondersteund voor exporteren en zal genegeerd worden.", 1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Argument bevat geen bestandsnaam.\r\nParameter name: filename")] + public void ExportAsImageThrowsOnIncompleteFileName() + { + SaveDeleteAndAssertExport(".noname"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Extensie (.ext) wordt niet ondersteund.\r\nParameter name: filename")] + public void ExportAsImageThrowsOnUnSupportedExtension() + { + SaveDeleteAndAssertExport("incorrect.ext"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Argument kan niet de waarde 'null' hebben.\r\nParameter name: filename")] + public void ExportAsImageThrowsOnNullArgument() + { + SaveDeleteAndAssertExport(null); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Bestandsnaam moet een extensie hebben.\r\nParameter name: filename")] + public void ExportAsImageThrowsOnMissingExtension() + { + SaveDeleteAndAssertExport("noextension"); + } + + private static void SaveDeleteAndAssertExport(string exportFileName, IChart chart = null) + { + try + { + if (chart == null) + { + new Chart().ExportAsImage(exportFileName, null, null); + } + else + { + chart.ExportAsImage(exportFileName, null, null); + } + Assert.IsTrue(File.Exists(exportFileName)); + } + finally + { + if (File.Exists(exportFileName)) + { + File.Delete(exportFileName); + } + } + } + } +} \ No newline at end of file Index: Core/Common/test/Core.Common.Controls.Charting.Test/ChartViewDateTimeFormatProviderTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Controls.Charting.Test/ChartViewDateTimeFormatProviderTest.cs (revision 0) +++ Core/Common/test/Core.Common.Controls.Charting.Test/ChartViewDateTimeFormatProviderTest.cs (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -0,0 +1,76 @@ +using System; +using System.Globalization; +using System.Threading; + +using NUnit.Framework; + +namespace Core.Common.Controls.Charting.Test +{ + [TestFixture] + public class ChartViewDateTimeFormatProviderTest + { + [Test] + public void ReturnsSingleDayOutputForSingleDayRange() + { + var provider = new TimeNavigatableLabelFormatProvider(); + + var oldCulture = Thread.CurrentThread.CurrentCulture; + var oldCultureUI = Thread.CurrentThread.CurrentUICulture; + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + + provider.CustomDateTimeFormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; + var outputString = provider.GetRangeLabel(new DateTime(2010, 1, 1), new DateTime(2010, 1, 1)); + + Assert.AreEqual("(Friday, 01 January 2010)", outputString, "Datetime formatted output not as expected"); + + Thread.CurrentThread.CurrentCulture = oldCulture; + Thread.CurrentThread.CurrentUICulture = oldCultureUI; + } + + [Test] + [SetCulture("nl-NL")] + public void GetCorrectFormatForRange_forNL() + { + GetCorrectFormatForRange(); + } + + [Test] + [SetCulture("en-US")] + public void GetCorrectFormatForRange_forEN() + { + GetCorrectFormatForRange(); + } + + private static void GetCorrectFormatForRange() + { + // Setup + var provider = new TimeNavigatableLabelFormatProvider(); + + var timeA = new DateTime(2010, 1, 1); + var timeB = new DateTime(2010, 10, 1); + + DateTimeFormatInfo dateTimeFormatInfo = CultureInfo.CurrentCulture.DateTimeFormat; + + string longDatePattern = dateTimeFormatInfo.LongDatePattern; + string shortDateTimePattern = dateTimeFormatInfo.ShortDatePattern + " " + dateTimeFormatInfo.ShortTimePattern; + string yearMonthPattern = dateTimeFormatInfo.YearMonthPattern; + + var a = timeA.ToString(longDatePattern); + var b = timeB.ToString(longDatePattern); + var expectedAnnotation = String.Format("({0} tot {1})", a, b); + + // Call + var annotation = provider.GetRangeLabel(timeA, timeB); + + // Assert + Assert.AreEqual(expectedAnnotation, annotation); + + var label1 = provider.GetLabel(timeA, new TimeSpan(2, 1, 1, 1)); + var label2 = provider.GetLabel(timeA, new TimeSpan(600, 1, 1, 1)); + + Assert.AreEqual(label1, timeA.ToString(shortDateTimePattern)); + Assert.AreEqual(label2, timeA.ToString(yearMonthPattern)); + } + } +} \ No newline at end of file Index: Core/Common/test/Core.Common.Controls.Charting.Test/Core.Common.Controls.Charting.Test.csproj =================================================================== diff -u -r78b769cdeacaa9a56939825e527e7fe71c3a0281 -r3bfa4dc5fb5ea3560752479de86cb843419f8fe3 --- Core/Common/test/Core.Common.Controls.Charting.Test/Core.Common.Controls.Charting.Test.csproj (.../Core.Common.Controls.Charting.Test.csproj) (revision 78b769cdeacaa9a56939825e527e7fe71c3a0281) +++ Core/Common/test/Core.Common.Controls.Charting.Test/Core.Common.Controls.Charting.Test.csproj (.../Core.Common.Controls.Charting.Test.csproj) (revision 3bfa4dc5fb5ea3560752479de86cb843419f8fe3) @@ -38,6 +38,10 @@ true + + ..\..\..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll + True + @@ -47,14 +51,29 @@ + + + + + + + + {43cd4cc3-8500-411b-822e-1d3b5af56fbc} Core.Common.Controls.Charting + + {d749ee4c-ce50-4c17-bf01-9a953028c126} + Core.Common.TestUtil + + + +