// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) 2014 OxyPlot contributors // // // Represents a control that displays a . // // -------------------------------------------------------------------------------------------------------------------- namespace OxyPlot.WindowsForms { using System; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; /// /// Represents a control that displays a . /// [Serializable] public class PlotView : Control, IPlotView { /// /// The category for the properties of this control. /// private const string OxyPlotCategory = "OxyPlot"; /// /// The invalidate lock. /// private readonly object invalidateLock = new object(); /// /// The model lock. /// private readonly object modelLock = new object(); /// /// The rendering lock. /// private readonly object renderingLock = new object(); /// /// The render context. /// private readonly GraphicsRenderContext renderContext; /// /// The tracker label. /// [NonSerialized] private Label trackerLabel; /// /// The current model (holding a reference to this plot view). /// [NonSerialized] private PlotModel currentModel; /// /// The is model invalidated. /// private bool isModelInvalidated; /// /// The model. /// private PlotModel model; /// /// The default controller. /// private IPlotController defaultController; /// /// The update data flag. /// private bool updateDataFlag = true; /// /// The zoom rectangle. /// private Rectangle zoomRectangle; /// /// Initializes a new instance of the class. /// public PlotView() { this.renderContext = new GraphicsRenderContext(); // ReSharper disable DoNotCallOverridableMethodsInConstructor this.DoubleBuffered = true; // ReSharper restore DoNotCallOverridableMethodsInConstructor this.PanCursor = Cursors.Hand; this.ZoomRectangleCursor = Cursors.SizeNWSE; this.ZoomHorizontalCursor = Cursors.SizeWE; this.ZoomVerticalCursor = Cursors.SizeNS; } /// /// Gets the actual model in the view. /// /// /// The actual model. /// Model IView.ActualModel { get { return this.Model; } } /// /// Gets the actual model. /// /// The actual model. public PlotModel ActualModel { get { return this.Model; } } /// /// Gets the actual controller. /// /// /// The actual . /// IController IView.ActualController { get { return this.ActualController; } } /// /// Gets the coordinates of the client area of the view. /// public OxyRect ClientArea { get { return new OxyRect(this.ClientRectangle.Left, this.ClientRectangle.Top, this.ClientRectangle.Width, this.ClientRectangle.Height); } } /// /// Gets the actual plot controller. /// /// The actual plot controller. public IPlotController ActualController { get { return this.Controller ?? (this.defaultController ?? (this.defaultController = new PlotController())); } } /// /// Gets or sets the model. /// [Browsable(false)] [DefaultValue(null)] [Category(OxyPlotCategory)] public PlotModel Model { get { return this.model; } set { if (this.model != value) { this.model = value; this.OnModelChanged(); } } } /// /// Gets or sets the plot controller. /// /// The controller. [Browsable(false)] [DefaultValue(null)] [Category(OxyPlotCategory)] public IPlotController Controller { get; set; } /// /// Gets or sets the pan cursor. /// [Category(OxyPlotCategory)] public Cursor PanCursor { get; set; } /// /// Gets or sets the horizontal zoom cursor. /// [Category(OxyPlotCategory)] public Cursor ZoomHorizontalCursor { get; set; } /// /// Gets or sets the rectangle zoom cursor. /// [Category(OxyPlotCategory)] public Cursor ZoomRectangleCursor { get; set; } /// /// Gets or sets the vertical zoom cursor. /// [Category(OxyPlotCategory)] public Cursor ZoomVerticalCursor { get; set; } /// /// Hides the tracker. /// public void HideTracker() { if (this.trackerLabel != null) { this.trackerLabel.Visible = false; } } /// /// Hides the zoom rectangle. /// public void HideZoomRectangle() { this.zoomRectangle = Rectangle.Empty; this.Invalidate(); } /// /// Invalidates the plot (not blocking the UI thread) /// /// if set to true, all data collections will be updated. public void InvalidatePlot(bool updateData) { lock (this.invalidateLock) { this.isModelInvalidated = true; this.updateDataFlag = this.updateDataFlag || updateData; } this.Invalidate(); } /// /// Called when the Model property has been changed. /// public void OnModelChanged() { lock (this.modelLock) { if (this.currentModel != null) { ((IPlotModel)this.currentModel).AttachPlotView(null); this.currentModel = null; } if (this.Model != null) { ((IPlotModel)this.Model).AttachPlotView(this); this.currentModel = this.Model; } } this.InvalidatePlot(true); } /// /// Sets the cursor type. /// /// The cursor type. public void SetCursorType(CursorType cursorType) { switch (cursorType) { case CursorType.Pan: this.Cursor = this.PanCursor; break; case CursorType.ZoomRectangle: this.Cursor = this.ZoomRectangleCursor; break; case CursorType.ZoomHorizontal: this.Cursor = this.ZoomHorizontalCursor; break; case CursorType.ZoomVertical: this.Cursor = this.ZoomVerticalCursor; break; default: this.Cursor = Cursors.Arrow; break; } } /// /// Shows the tracker. /// /// The data. public void ShowTracker(TrackerHitResult data) { if (this.trackerLabel == null) { this.trackerLabel = new Label { Parent = this, BackColor = Color.LightSkyBlue, AutoSize = true, Padding = new Padding(5) }; } this.trackerLabel.Text = data.ToString(); this.trackerLabel.Top = (int)data.Position.Y - this.trackerLabel.Height; this.trackerLabel.Left = (int)data.Position.X - this.trackerLabel.Width / 2; this.trackerLabel.Visible = true; } /// /// Shows the zoom rectangle. /// /// The rectangle. public void ShowZoomRectangle(OxyRect rectangle) { this.zoomRectangle = new Rectangle((int)rectangle.Left, (int)rectangle.Top, (int)rectangle.Width, (int)rectangle.Height); this.Invalidate(); } /// /// Sets the clipboard text. /// /// The text. public void SetClipboardText(string text) { try { // todo: can't get the following solution to work // http://stackoverflow.com/questions/5707990/requested-clipboard-operation-did-not-succeed Clipboard.SetText(text); } catch (ExternalException ee) { // Requested Clipboard operation did not succeed. MessageBox.Show(this, ee.Message, "OxyPlot"); } } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); this.Focus(); this.Capture = true; this.ActualController.HandleMouseDown(this, e.ToMouseDownEventArgs(GetModifiers())); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); this.ActualController.HandleMouseMove(this, e.ToMouseEventArgs(GetModifiers())); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); this.Capture = false; this.ActualController.HandleMouseUp(this, e.ToMouseUpEventArgs(GetModifiers())); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); this.ActualController.HandleMouseEnter(this, e.ToMouseEventArgs(GetModifiers())); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); this.ActualController.HandleMouseLeave(this, e.ToMouseEventArgs(GetModifiers())); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); this.ActualController.HandleMouseWheel(this, e.ToMouseWheelEventArgs(GetModifiers())); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); try { lock (this.invalidateLock) { if (this.isModelInvalidated) { if (this.model != null) { ((IPlotModel)this.model).Update(this.updateDataFlag); this.updateDataFlag = false; } this.isModelInvalidated = false; } } lock (this.renderingLock) { this.renderContext.SetGraphicsTarget(e.Graphics); if (this.model != null) { if (!this.model.Background.IsUndefined()) { using (var brush = new SolidBrush(this.model.Background.ToColor())) { e.Graphics.FillRectangle(brush, e.ClipRectangle); } } ((IPlotModel)this.model).Render(this.renderContext, this.Width, this.Height); } if (this.zoomRectangle != Rectangle.Empty) { using (var zoomBrush = new SolidBrush(Color.FromArgb(0x40, 0xFF, 0xFF, 0x00))) using (var zoomPen = new Pen(Color.Black)) { zoomPen.DashPattern = new float[] { 3, 1 }; e.Graphics.FillRectangle(zoomBrush, this.zoomRectangle); e.Graphics.DrawRectangle(zoomPen, this.zoomRectangle); } } } } catch (Exception paintException) { var trace = new StackTrace(paintException); Debug.WriteLine(paintException); Debug.WriteLine(trace); using (var font = new Font("Arial", 10)) { e.Graphics.ResetTransform(); e.Graphics.DrawString( "OxyPlot paint exception: " + paintException.Message, font, Brushes.Red, this.Width * 0.5f, this.Height * 0.5f, new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); } } } /// /// Raises the event. /// /// A that contains the event data. protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e) { base.OnPreviewKeyDown(e); var args = new OxyKeyEventArgs { ModifierKeys = GetModifiers(), Key = e.KeyCode.Convert() }; this.ActualController.HandleKeyDown(this, args); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnResize(EventArgs e) { base.OnResize(e); this.InvalidatePlot(false); } /// /// Gets the current modifier keys. /// /// A value. private static OxyModifierKeys GetModifiers() { var modifiers = OxyModifierKeys.None; // ReSharper disable once RedundantNameQualifier if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) { modifiers |= OxyModifierKeys.Shift; } // ReSharper disable once RedundantNameQualifier if ((Control.ModifierKeys & Keys.Control) == Keys.Control) { modifiers |= OxyModifierKeys.Control; } // ReSharper disable once RedundantNameQualifier if ((Control.ModifierKeys & Keys.Alt) == Keys.Alt) { modifiers |= OxyModifierKeys.Alt; } return modifiers; } } }