This commit is contained in:
2024-02-23 09:29:44 -05:00
parent 2bbedc0178
commit 0038248f33
398 changed files with 39074 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
/// <summary>
/// Paints an arrow with start and end points in viewport coordinates.
/// </summary>
public class Arrow : Segment
{
/// <summary>
/// Initializes a new instance of the <see cref="Arrow"/> class.
/// </summary>
public Arrow()
{
Init();
}
/// <summary>
/// Initializes a new instance of the <see cref="Arrow"/> class.
/// </summary>
/// <param name="startPoint">The start point of arrow.</param>
/// <param name="endPoint">The end pointof arrow .</param>
public Arrow(Point startPoint, Point endPoint)
: base(startPoint, endPoint)
{
Init();
}
private void Init()
{
geometryGroup.Children.Add(LineGeometry);
geometryGroup.Children.Add(leftLineGeometry);
geometryGroup.Children.Add(rightLineGeometry);
}
#region ArrowLength property
/// <summary>
/// Gets or sets the length of the arrow.
/// </summary>
/// <value>The length of the arrow.</value>
public double ArrowLength
{
get { return (double)GetValue(ArrowLengthProperty); }
set { SetValue(ArrowLengthProperty, value); }
}
/// <summary>
/// Identifies ArrowLength dependency property.
/// </summary>
public static readonly DependencyProperty ArrowLengthProperty = DependencyProperty.Register(
"ArrowLength",
typeof(double),
typeof(Arrow),
new FrameworkPropertyMetadata(0.1, OnPointChanged));
#endregion
#region ArrowAngle property
/// <summary>
/// Gets or sets the arrow angle in degrees.
/// </summary>
/// <value>The arrow angle.</value>
public double ArrowAngle
{
get { return (double)GetValue(ArrowAngleProperty); }
set { SetValue(ArrowAngleProperty, value); }
}
/// <summary>
/// Identifies ArrowAngle dependency property.
/// </summary>
public static readonly DependencyProperty ArrowAngleProperty = DependencyProperty.Register(
"ArrowAngle",
typeof(double),
typeof(Arrow),
new FrameworkPropertyMetadata(15.0, OnPointChanged));
#endregion
protected override void UpdateUIRepresentationCore()
{
base.UpdateUIRepresentationCore();
var transform = Plotter.Viewport.Transform;
Point p1 = StartPoint.DataToScreen(transform);
Point p2 = EndPoint.DataToScreen(transform);
Vector arrowVector = p1 - p2;
Vector arrowCapVector = ArrowLength * arrowVector;
Matrix leftMatrix = Matrix.Identity;
leftMatrix.Rotate(ArrowAngle);
Matrix rightMatrix = Matrix.Identity;
rightMatrix.Rotate(-ArrowAngle);
Vector leftArrowLine = leftMatrix.Transform(arrowCapVector);
Vector rightArrowLine = rightMatrix.Transform(arrowCapVector);
leftLineGeometry.StartPoint = p2;
rightLineGeometry.StartPoint = p2;
leftLineGeometry.EndPoint = p2 + leftArrowLine;
rightLineGeometry.EndPoint = p2 + rightArrowLine;
}
private LineGeometry leftLineGeometry = new LineGeometry();
private LineGeometry rightLineGeometry = new LineGeometry();
private GeometryGroup geometryGroup = new GeometryGroup();
protected override Geometry DefiningGeometry
{
get { return geometryGroup; }
}
}
}

View File

