diff options
Diffstat (limited to 'Software/Visual_Studio/SideChains/RealTimeGraphX.WPF/Surfaces/WpfGraphSurface.cs')
| -rw-r--r-- | Software/Visual_Studio/SideChains/RealTimeGraphX.WPF/Surfaces/WpfGraphSurface.cs | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphX.WPF/Surfaces/WpfGraphSurface.cs b/Software/Visual_Studio/SideChains/RealTimeGraphX.WPF/Surfaces/WpfGraphSurface.cs new file mode 100644 index 000000000..b9e425282 --- /dev/null +++ b/Software/Visual_Studio/SideChains/RealTimeGraphX.WPF/Surfaces/WpfGraphSurface.cs @@ -0,0 +1,434 @@ +using RealTimeGraphX.EventArguments; +using RealTimeGraphX.WPF.DataSeries; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace RealTimeGraphX.WPF.Surfaces +{ + /// <summary> + /// Represents a WPF implementation for <see cref="IGraphSurface{TImage}"/>. + /// This surface expects a graph image of type <see cref="BitmapSource"/>. + /// </summary> + /// <seealso cref="System.Windows.Controls.Control" /> + /// <seealso cref="RealTimeGraphX.IGraphSurface{System.Windows.Media.Imaging.BitmapSource}" /> + public class WpfGraphSurface : Control, IGraphSurface<WpfDataSeries, BitmapSource> + { + private Rectangle _selection_rectangle; + private Canvas _selection_canvas; + private bool _is_selection_mouse_down_zoom; + private bool _is_selection_mouse_down_pan; + private Point _selection_start_point; + private bool _is_scaled; + private Rect _pan_rect; + private Point _current_mouse_position; + private Point _last_mouse_position; + private Grid _grid; + + #region Events + + /// <summary> + /// Occurs when <see cref="IGraphPainter{TImage}"/> has changed. + /// </summary> + public event EventHandler<InputChangedEventArgs<IGraphPainter<WpfDataSeries, BitmapSource>>> InputChanged; + + /// <summary> + /// Occurs when the surface size has changed. + /// </summary> + public event EventHandler<SurfaceSizeChangedEventArgs> SurfaceSizeChanged; + + /// <summary> + /// Occurs when the connected input <see cref="IGraphPainter"/> has changed. + /// </summary> + event EventHandler<InputChangedEventArgs<IGraphPainter>> IGraphInputComponent<IGraphPainter>.InputChanged + { + add + { + InputChanged += new EventHandler<InputChangedEventArgs<IGraphPainter<WpfDataSeries, BitmapSource>>>((sender, e) => { value.Invoke(sender, new InputChangedEventArgs<IGraphPainter>(e.Input)); }); + } + + remove + { + InputChanged -= new EventHandler<InputChangedEventArgs<IGraphPainter<WpfDataSeries, BitmapSource>>>((sender, e) => { value.Invoke(sender, new InputChangedEventArgs<IGraphPainter>(e.Input)); }); + } + } + + #endregion + + #region Properties + + /// <summary> + /// Gets the current graph image. + /// </summary> + public BitmapSource Image + { + get { return (BitmapSource)GetValue(ImageProperty); } + private set { SetValue(ImageProperty, value); } + } + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(BitmapSource), typeof(WpfGraphSurface), new PropertyMetadata(null)); + + private double _surfaceWidth; + /// <summary> + /// Gets the width of the surface. + /// </summary> + public double SurfaceWidth + { + get + { + return _surfaceWidth; + } + private set + { + _surfaceWidth = value; + } + } + + private double _surfaceHeight; + /// <summary> + /// Gets the height of the surface. + /// </summary> + public double SurfaceHeight + { + get + { + return _surfaceHeight; + } + private set + { + _surfaceHeight = value; + } + } + + private IGraphPainter<WpfDataSeries,BitmapSource> _input; + /// <summary> + /// Gets the connected input <see cref="IGraphPainter{TImage}"/>. + /// </summary> + public IGraphPainter<WpfDataSeries,BitmapSource> Input + { + get + { + return _input; + } + private set + { + _input = value; + OnInputChanged(_input); + } + } + + /// <summary> + /// Gets the connected input <see cref="IGraphPainter"/>. + /// </summary> + IGraphPainter IGraphInputComponent<IGraphPainter>.Input + { + get + { + return Input; + } + } + + #endregion + + #region Constructors + + /// <summary> + /// Initializes the <see cref="WpfGraphSurface"/> class. + /// </summary> + static WpfGraphSurface() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(WpfGraphSurface), new FrameworkPropertyMetadata(typeof(WpfGraphSurface))); + } + + /// <summary> + /// Initializes a new instance of the <see cref="WpfGraphSurface"/> class. + /// </summary> + public WpfGraphSurface() + { + SizeChanged += OnSizeChanged; + } + + #endregion + + #region Protected Methods + + /// <summary> + /// Called when the mouse moves over the zoom/pan selection canvas. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param> + protected virtual void OnSelectionCanvasMouseMove(object sender, MouseEventArgs e) + { + _current_mouse_position = e.GetPosition(_selection_canvas); + + if (_is_selection_mouse_down_zoom && Keyboard.IsKeyDown(Key.LeftCtrl)) + { + Canvas.SetLeft(_selection_rectangle, _selection_start_point.X); + Canvas.SetTop(_selection_rectangle, _selection_start_point.Y); + + Point _selection_current_point = e.GetPosition(_selection_canvas); + + if (_selection_current_point.X - _selection_start_point.X > 1) + { + _selection_rectangle.Width = _selection_current_point.X - _selection_start_point.X; + } + + if (_selection_current_point.Y - _selection_start_point.Y > 1) + { + _selection_rectangle.Height = _selection_current_point.Y - _selection_start_point.Y; + } + + if (_selection_current_point.X < _selection_start_point.X) + { + Canvas.SetLeft(_selection_rectangle, _selection_current_point.X); + _selection_rectangle.Width = _selection_start_point.X - _selection_current_point.X; + } + + if (_selection_current_point.Y < _selection_start_point.Y) + { + Canvas.SetTop(_selection_rectangle, _selection_current_point.Y); + _selection_rectangle.Height = _selection_start_point.Y - _selection_current_point.Y; + } + } + else if (_is_selection_mouse_down_pan && _is_scaled) + { + Point _selection_current_point = e.GetPosition(_selection_canvas); + + double delta_x = _current_mouse_position.X - _last_mouse_position.X; + double delta_y = _current_mouse_position.Y - _last_mouse_position.Y; + + var renderer = Input; + + double x = renderer.ZoomRect.Left - delta_x; + double y = renderer.ZoomRect.Top - delta_y; + + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + if (x + renderer.ZoomRect.Width > SurfaceWidth) + { + x = x - (x + renderer.ZoomRect.Width - SurfaceWidth); + } + + if (y + renderer.ZoomRect.Height > SurfaceHeight) + { + y = y - (y + renderer.ZoomRect.Height - SurfaceHeight); + } + + Rect target_rect = new Rect(x, y, renderer.ZoomRect.Width, renderer.ZoomRect.Height); + + renderer.SetZoomRect(target_rect.ToGraphRect()); + } + + _last_mouse_position = _current_mouse_position; + } + + /// <summary> + /// Called when the mouse released from the zoom/pan selection canvas. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param> + protected virtual void OnSelectionCanvasMouseUp(object sender, MouseButtonEventArgs e) + { + _selection_canvas.ReleaseMouseCapture(); + + if (_is_selection_mouse_down_pan) + { + _is_selection_mouse_down_pan = false; + _pan_rect = Input.ZoomRect.ToWpfRect(); + } + else if (_is_selection_mouse_down_zoom) + { + _is_selection_mouse_down_zoom = false; + + Rect target_rect = new Rect(Canvas.GetLeft(_selection_rectangle), Canvas.GetTop(_selection_rectangle), _selection_rectangle.Width, _selection_rectangle.Height); + _pan_rect = target_rect; + Input.SetZoomRect(target_rect.ToGraphRect()); + _selection_rectangle.Visibility = Visibility.Hidden; + _is_scaled = true; + } + } + + /// <summary> + /// Called when the mouse pressed on the zoom/pan selection canvas. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param> + protected virtual void OnSelectionCanvasMouseDown(object sender, MouseButtonEventArgs e) + { + Mouse.Capture(_selection_canvas); + + _selection_start_point = e.GetPosition(_selection_canvas); + _current_mouse_position = _selection_start_point; + _last_mouse_position = _current_mouse_position; + + if (e.ClickCount == 2) + { + Input.ResetZoomRect(); + _is_scaled = false; + } + else if (Keyboard.IsKeyDown(Key.LeftCtrl)) + { + _selection_rectangle.Width = 0; + _selection_rectangle.Height = 0; + _is_selection_mouse_down_zoom = true; + _is_selection_mouse_down_pan = false; + _selection_rectangle.Visibility = Visibility.Visible; + } + else + { + _is_selection_mouse_down_pan = true; + } + } + + /// <summary> + /// Called when the control size changes + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param> + protected virtual void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + OnSurfaceSizeChanged(_grid.ActualWidth, _grid.ActualHeight); + } + + /// <summary> + /// Called when surface size has changed. + /// </summary> + /// <param name="width">The width.</param> + /// <param name="height">The height.</param> + protected virtual void OnSurfaceSizeChanged(double width, double height) + { + SurfaceWidth = width; + SurfaceHeight = height; + + SurfaceSizeChanged?.Invoke(this, new SurfaceSizeChangedEventArgs() { Width = width, Height = height }); + + if (Input != null) + { + Input.ResetZoomRect(); + } + } + + /// <summary> + /// Raises the <see cref="InputChanged"/> event. + /// </summary> + /// <param name="input">The input.</param> + protected virtual void OnInputChanged(IGraphPainter<WpfDataSeries, BitmapSource> input) + { + InputChanged?.Invoke(this, new InputChangedEventArgs<IGraphPainter<WpfDataSeries, BitmapSource>>(input)); + } + + /// <summary> + /// Called when the painter has deliverd a new image to display. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The <see cref="PaintingCompletedEventArgs{BitmapSource}"/> instance containing the event data.</param> + protected virtual void OnPaintingCompleted(object sender, PaintingCompletedEventArgs<BitmapSource> e) + { + Image = e.Image; + } + + #endregion + + #region Apply Template + + /// <summary> + /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate" />. + /// </summary> + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _selection_rectangle = GetTemplateChild("PART_SelectionRectangle") as Rectangle; + _selection_canvas = GetTemplateChild("PART_SelectionCanvas") as Canvas; + _grid = GetTemplateChild("PART_Grid") as Grid; + + _selection_canvas.MouseDown += OnSelectionCanvasMouseDown; + _selection_canvas.MouseUp += OnSelectionCanvasMouseUp; + _selection_canvas.MouseMove += OnSelectionCanvasMouseMove; + } + + #endregion + + #region Public Methods + + /// <summary> + /// Invokes a method on the surface thread. + /// </summary> + /// <param name="action">The action.</param> + public void InvokeOnSurface(Action action) + { + Dispatcher.BeginInvoke(action); + } + + /// <summary> + /// Connects this surface to the specified <see cref="IGraphPainter{TImage}"/>. + /// </summary> + /// <param name="painter">The painter.</param> + /// <param name="fromInput">Specifies whether this call was made from an input <see cref="IGraphPainter{TImage}"/>.</param> + public void ConnectInput(IGraphPainter<WpfDataSeries,BitmapSource> painter, bool fromInput = false) + { + painter.ThrowIfNull("Cannot connect a null painter."); + + DisconnectInput(); + + Input = painter; + + Input.PaintingCompleted += OnPaintingCompleted; + + if (!fromInput) + { + Input.ConnectOutput(this, true); + } + } + + /// <summary> + /// Disconnects the current connected input <see cref="IGraphPainter"/>. + /// </summary> + /// <param name="fromInput">Specifies whether this call was made from an input <see cref="IGraphPainter"/>.</param> + public void DisconnectInput(bool fromInput = false) + { + if (Input != null) + { + if (!fromInput) + { + Input.DisconnectOutput(true); + } + + Input.PaintingCompleted -= OnPaintingCompleted; + + Input = null; + } + } + + /// <summary> + /// Connects this surface to the specified <see cref="IGraphPainter"/>. + /// </summary> + /// <param name="painter">The painter.</param> + /// <param name="fromInput">Specifies whether this call was made from an input <see cref="IGraphPainter"/>.</param> + void IGraphInputComponent<IGraphPainter>.ConnectInput(IGraphPainter painter, bool fromInput) + { + ConnectInput(painter as IGraphPainter<WpfDataSeries, BitmapSource>, fromInput); + } + + #endregion + } +} |
