using SciChart.Charting.ChartModifiers;
using SciChart.Charting.Visuals.Annotations;
using SciChart.Core.Utility.Mouse;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System;
namespace Charting.V2.CustomModifiers
{
///
/// Class XABCDModifier.
///
///
/// This class enabled creating consecutive annotations which stay connected to each other.
///
///
public class XABCDModifier : ChartModifierBase
{
///
/// Gets or sets a value indicating whether [close on completion].
///
/// true if [close on completion]; otherwise, false.
public bool CloseOnCompletion
{
get { return (bool)GetValue(CloseOnCompletionProperty); }
set { SetValue(CloseOnCompletionProperty, value); }
}
// Using a DependencyProperty as the backing store for CloseOnCompletion. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CloseOnCompletionProperty =
DependencyProperty.Register("CloseOnCompletion", typeof(bool), typeof(XABCDModifier), new PropertyMetadata(null));
#region Public Fields
///
/// The annotation identifier. This is intended to be used as a filter for CRUD of XABCD patterns.
///
public const string ANNOTATION_IDENTIFIER = "XABCDPattern";
#endregion Public Fields
#region Private Fields
///
/// The current annotation.
///
private AnnotationBase _currentAnnotation;
///
/// The is creation active.
///
private bool _isCreationActive = false;
///
/// The is in draw mode.
///
private bool _isInDrawMode = false;
///
/// The linked lines.
///
private IDictionary _linkedLines = new Dictionary();
///
/// The previous annotation.
///
private AnnotationBase _previousAnnotation;
#endregion Private Fields
#region Public Methods
///
/// Called when the element is attached to the Chart Surface
///
public override void OnAttached()
{
base.OnAttached();
// set for testing purposes. this would normally be set via a view model or databound in a view.
this.CloseOnCompletion = false;
}
public override void OnDetached()
{
base.OnDetached();
foreach (AnnotationBase annotation in ParentSurface.Annotations)
{
if ((string)annotation.Tag == ANNOTATION_IDENTIFIER)
{
annotation.Selected -= OnLineSelected;
}
}
}
///
/// Called when a Mouse DoubleClick occurs on the parent
///
///
/// This overridden method is only used to activate/deactive the modifer for testing purposes.
/// In a production setting, this modifier will need to be activated via a view model based on appropriate criteria.
///
/// Arguments detailing the mouse button operation
public override void OnModifierDoubleClick(ModifierMouseArgs e)
{
base.OnModifierDoubleClick(e);
if (_isInDrawMode)
{
_linkedLines.Clear();
_isCreationActive = false;
_currentAnnotation.IsEditable = true;
}
// turn on and off via double click
_isInDrawMode = !_isInDrawMode;
}
///
/// Called when the KeyDown event is fired for the Master of the current .
///
///
/// Used during testing environment to stop creation without a mouse action.
///
/// Arguments detailing the key event
public override void OnModifierKeyDown(ModifierKeyArgs e)
{
if (e.Key == Key.Delete)
{
TryRemoveAnnotation();
}
if (e.Key == Key.Escape)
{
_currentAnnotation.IsEditable = true;
_isInDrawMode = false;
_isCreationActive = false;
_linkedLines.Clear();
return;
}
if (!_isInDrawMode) return;
if (e.Key != Key.LeftCtrl) return;
base.OnModifierKeyDown(e);
_currentAnnotation.IsEditable = true;
_isInDrawMode = false;
_isCreationActive = false;
if (_linkedLines.Count > 1)
{
}
}
///
/// Called when a Mouse Button is pressed on the parent
///
///
/// This is the primary method for utilizing this modifier.
///
/// Arguments detailing the mouse button operation
public override void OnModifierMouseDown(ModifierMouseArgs e)
{
if (!_isInDrawMode) return;
base.OnModifierMouseDown(e);
if (_isInDrawMode)
{
// handle subsequent clicks on an existing annotation
if (_isCreationActive)
{
CompletePatternLeg(e);
CreatePatternLeg(e);
LinkAnnotations(_currentAnnotation, _previousAnnotation);
if (_linkedLines.Count == 5)
{
CompletePatternLeg(e);
CreatePatternLeg(e);
LinkAnnotations(_currentAnnotation, _previousAnnotation);
_previousAnnotation.X2 = _linkedLines["BC"].X1;
_previousAnnotation.Y2 = _linkedLines["BC"].Y1;
CompletePatternLeg(e);
_currentAnnotation.X1 = _linkedLines["DB"].X2;
_currentAnnotation.Y1 = _linkedLines["DB"].Y2;
_currentAnnotation.X2 = _linkedLines["XA"].X1;
_currentAnnotation.Y2 = _linkedLines["XA"].Y1;
// need to bind BX X1Y1 to DB X2Y2
LinkAnnotations(_linkedLines["BX"], _linkedLines["DB"]);
LinkAnnotations(_linkedLines["BC"], _linkedLines["DB"]);
LinkAnnotations(_linkedLines["BX"], _linkedLines["AB"]);
LinkAnnotations(_linkedLines["XA"], _linkedLines["BX"]);
CreateDataBinding(_linkedLines["BX"], AnnotationBase.X1Property, _linkedLines["BC"], "X1");
CreateDataBinding(_linkedLines["BX"], AnnotationBase.Y1Property, _linkedLines["BC"], "Y1");
CreateDataBinding(_linkedLines["DB"], AnnotationBase.X2Property, _linkedLines["AB"], "X2");
CreateDataBinding(_linkedLines["DB"], AnnotationBase.Y2Property, _linkedLines["AB"], "Y2");
_previousAnnotation.IsSelected = false;
_currentAnnotation.IsEditable = true;
_currentAnnotation.IsSelected = false;
_isInDrawMode = false;
_isCreationActive = false;
_linkedLines.Clear();
}
}
// handle initially creating an annotation
else
{
_isCreationActive = !_isCreationActive;
CreatePatternLeg(e);
}
}
}
private void OnLineSelected(object sender, EventArgs e)
{
var line = sender as LineAnnotation;
if (line != null)
{
var tag = (string)line.Tag;
}
}
private void CompletePatternLeg(ModifierMouseArgs e)
{
//_currentAnnotation.X2 = ParentSurface.XAxis.GetDataValue(e.MousePoint.X);
//_currentAnnotation.Y2 = ParentSurface.YAxis.GetDataValue(e.MousePoint.Y);
_previousAnnotation = _currentAnnotation;
_previousAnnotation.IsEditable = true; // can't be set while placing (interferes with mouse clicks), must be after placed
_previousAnnotation.IsSelected = false;
}
private void CreatePatternLeg(ModifierMouseArgs e)
{
_currentAnnotation = CreateNewLine();
SetAllCoordinates(_currentAnnotation, e.MousePoint);
AddAnnotation(GetXABCDLegName(), _currentAnnotation);
}
///
/// Called when the Mouse is moved on the parent
///
///
/// This is used solely to update the X2Y2 of the annotation being drawn which give real-time visual feed back on placement.
///
/// Arguments detailing the mouse move operation
public override void OnModifierMouseMove(ModifierMouseArgs e)
{
if (!_isInDrawMode || !_isCreationActive) return;
base.OnModifierMouseMove(e);
// give real time feedback on annotation being drawn
_currentAnnotation.X2 = ParentSurface.XAxis.GetDataValue(e.MousePoint.X);
_currentAnnotation.Y2 = ParentSurface.YAxis.GetDataValue(e.MousePoint.Y);
}
#endregion Public Methods
#region Private Methods
///
/// Adds the annotation.
///
///
/// The _linkedLines dictionary is used to allow connecting non-consecutive annotations in a group or pattern.
/// _linkedLines dictionary is cleared after the XABCDModifier is deactivated.
///
/// Name of the leg.
/// The annotation.
private void AddAnnotation(string legName, AnnotationBase annotation)
{
ParentSurface.Annotations.Add(annotation); // required for annotation to be rendered on SciChartSurface
_linkedLines.Add(legName, annotation); // used internally for advanced/custom annotation linking
}
///
/// Creates the data binding.
///
///
/// To prevent annotations from separating, call this function once for each annotation's properties, i.e. line1's X2Y2 and line2's X1Y1.
/// This will allow either annotation itself, or adorner handles to be selected and still keep the annotations connected.
///
/// The target.
/// The source property. This must be a DependencyProperty.
/// The source.
/// The string representation of the property to be data bound.
private void CreateDataBinding(AnnotationBase target, DependencyProperty targetProperty, AnnotationBase source, string sourceProperty)
{
Binding binding = new Binding();
binding.Mode = BindingMode.OneWay;
binding.Source = source;
binding.Path = new PropertyPath(sourceProperty);
target.SetBinding(targetProperty, binding);
}
///
/// Creates the new line.
///
///
/// The Tag property is specifically set so these annotations may be differentiated
/// from other annotations in .
/// When utilizing from another class or resource, the Tag property must be explicitly casted to a string.
///
/// This function could be easily extended to support other annotation types by switching on a DependencyProperty
/// that specifies the type of annotations this modifier should create.
///
/// AnnotationBase.
private AnnotationBase CreateNewLine()
{
/*var gb = new LinearGradientBrush();
gb.MappingMode = BrushMappingMode.RelativeToBoundingBox;
gb.StartPoint = new Point(0, 0);
gb.EndPoint = new Point(1, 1);
gb.GradientStops.Add(new GradientStop(Colors.Blue, 0.0));
gb.GradientStops.Add(new GradientStop(Colors.LawnGreen, 1.0));*/
var line = new LineAnnotation
{
//Tag = ANNOTATION_IDENTIFIER,
Tag = GetXABCDLegName(),
StrokeThickness = 2,
Stroke = Brushes.LawnGreen,
XAxisId = ParentSurface.XAxis.Id,
YAxisId = ParentSurface.XAxis.Id
};
line.Selected += OnLineSelected;
return line;
}
///
/// Gets the name of the XABCD leg.
///
/// System.String.
private string GetXABCDLegName()
{
string result = string.Empty;
switch (_linkedLines.Count)
{
case 0:
result = "XA";
break;
case 1:
result = "AB";
break;
case 2:
result = "BC";
break;
case 3:
result = "CD";
break;
case 4:
result = "DB";
break;
case 5:
result = "BX";
break;
default:
result = _linkedLines.Count.ToString();
break;
}
return result;
}
///
/// Links the annotations.
///
///
/// Helper method to link the beginning of the source annotation to the ending of the target annotation.
///
/// The target.
/// The source.
private void LinkAnnotations(AnnotationBase target, AnnotationBase source)
{
CreateDataBinding(target, AnnotationBase.X1Property, source, "X2");
CreateDataBinding(target, AnnotationBase.Y1Property, source, "Y2");
CreateDataBinding(source, AnnotationBase.X2Property, target, "X1");
CreateDataBinding(source, AnnotationBase.Y2Property, target, "Y1");
}
///
/// Sets all coordinates.
///
///
/// Helper method to set the X1Y1 and X2Y2 properties so the annotation can be added to the ParentSurface.Annotations collection.
///
/// The annotation.
/// The mouse point.
private void SetAllCoordinates(AnnotationBase annotation, Point mousePoint)
{
annotation.X1 = ParentSurface.XAxis.GetDataValue(mousePoint.X);
annotation.Y1 = ParentSurface.YAxis.GetDataValue(mousePoint.Y);
annotation.X2 = ParentSurface.XAxis.GetDataValue(mousePoint.X);
annotation.Y2 = ParentSurface.YAxis.GetDataValue(mousePoint.Y);
}
///
/// Tries the remove annotation based on if it is selected. Supports multi-delete.
///
private void TryRemoveAnnotation()
{
var selected = ParentSurface.Annotations.Where(a => a.IsSelected).ToArray();
foreach (AnnotationBase annotation in selected)
{
if (annotation.IsSelected)
{
ParentSurface.Annotations.Remove(annotation);
}
}
}
///
/// Tries the remove annotation.
///
/// The annotation.
private void TryRemoveAnnotation(AnnotationBase annotation)
{
ParentSurface.Annotations.Remove(annotation);
}
#endregion Private Methods
}
}