@@ -0,0 +1,46 @@
<d3:PositionalViewportUIContainer x:Class="Microsoft.Research.DynamicDataDisplay.Charts.Shapes.DraggablePoint"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="clr-namespace:Microsoft.Research.DynamicDataDisplay.Charts"
xmlns:d3core="clr-namespace:Microsoft.Research.DynamicDataDisplay"
ToolTip="{Binding Position, RelativeSource={RelativeSource Self}}">
<d3:PositionalViewportUIContainer.Style>
<Style TargetType="{x:Type d3:PositionalViewportUIContainer}">
<Style.Resources>
<Storyboard x:Key="story">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1"
To="0.1" Duration="0:0:0.4" AutoReverse="True"
RepeatBehavior="Forever"/>
</Storyboard>
</Style.Resources>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="Cursor" Value="ScrollAll"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsMouseCaptured" Value="False"/>
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard Name="storyboard" Storyboard="{StaticResource story}"/>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="storyboard"/>
</MultiTrigger.ExitActions>
</MultiTrigger>
</Style.Triggers>
</Style>
</d3:PositionalViewportUIContainer.Style>
<Grid Width="12" Height="12">
<Ellipse Width="3" Height="3" Fill="Black"/>
<Ellipse Width="12" Height="12" Fill="Transparent" Stroke="Black"/>
</Grid>
</d3:PositionalViewportUIContainer>

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.Windows.Controls.Primitives;
using Microsoft.Research.DynamicDataDisplay;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
/// <summary>
/// Represents a simple draggable point with position bound to point in viewport coordinates, which allows to drag iself by mouse.
/// </summary>
public partial class DraggablePoint : PositionalViewportUIContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="DraggablePoint"/> class.
/// </summary>
public DraggablePoint()
{
InitializeComponent();
}
/// <summary>
/// Initializes a new instance of the <see cref="DraggablePoint"/> class.
/// </summary>
/// <param name="position">The position of DraggablePoint.</param>
public DraggablePoint(Point position) : this() { Position = position; }
bool dragging = false;
Point dragStart;
Vector shift;
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (Plotter == null)
return;
dragStart = e.GetPosition(Plotter.ViewportPanel).ScreenToData(Plotter.Viewport.Transform);
shift = Position - dragStart;
dragging = true;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
ReleaseMouseCapture();
dragging = false;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (!dragging)
{
if (IsMouseCaptured)
ReleaseMouseCapture();
return;
}
if (!IsMouseCaptured)
CaptureMouse();
Point mouseInData = e.GetPosition(Plotter.ViewportPanel).ScreenToData(Plotter.Viewport.Transform);
if (mouseInData != dragStart)
{
Position = mouseInData + shift;
e.Handled = true;
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (dragging)
{
dragging = false;
if (IsMouseCaptured)
{
ReleaseMouseCapture();
e.Handled = true;
}
}
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
return base.ArrangeOverride(arrangeBounds);
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Shapes;
using System.Windows;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents an infinite horizontal line with y-coordinate.
/// </summary>
public sealed class HorizontalLine : SimpleLine
{
/// <summary>
/// Initializes a new instance of the <see cref="HorizontalLine"/> class.
/// </summary>
public HorizontalLine() { }
/// <summary>
/// Initializes a new instance of the <see cref="HorizontalLine"/> class with specified y coordinate.
/// </summary>
/// <param name="yCoordinate">The y coordinate of line.</param>
public HorizontalLine(double yCoordinate)
{
Value = yCoordinate;
}
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
Point p1 = new Point(Plotter.Viewport.Visible.XMin, Value).DataToScreen(transform);
Point p2 = new Point(Plotter.Viewport.Visible.XMax, Value).DataToScreen(transform);
LineGeometry.StartPoint = p1;
LineGeometry.EndPoint = p2;
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Paints horizontal filled and outlined range in viewport coordinates.
/// </summary>
public sealed class HorizontalRange : RangeHighlight
{
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
DataRect visible = Plotter.Viewport.Visible;
Point p1_left = new Point(visible.XMin, Value1).DataToScreen(transform);
Point p1_right = new Point(visible.XMax, Value1).DataToScreen(transform);
Point p2_left = new Point(visible.XMin, Value2).DataToScreen(transform);
Point p2_right = new Point(visible.XMax, Value2).DataToScreen(transform);
LineGeometry1.StartPoint = p1_left;
LineGeometry1.EndPoint = p1_right;
LineGeometry2.StartPoint = p2_left;
LineGeometry2.EndPoint = p2_right;
RectGeometry.Rect = new Rect(p1_left, p2_right);
}
}
}

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows.Data;
using System.Windows;
using System.Windows.Threading;
using System.ComponentModel;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
/// <summary>
/// Represents an editor of points' position of ViewportPolyline or ViewportPolygon.
/// </summary>
[ContentProperty("Polyline")]
public class PolylineEditor : IPlotterElement
{
/// <summary>
/// Initializes a new instance of the <see cref="PolylineEditor"/> class.
/// </summary>
public PolylineEditor() { }
private ViewportPolylineBase polyline;
/// <summary>
/// Gets or sets the polyline, to edit points of which.
/// </summary>
/// <value>The polyline.</value>
[NotNull]
public ViewportPolylineBase Polyline
{
get { return polyline; }
set
{
if (value == null)
throw new ArgumentNullException("Polyline");
if (polyline != value)
{
polyline = value;
var descr = DependencyPropertyDescriptor.FromProperty(ViewportPolylineBase.PointsProperty, typeof(ViewportPolylineBase));
descr.AddValueChanged(polyline, OnPointsReplaced);
if (plotter != null)
{
AddLineToPlotter(false);
}
}
}
}
bool pointsAdded = false;
private void OnPointsReplaced(object sender, EventArgs e)
{
if (plotter == null)
return;
if (pointsAdded)
return;
ViewportPolylineBase line = (ViewportPolylineBase)sender;
pointsAdded = true;
List<IPlotterElement> draggablePoints = new List<IPlotterElement>();
GetDraggablePoints(draggablePoints);
foreach (var point in draggablePoints)
{
plotter.Children.Add(point);
}
}
private void AddLineToPlotter(bool async)
{
if (!async)
{
foreach (var item in GetAllElementsToAdd())
{
plotter.Children.Add(item);
}
}
else
{
plotter.Dispatcher.BeginInvoke(((Action)(() => { AddLineToPlotter(false); })), DispatcherPriority.Send);
}
}
private List<IPlotterElement> GetAllElementsToAdd()
{
var result = new List<IPlotterElement>(1 + polyline.Points.Count);
result.Add(polyline);
GetDraggablePoints(result);
return result;
}
private void GetDraggablePoints(List<IPlotterElement> collection)
{
for (int i = 0; i < polyline.Points.Count; i++)
{
DraggablePoint point = new DraggablePoint();
point.SetBinding(DraggablePoint.PositionProperty, new Binding
{
Source = polyline,
Path = new PropertyPath("Points[" + i + "]"),
Mode = BindingMode.TwoWay
});
collection.Add(point);
}
}
#region IPlotterElement Members
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = (Plotter2D)plotter;
if (polyline != null)
{
AddLineToPlotter(true);
}
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
this.plotter = null;
}
private Plotter2D plotter;
/// <summary>
/// Gets the parent plotter of chart.
/// Should be equal to null if item is not connected to any plotter.
/// </summary>
/// <value>The plotter.</value>
public Plotter Plotter
{
get { return plotter; }
}
#endregion
}
}

