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) { 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); } } } } }