View File

@@ -0,0 +1,194 @@
#define old
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay;
using System.Windows.Threading;
using System.Diagnostics;
using System.Windows.Markup;
using System.Collections.ObjectModel;
using Microsoft.Research.DynamicDataDisplay.Common;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using System.Windows.Data;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
public sealed class PositionChangedEventArgs : EventArgs
{
public Point Position { get; internal set; }
public Point PreviousPosition { get; internal set; }
}
public delegate Point PositionCoerceCallback(PositionalViewportUIContainer container, Point position);
public class PositionalViewportUIContainer : ContentControl, IPlotterElement
{
static PositionalViewportUIContainer()
{
Type type = typeof(PositionalViewportUIContainer);
// todo subscribe for properties changes
HorizontalContentAlignmentProperty.AddOwner(
type, new FrameworkPropertyMetadata(HorizontalAlignment.Center));
VerticalContentAlignmentProperty.AddOwner(
type, new FrameworkPropertyMetadata(VerticalAlignment.Center));
}
public PositionalViewportUIContainer()
{
PlotterEvents.PlotterChangedEvent.Subscribe(this, OnPlotterChanged);
//SetBinding(ViewportPanel.XProperty, new Binding("Position.X") { Source = this, Mode = BindingMode.TwoWay });
//SetBinding(ViewportPanel.YProperty, new Binding("Position.Y") { Source = this, Mode = BindingMode.TwoWay });
}
protected virtual void OnPlotterChanged(object sender, PlotterChangedEventArgs e)
{
if (e.CurrentPlotter != null)
OnPlotterAttached(e.CurrentPlotter);
else if (e.PreviousPlotter != null)
OnPlotterDetaching(e.PreviousPlotter);
}
public Point Position
{
get { return (Point)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); }
}
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register(
"Position",
typeof(Point),
typeof(PositionalViewportUIContainer),
new FrameworkPropertyMetadata(new Point(0, 0), OnPositionChanged, CoercePosition));
private static object CoercePosition(DependencyObject d, object value)
{
PositionalViewportUIContainer owner = (PositionalViewportUIContainer)d;
if (owner.positionCoerceCallbacks.Count > 0)
{
Point position = (Point)value;
foreach (var callback in owner.positionCoerceCallbacks)
{
position = callback(owner, position);
}
value = position;
}
return value;
}
private readonly ObservableCollection<PositionCoerceCallback> positionCoerceCallbacks = new ObservableCollection<PositionCoerceCallback>();
/// <summary>
/// Gets the list of callbacks which are called every time Position changes to coerce it.
/// </summary>
/// <value>The position coerce callbacks.</value>
public ObservableCollection<PositionCoerceCallback> PositionCoerceCallbacks
{
get { return positionCoerceCallbacks; }
}
private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PositionalViewportUIContainer container = (PositionalViewportUIContainer)d;
container.OnPositionChanged(e);
}
public event EventHandler<PositionChangedEventArgs> PositionChanged;
private void OnPositionChanged(DependencyPropertyChangedEventArgs e)
{
PositionChanged.Raise(this, new PositionChangedEventArgs { Position = (Point)e.NewValue, PreviousPosition = (Point)e.OldValue });
ViewportPanel.SetX(this, Position.X);
ViewportPanel.SetY(this, Position.Y);
}
#region IPlotterElement Members
private const string canvasName = "ViewportUIContainer_Canvas";
private ViewportHostPanel hostPanel;
private Plotter2D plotter;
public void OnPlotterAttached(Plotter plotter)
{
if (Parent == null)
{
hostPanel = new ViewportHostPanel();
Viewport2D.SetIsContentBoundsHost(hostPanel, false);
hostPanel.Children.Add(this);
plotter.Dispatcher.BeginInvoke(() =>
{
plotter.Children.Add(hostPanel);
}, DispatcherPriority.Send);
}
#if !old
Canvas hostCanvas = (Canvas)hostPanel.FindName(canvasName);
if (hostCanvas == null)
{
hostCanvas = new Canvas { ClipToBounds = true };
Panel.SetZIndex(hostCanvas, 1);
INameScope nameScope = NameScope.GetNameScope(hostPanel);
if (nameScope == null)
{
nameScope = new NameScope();
NameScope.SetNameScope(hostPanel, nameScope);
}
hostPanel.RegisterName(canvasName, hostCanvas);
hostPanel.Children.Add(hostCanvas);
}
hostCanvas.Children.Add(this);
#else
#endif
Plotter2D plotter2d = (Plotter2D)plotter;
this.plotter = plotter2d;
}
public void OnPlotterDetaching(Plotter plotter)
{
Plotter2D plotter2d = (Plotter2D)plotter;
#if !old
Canvas hostCanvas = (Canvas)hostPanel.FindName(canvasName);
if (hostCanvas.Children.Count == 1)
{
// only this ViewportUIContainer left
hostPanel.Children.Remove(hostCanvas);
}
hostCanvas.Children.Remove(this);
#else
if (hostPanel != null)
{
hostPanel.Children.Remove(this);
}
plotter.Dispatcher.BeginInvoke(() =>
{
plotter.Children.Remove(hostPanel);
}, DispatcherPriority.Send);
#endif
this.plotter = null;
}
public Plotter2D Plotter
{
get { return plotter; }
}
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
}

View File

@@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents rectangle with corners bound to viewport coordinates.
/// </summary>
[TemplatePart(Name = "PART_LinesPath", Type = typeof(Path))]
[TemplatePart(Name = "PART_RectPath", Type = typeof(Path))]
public abstract class RangeHighlight : Control, IPlotterElement
{
/// <summary>
/// Initializes a new instance of the <see cref="RangeHighlight"/> class.
/// </summary>
protected RangeHighlight()
{
Resources = new ResourceDictionary { Source = new Uri("/DynamicDataDisplay;component/Charts/Shapes/RangeHighlightStyle.xaml", UriKind.Relative) };
Style = (Style)FindResource(typeof(RangeHighlight));
ApplyTemplate();
}
bool partsLoaded = false;
protected bool PartsLoaded
{
get { return partsLoaded; }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
linesPath = (Path)Template.FindName("PART_LinesPath", this);
GeometryGroup linesGroup = new GeometryGroup();
linesGroup.Children.Add(lineGeometry1);
linesGroup.Children.Add(lineGeometry2);
linesPath.Data = linesGroup;
rectPath = (Path)Template.FindName("PART_RectPath", this);
rectPath.Data = rectGeometry;
partsLoaded = true;
}
#region Presentation DPs
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(RangeHighlight),
new PropertyMetadata(Shape.FillProperty.DefaultMetadata.DefaultValue));
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
"Stroke",
typeof(Brush),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeProperty.DefaultMetadata.DefaultValue));
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness",
typeof(double),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeThicknessProperty.DefaultMetadata.DefaultValue));
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public static readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register(
"StrokeStartLineCap",
typeof(PenLineCap),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeStartLineCapProperty.DefaultMetadata.DefaultValue));
public PenLineCap StrokeStartLineCap
{
get { return (PenLineCap)GetValue(StrokeStartLineCapProperty); }
set { SetValue(StrokeStartLineCapProperty, value); }
}
public static readonly DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register(
"StrokeEndLineCap",
typeof(PenLineCap),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeEndLineCapProperty.DefaultMetadata.DefaultValue));
public PenLineCap StrokeEndLineCap
{
get { return (PenLineCap)GetValue(StrokeEndLineCapProperty); }
set { SetValue(StrokeEndLineCapProperty, value); }
}
public static readonly DependencyProperty StrokeDashCapProperty = DependencyProperty.Register(
"StrokeDashCap",
typeof(PenLineCap),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeDashCapProperty.DefaultMetadata.DefaultValue));
public PenLineCap StrokeDashCap
{
get { return (PenLineCap)GetValue(StrokeDashCapProperty); }
set { SetValue(StrokeDashCapProperty, value); }
}
public static readonly DependencyProperty StrokeLineJoinProperty = DependencyProperty.Register(
"StrokeLineJoin",
typeof(PenLineJoin),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeLineJoinProperty.DefaultMetadata.DefaultValue));
public PenLineJoin StrokeLineJoin
{
get { return (PenLineJoin)GetValue(StrokeLineJoinProperty); }
set { SetValue(StrokeLineJoinProperty, value); }
}
public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register(
"StrokeMiterLimit",
typeof(double),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeMiterLimitProperty.DefaultMetadata.DefaultValue));
public double StrokeMiterLimit
{
get { return (double)GetValue(StrokeMiterLimitProperty); }
set { SetValue(StrokeMiterLimitProperty, value); }
}
public static readonly DependencyProperty StrokeDashOffsetProperty = DependencyProperty.Register(
"StrokeDashOffset",
typeof(double),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeDashOffsetProperty.DefaultMetadata.DefaultValue));
public double StrokeDashOffset
{
get { return (double)GetValue(StrokeDashOffsetProperty); }
set { SetValue(StrokeDashOffsetProperty, value); }
}
public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register(
"StrokeDashArray",
typeof(DoubleCollection),
typeof(RangeHighlight),
new PropertyMetadata(Shape.StrokeDashArrayProperty.DefaultMetadata.DefaultValue));
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public DoubleCollection StrokeDashArray
{
get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
set { SetValue(StrokeDashArrayProperty, value); }
}
#endregion
#region Values dependency properties
/// <summary>
/// Gets or sets the first value determining position of rectangle in viewport coordinates.
/// </summary>
/// <value>The value1.</value>
public double Value1
{
get { return (double)GetValue(Value1Property); }
set { SetValue(Value1Property, value); }
}
public static readonly DependencyProperty Value1Property =
DependencyProperty.Register(
"Value1",
typeof(double),
typeof(RangeHighlight),
new FrameworkPropertyMetadata(0.0, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RangeHighlight r = (RangeHighlight)d;
r.OnValueChanged(e);
}
/// <summary>
/// Gets or sets the second value determining position of rectangle in viewport coordinates.
/// </summary>
/// <value>The value2.</value>
public double Value2
{
get { return (double)GetValue(Value2Property); }
set { SetValue(Value2Property, value); }
}
public static readonly DependencyProperty Value2Property =
DependencyProperty.Register(
"Value2",
typeof(double),
typeof(RangeHighlight),
new FrameworkPropertyMetadata(0.0, OnValueChanged));
private void OnValueChanged(DependencyPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
#endregion
#region Geometry
private Path rectPath;
private Path linesPath;
private readonly RectangleGeometry rectGeometry = new RectangleGeometry();
protected RectangleGeometry RectGeometry
{
get { return rectGeometry; }
}
private readonly LineGeometry lineGeometry1 = new LineGeometry();
protected LineGeometry LineGeometry1
{
get { return lineGeometry1; }
}
private readonly LineGeometry lineGeometry2 = new LineGeometry();
protected LineGeometry LineGeometry2
{
get { return lineGeometry2; }
}
#endregion
#region IPlotterElement Members
private Plotter2D plotter;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
plotter.CentralGrid.Children.Add(this);
Plotter2D plotter2d = (Plotter2D)plotter;
this.plotter = plotter2d;
plotter2d.Viewport.PropertyChanged += Viewport_PropertyChanged;
UpdateUIRepresentation();
}
private void UpdateUIRepresentation()
{
if (Plotter == null) return;
if (partsLoaded)
{
UpdateUIRepresentationCore();
}
}
protected virtual void UpdateUIRepresentationCore() { }
void Viewport_PropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
Plotter2D plotter2d = (Plotter2D)plotter;
plotter2d.Viewport.PropertyChanged -= Viewport_PropertyChanged;
plotter.CentralGrid.Children.Remove(this);
this.plotter = null;
}
public Plotter2D Plotter
{
get { return plotter; }
}
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
}

View File

@@ -0,0 +1,46 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:Microsoft.Research.DynamicDataDisplay.Charts">
<Style TargetType="{x:Type Shape}" x:Key="linesPathStyle">
<Setter Property="Fill" Value="{x:Null}"/>
<Setter Property="Stroke" Value="{Binding l:RangeHighlight.Stroke}"/>
<Setter Property="StrokeDashArray" Value="{Binding l:RangeHighlight.StrokeDashArray}"/>
<Setter Property="StrokeDashCap" Value="{Binding l:RangeHighlight.StrokeDashCap}"/>
<Setter Property="StrokeDashOffset" Value="{Binding l:RangeHighlight.StrokeDashOffset}"/>
<Setter Property="StrokeEndLineCap" Value="{Binding l:RangeHighlight.StrokeEndLineCap}"/>
<Setter Property="StrokeLineJoin" Value="{Binding l:RangeHighlight.StrokeLineJoin}"/>
<Setter Property="StrokeMiterLimit" Value="{Binding l:RangeHighlight.StrokeMiterLimit}"/>
<Setter Property="StrokeStartLineCap" Value="{Binding l:RangeHighlight.StrokeStartLineCap}"/>
<Setter Property="StrokeThickness" Value="{Binding l:RangeHighlight.StrokeThickness}"/>
</Style>
<Style TargetType="{x:Type l:RangeHighlight}">
<Setter Property="Stroke" Value="DarkBlue"/>
<Setter Property="StrokeThickness" Value="1"/>
<Setter Property="Opacity" Value="0.3"/>
<Setter Property="Fill" Value="Blue"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type l:RangeHighlight}">
<Grid>
<Path Name="PART_RectPath"
Fill="{TemplateBinding Fill}"/>
<Path Name="PART_LinesPath"
Fill="{x:Null}"
Stroke="{TemplateBinding Stroke}"
StrokeDashArray="{TemplateBinding StrokeDashArray}"
StrokeDashCap="{TemplateBinding StrokeDashCap}"
StrokeDashOffset="{TemplateBinding StrokeDashOffset}"
StrokeEndLineCap="{TemplateBinding StrokeEndLineCap}"
StrokeLineJoin="{TemplateBinding StrokeLineJoin}"
StrokeMiterLimit="{TemplateBinding StrokeMiterLimit}"
StrokeStartLineCap="{TemplateBinding StrokeStartLineCap}"
StrokeThickness="{TemplateBinding StrokeThickness}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using System.ComponentModel;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents a rectangle with corners bound to viewport coordinates.
/// </summary>
public sealed class RectangleHighlight : ViewportShape
{
/// <summary>
/// Initializes a new instance of the <see cref="RectangleHighlight"/> class.
/// </summary>
public RectangleHighlight() { }
/// <summary>
/// Initializes a new instance of the <see cref="RectangleHighlight"/> class.
/// </summary>
/// <param name="bounds">The bounds.</param>
public RectangleHighlight(Rect bounds)
{
Bounds = bounds;
}
private DataRect rect = DataRect.Empty;
public DataRect Bounds
{
get { return rect; }
set
{
if (rect != value)
{
rect = value;
UpdateUIRepresentation();
}
}
}
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
Point p1 = rect.XMaxYMax.DataToScreen(transform);
Point p2 = rect.XMinYMin.DataToScreen(transform);
rectGeometry.Rect = new Rect(p1, p2);
}
private RectangleGeometry rectGeometry = new RectangleGeometry();
protected override Geometry DefiningGeometry
{
get { return rectGeometry; }
}
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using System.Windows.Shapes;
using Microsoft.Research.DynamicDataDisplay;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents a segment with start and end points bound to viewport coordinates.
/// </summary>
public class Segment : ViewportShape
{
/// <summary>
/// Initializes a new instance of the <see cref="Segment"/> class.
/// </summary>
public Segment() { }
/// <summary>
/// Initializes a new instance of the <see cref="Segment"/> class.
/// </summary>
/// <param name="startPoint">The start point of segment.</param>
/// <param name="endPoint">The end point of segment.</param>
public Segment(Point startPoint, Point endPoint)
{
StartPoint = startPoint;
EndPoint = endPoint;
}
/// <summary>
/// Gets or sets the start point of segment.
/// </summary>
/// <value>The start point.</value>
public Point StartPoint
{
get { return (Point)GetValue(StartPointProperty); }
set { SetValue(StartPointProperty, value); }
}
/// <summary>
/// Identifies the StartPoint dependency property.
/// </summary>
public static readonly DependencyProperty StartPointProperty = DependencyProperty.Register(
"StartPoint",
typeof(Point),
typeof(Segment),
new FrameworkPropertyMetadata(new Point(), OnPointChanged));
protected static void OnPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Segment segment = (Segment)d;
segment.UpdateUIRepresentation();
}
protected virtual void OnPointChanged() { }
/// <summary>
/// Gets or sets the end point of segment.
/// </summary>
/// <value>The end point.</value>
public Point EndPoint
{
get { return (Point)GetValue(EndPointProperty); }
set { SetValue(EndPointProperty, value); }
}
/// <summary>
/// Identifies the EndPoint dependency property.
/// </summary>
public static readonly DependencyProperty EndPointProperty = DependencyProperty.Register(
"EndPoint",
typeof(Point),
typeof(Segment),
new FrameworkPropertyMetadata(new Point(), OnPointChanged));
protected override void UpdateUIRepresentationCore()
{
if (Plotter == null)
return;
var transform = Plotter.Viewport.Transform;
Point p1 = StartPoint.DataToScreen(transform);
Point p2 = EndPoint.DataToScreen(transform);
lineGeometry.StartPoint = p1;
lineGeometry.EndPoint = p2;
}
LineGeometry lineGeometry = new LineGeometry();
protected LineGeometry LineGeometry
{
get { return lineGeometry; }
}
protected override Geometry DefiningGeometry
{
get { return lineGeometry; }
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents simple line bound to viewport coordinates.
/// </summary>
public abstract class SimpleLine : ViewportShape
{
/// <summary>
/// Initializes a new instance of the <see cref="SimpleLine"/> class.
/// </summary>
protected SimpleLine() { }
/// <summary>
/// Gets or sets the value of line - e.g., its horizontal or vertical coordinate.
/// </summary>
/// <value>The value.</value>
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
/// <summary>
/// Identifies Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof(double),
typeof(SimpleLine),
new PropertyMetadata(
0.0, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SimpleLine line = (SimpleLine)d;
line.OnValueChanged();
}
protected virtual void OnValueChanged()
{
UpdateUIRepresentation();
}
private LineGeometry lineGeometry = new LineGeometry();
protected LineGeometry LineGeometry
{
get { return lineGeometry; }
}
protected override Geometry DefiningGeometry
{
get { return lineGeometry; }
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
public class TemplateableDraggablePoint : DraggablePoint
{
private readonly Control marker = new Control { Focusable = false };
public TemplateableDraggablePoint()
{
marker.SetBinding(Control.TemplateProperty, new Binding { Source = this, Path = new PropertyPath("MarkerTemplate") });
Content = marker;
}
public ControlTemplate MarkerTemplate
{
get { return (ControlTemplate)GetValue(MarkerTemplateProperty); }
set { SetValue(MarkerTemplateProperty, value); }
}
public static readonly DependencyProperty MarkerTemplateProperty = DependencyProperty.Register(
"MarkerTemplate",
typeof(ControlTemplate),
typeof(TemplateableDraggablePoint),
new FrameworkPropertyMetadata(null));
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents an infinite vertical line with x viewport coordinate.
/// </summary>
public sealed class VerticalLine : SimpleLine
{
/// <summary>
/// Initializes a new instance of the <see cref="VerticalLine"/> class.
/// </summary>
public VerticalLine() { }
/// <summary>
/// Initializes a new instance of the <see cref="VerticalLine"/> class with specified x coordinate.
/// </summary>
/// <param name="xCoordinate">The x coordinate.</param>
public VerticalLine(double xCoordinate)
{
Value = xCoordinate;
}
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
Point p1 = new Point(Value, Plotter.Viewport.Visible.YMin).DataToScreen(transform);
Point p2 = new Point(Value, Plotter.Viewport.Visible.YMax).DataToScreen(transform);
LineGeometry.StartPoint = p1;
LineGeometry.EndPoint = p2;
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Paints vertical filled and outlined range in viewport coordinates.
/// </summary>
public sealed class VerticalRange : RangeHighlight
{
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
DataRect visible = Plotter.Viewport.Visible;
Point p1_top = new Point(Value1, visible.YMin).DataToScreen(transform);
Point p1_bottom = new Point(Value1, visible.YMax).DataToScreen(transform);
Point p2_top = new Point(Value2, visible.YMin).DataToScreen(transform);
Point p2_bottom = new Point(Value2, visible.YMax).DataToScreen(transform);
LineGeometry1.StartPoint = p1_top;
LineGeometry1.EndPoint = p1_bottom;
LineGeometry2.StartPoint = p2_top;
LineGeometry2.EndPoint = p2_bottom;
RectGeometry.Rect = new Rect(p1_top, p2_bottom);
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Charts.Shapes;
using System.Windows.Media;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.Charts.NewLine;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
public class ViewportPolyBezierCurve : ViewportPolylineBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ViewportPolyBezierCurve"/> class.
/// </summary>
public ViewportPolyBezierCurve() { }
public PointCollection BezierPoints
{
get { return (PointCollection)GetValue(BezierPointsProperty); }
set { SetValue(BezierPointsProperty, value); }
}
public static readonly DependencyProperty BezierPointsProperty = DependencyProperty.Register(
"BezierPoints",
typeof(PointCollection),
typeof(ViewportPolyBezierCurve),
new FrameworkPropertyMetadata(null, OnPropertyChanged));
private bool buildBezierPoints = true;
public bool BuildBezierPoints
{
get { return buildBezierPoints; }
set { buildBezierPoints = value; }
}
bool updating = false;
protected override void UpdateUIRepresentationCore()
{
if (updating) return;
updating = true;
var transform = Plotter.Viewport.Transform;
PathGeometry geometry = PathGeometry;
PointCollection points = Points;
geometry.Clear();
if (BezierPoints != null)
{
points = BezierPoints;
var screenPoints = points.DataToScreen(transform).ToArray();
PathFigure figure = new PathFigure();
figure.StartPoint = screenPoints[0];
figure.Segments.Add(new PolyBezierSegment(screenPoints.Skip(1), true));
geometry.Figures.Add(figure);
geometry.FillRule = this.FillRule;
}
else if (points == null) { }
else
{
PathFigure figure = new PathFigure();
if (points.Count > 0)
{
Point[] bezierPoints = null;
figure.StartPoint = points[0].DataToScreen(transform);
if (points.Count > 1)
{
Point[] screenPoints = points.DataToScreen(transform).ToArray();
bezierPoints = BezierBuilder.GetBezierPoints(screenPoints).Skip(1).ToArray();
figure.Segments.Add(new PolyBezierSegment(bezierPoints, true));
}
if (bezierPoints != null && buildBezierPoints)
{
Array.Resize(ref bezierPoints, bezierPoints.Length + 1);
Array.Copy(bezierPoints, 0, bezierPoints, 1, bezierPoints.Length - 1);
bezierPoints[0] = figure.StartPoint;
BezierPoints = new PointCollection(bezierPoints.ScreenToData(transform));
}
}
geometry.Figures.Add(figure);
geometry.FillRule = this.FillRule;
}
updating = false;
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using System.Windows.Shapes;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
/// <summary>
/// Represents a closed filled figure with points in Viewport coordinates.
/// </summary>
public sealed class ViewportPolygon : ViewportPolylineBase
{
static ViewportPolygon()
{
Type type = typeof(ViewportPolygon);
Shape.FillProperty.AddOwner(type, new FrameworkPropertyMetadata(Brushes.Coral));
}
/// <summary>
/// Initializes a new instance of the <see cref="ViewportPolygon"/> class.
/// </summary>
public ViewportPolygon() { }
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
PathGeometry geometry = PathGeometry;
PointCollection points = Points;
geometry.Clear();
if (points == null) { }
else
{
PathFigure figure = new PathFigure();
if (points.Count > 0)
{
figure.StartPoint = points[0].DataToScreen(transform);
if (points.Count > 1)
{
Point[] pointArray = new Point[points.Count - 1];
for (int i = 1; i < points.Count; i++)
{
pointArray[i - 1] = points[i].DataToScreen(transform);
}
figure.Segments.Add(new PolyLineSegment(pointArray, true));
figure.IsClosed = true;
}
}
geometry.Figures.Add(figure);
geometry.FillRule = this.FillRule;
}
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
/// <summary>
/// Represents a polyline with points in Viewport coordinates.
/// </summary>
public sealed class ViewportPolyline : ViewportPolylineBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ViewportPolyline"/> class.
/// </summary>
public ViewportPolyline() { }
protected override void UpdateUIRepresentationCore()
{
var transform = Plotter.Viewport.Transform;
PathGeometry geometry = PathGeometry;
PointCollection points = Points;
geometry.Clear();
if (points == null) { }
else
{
PathFigure figure = new PathFigure();
if (points.Count > 0)
{
figure.StartPoint = points[0].DataToScreen(transform);
if (points.Count > 1)
{
Point[] pointArray = new Point[points.Count - 1];
for (int i = 1; i < points.Count; i++)
{
pointArray[i - 1] = points[i].DataToScreen(transform);
}
figure.Segments.Add(new PolyLineSegment(pointArray, true));
}
}
geometry.Figures.Add(figure);
geometry.FillRule = this.FillRule;
}
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
{
public abstract class ViewportPolylineBase : ViewportShape
{
protected ViewportPolylineBase()
{
}
#region Properties
/// <summary>
/// Gets or sets the points in Viewport coordinates, that form the line.
/// </summary>
/// <value>The points.</value>
public PointCollection Points
{
get { return (PointCollection)GetValue(PointsProperty); }
set { SetValue(PointsProperty, value); }
}
/// <summary>
/// Identifies the Points dependency property.
/// </summary>
public static readonly DependencyProperty PointsProperty = DependencyProperty.Register(
"Points",
typeof(PointCollection),
typeof(ViewportPolylineBase),
new FrameworkPropertyMetadata(new PointCollection(), OnPropertyChanged));
protected static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ViewportPolylineBase polyline = (ViewportPolylineBase)d;
PointCollection currentPoints = (PointCollection)e.NewValue;
polyline.UpdateUIRepresentation();
}
/// <summary>
/// Gets or sets the fill rule of polygon or polyline.
/// </summary>
/// <value>The fill rule.</value>
public FillRule FillRule
{
get { return (FillRule)GetValue(FillRuleProperty); }
set { SetValue(FillRuleProperty, value); }
}
public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register(
"FillRule",
typeof(FillRule),
typeof(ViewportPolylineBase),
new FrameworkPropertyMetadata(FillRule.EvenOdd, OnPropertyChanged));
#endregion
private PathGeometry geometry = new PathGeometry();
protected PathGeometry PathGeometry
{
get { return geometry; }
}
protected sealed override Geometry DefiningGeometry
{
get { return geometry; }
}
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
/// <summary>
/// Represents a base class for simple shapes with viewport-bound coordinates.
/// </summary>
public abstract class ViewportShape : Shape, IPlotterElement
{
static ViewportShape()
{
Type type = typeof(ViewportShape);
Shape.StrokeProperty.AddOwner(type, new FrameworkPropertyMetadata(Brushes.Blue));
Shape.StrokeThicknessProperty.AddOwner(type, new FrameworkPropertyMetadata(2.0));
}
/// <summary>
/// Initializes a new instance of the <see cref="ViewportShape"/> class.
/// </summary>
protected ViewportShape() { }
protected void UpdateUIRepresentation()
{
if (Plotter == null)
return;
UpdateUIRepresentationCore();
}
protected virtual void UpdateUIRepresentationCore() { }
#region IPlotterElement Members
private Plotter2D plotter;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
plotter.CentralGrid.Children.Add(this);
Plotter2D plotter2d = (Plotter2D)plotter;
this.plotter = plotter2d;
plotter2d.Viewport.PropertyChanged += Viewport_PropertyChanged;
UpdateUIRepresentation();
}
private void Viewport_PropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
{
OnViewportPropertyChanged(e);
}
protected virtual void OnViewportPropertyChanged(ExtendedPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
Plotter2D plotter2d = (Plotter2D)plotter;
plotter2d.Viewport.PropertyChanged -= Viewport_PropertyChanged;
plotter.CentralGrid.Children.Remove(this);
this.plotter = null;
}
public Plotter2D Plotter
{
get { return plotter; }
}
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
}