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.Diagnostics;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using Microsoft.Research.DynamicDataDisplay;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
///
/// Adds to ChartPlotter two crossed lines, bound to mouse cursor position, and two labels near axes with mouse position in its text.
///
public partial class CursorCoordinateGraph : ContentGraph
{
///
/// Initializes a new instance of the class.
///
public CursorCoordinateGraph()
{
InitializeComponent();
}
Vector blockShift = new Vector(3, 3);
#region Plotter
protected override void OnPlotterAttached()
{
UIElement parent = (UIElement)Parent;
parent.MouseMove += parent_MouseMove;
parent.MouseEnter += Parent_MouseEnter;
parent.MouseLeave += Parent_MouseLeave;
UpdateVisibility();
UpdateUIRepresentation();
}
protected override void OnPlotterDetaching()
{
UIElement parent = (UIElement)Parent;
parent.MouseMove -= parent_MouseMove;
parent.MouseEnter -= Parent_MouseEnter;
parent.MouseLeave -= Parent_MouseLeave;
}
#endregion
private bool autoHide = true;
///
/// Gets or sets a value indicating whether to hide automatically cursor lines when mouse leaves plotter.
///
/// true if auto hide; otherwise, false.
public bool AutoHide
{
get { return autoHide; }
set { autoHide = value; }
}
private void Parent_MouseEnter(object sender, MouseEventArgs e)
{
if (autoHide)
{
UpdateVisibility();
}
}
private void UpdateVisibility()
{
horizLine.Visibility = vertGrid.Visibility = GetHorizontalVisibility();
vertLine.Visibility = horizGrid.Visibility = GetVerticalVisibility();
}
private Visibility GetHorizontalVisibility()
{
return showHorizontalLine ? Visibility.Visible : Visibility.Hidden;
}
private Visibility GetVerticalVisibility()
{
return showVerticalLine ? Visibility.Visible : Visibility.Hidden;
}
private void Parent_MouseLeave(object sender, MouseEventArgs e)
{
if (autoHide)
{
horizLine.Visibility = Visibility.Hidden;
vertLine.Visibility = Visibility.Hidden;
horizGrid.Visibility = Visibility.Hidden;
vertGrid.Visibility = Visibility.Hidden;
}
}
private bool followMouse = true;
///
/// Gets or sets a value indicating whether lines are following mouse cursor position.
///
/// true if lines are following mouse cursor position; otherwise, false.
public bool FollowMouse
{
get { return followMouse; }
set
{
followMouse = value;
if (!followMouse)
{
AutoHide = false;
}
UpdateUIRepresentation();
}
}
private void parent_MouseMove(object sender, MouseEventArgs e)
{
if (followMouse)
{
UpdateUIRepresentation();
}
}
protected override void OnViewportPropertyChanged(ExtendedPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
private string customXFormat = null;
///
/// Gets or sets the custom format string of x label.
///
/// The custom X format.
public string CustomXFormat
{
get { return customXFormat; }
set
{
if (customXFormat != value)
{
customXFormat = value;
UpdateUIRepresentation();
}
}
}
private string customYFormat = null;
///
/// Gets or sets the custom format string of y label.
///
/// The custom Y format.
public string CustomYFormat
{
get { return customYFormat; }
set
{
if (customYFormat != value)
{
customYFormat = value;
UpdateUIRepresentation();
}
}
}
private Func xTextMapping = null;
///
/// Gets or sets the text mapping of x label - function that builds text from x-coordinate of mouse in data.
///
/// The X text mapping.
public Func XTextMapping
{
get { return xTextMapping; }
set
{
if (xTextMapping != value)
{
xTextMapping = value;
UpdateUIRepresentation();
}
}
}
private Func yTextMapping = null;
///
/// Gets or sets the text mapping of y label - function that builds text from y-coordinate of mouse in data.
///
/// The Y text mapping.
public Func YTextMapping
{
get { return yTextMapping; }
set
{
if (yTextMapping != value)
{
yTextMapping = value;
UpdateUIRepresentation();
}
}
}
private bool showHorizontalLine = true;
///
/// Gets or sets a value indicating whether to show horizontal line.
///
/// true if horizontal line is shown; otherwise, false.
public bool ShowHorizontalLine
{
get { return showHorizontalLine; }
set
{
if (showHorizontalLine != value)
{
showHorizontalLine = value;
UpdateVisibility();
}
}
}
private bool showVerticalLine = true;
///
/// Gets or sets a value indicating whether to show vertical line.
///
/// true if vertical line is shown; otherwise, false.
public bool ShowVerticalLine
{
get { return showVerticalLine; }
set
{
if (showVerticalLine != value)
{
showVerticalLine = value;
UpdateVisibility();
}
}
}
private void UpdateUIRepresentation()
{
Point position = followMouse ? Mouse.GetPosition(this) : Position;
UpdateUIRepresentation(position);
}
private void UpdateUIRepresentation(Point mousePos)
{
if (Plotter2D == null) return;
var transform = Plotter2D.Viewport.Transform;
DataRect visible = Plotter2D.Viewport.Visible;
Rect output = Plotter2D.Viewport.Output;
if (!output.Contains(mousePos))
{
if (autoHide)
{
horizGrid.Visibility = horizLine.Visibility = vertGrid.Visibility = vertLine.Visibility = Visibility.Hidden;
}
return;
}
if (!followMouse)
{
mousePos = mousePos.DataToScreen(transform);
}
horizLine.X1 = output.Left;
horizLine.X2 = output.Right;
horizLine.Y1 = mousePos.Y;
horizLine.Y2 = mousePos.Y;
vertLine.X1 = mousePos.X;
vertLine.X2 = mousePos.X;
vertLine.Y1 = output.Top;
vertLine.Y2 = output.Bottom;
if (UseDashOffset)
{
horizLine.StrokeDashOffset = (output.Right - mousePos.X) / 2;
vertLine.StrokeDashOffset = (output.Bottom - mousePos.Y) / 2;
}
Point mousePosInData = mousePos.ScreenToData(transform);
string text = null;
if (showVerticalLine)
{
double xValue = mousePosInData.X;
if (xTextMapping != null)
text = xTextMapping(xValue);
// doesnot have xTextMapping or it returned null
if (text == null)
text = GetRoundedValue(visible.XMin, visible.XMax, xValue);
if (!String.IsNullOrEmpty(customXFormat))
text = String.Format(customXFormat, text);
horizTextBlock.Text = text;
}
double width = horizGrid.ActualWidth;
double x = mousePos.X + blockShift.X;
if (x + width > output.Right)
{
x = mousePos.X - blockShift.X - width;
}
Canvas.SetLeft(horizGrid, x);
if (showHorizontalLine)
{
double yValue = mousePosInData.Y;
text = null;
if (yTextMapping != null)
text = yTextMapping(yValue);
if (text == null)
text = GetRoundedValue(visible.YMin, visible.YMax, yValue);
if (!String.IsNullOrEmpty(customYFormat))
text = String.Format(customYFormat, text);
vertTextBlock.Text = text;
}
// by default vertGrid is positioned on the top of line.
double height = vertGrid.ActualHeight;
double y = mousePos.Y - blockShift.Y - height;
if (y < output.Top)
{
y = mousePos.Y + blockShift.Y;
}
Canvas.SetTop(vertGrid, y);
if (followMouse)
Position = mousePos;
}
///
/// Gets or sets the mouse position in screen coordinates.
///
/// The position.
public Point Position
{
get { return (Point)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); }
}
///
/// Identifies Position dependency property.
///
public static readonly DependencyProperty PositionProperty = DependencyProperty.Register(
"Position",
typeof(Point),
typeof(CursorCoordinateGraph),
new UIPropertyMetadata(new Point(), OnPositionChanged));
private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CursorCoordinateGraph graph = (CursorCoordinateGraph)d;
graph.UpdateUIRepresentation((Point)e.NewValue);
}
private string GetRoundedValue(double min, double max, double value)
{
double roundedValue = value;
var log = RoundingHelper.GetDifferenceLog(min, max);
string format = "G3";
double diff = Math.Abs(max - min);
if (1E3 < diff && diff < 1E6)
{
format = "F0";
}
if (log < 0)
format = "G" + (-log + 2).ToString();
return roundedValue.ToString(format);
}
#region UseDashOffset property
public bool UseDashOffset
{
get { return (bool)GetValue(UseDashOffsetProperty); }
set { SetValue(UseDashOffsetProperty, value); }
}
public static readonly DependencyProperty UseDashOffsetProperty = DependencyProperty.Register(
"UseDashOffset",
typeof(bool),
typeof(CursorCoordinateGraph),
new FrameworkPropertyMetadata(true, UpdateUIRepresentation));
private static void UpdateUIRepresentation(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CursorCoordinateGraph graph = (CursorCoordinateGraph)d;
if ((bool)e.NewValue)
{
graph.UpdateUIRepresentation();
}
else
{
graph.vertLine.ClearValue(Line.StrokeDashOffsetProperty);
graph.horizLine.ClearValue(Line.StrokeDashOffsetProperty);
}
}
#endregion
#region LineStroke property
public Brush LineStroke
{
get { return (Brush)GetValue(LineStrokeProperty); }
set { SetValue(LineStrokeProperty, value); }
}
public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
"LineStroke",
typeof(Brush),
typeof(CursorCoordinateGraph),
new PropertyMetadata(new SolidColorBrush(Color.FromArgb(170, 86, 86, 86))));
#endregion
#region LineStrokeThickness property
public double LineStrokeThickness
{
get { return (double)GetValue(LineStrokeThicknessProperty); }
set { SetValue(LineStrokeThicknessProperty, value); }
}
public static readonly DependencyProperty LineStrokeThicknessProperty = DependencyProperty.Register(
"LineStrokeThickness",
typeof(double),
typeof(CursorCoordinateGraph),
new PropertyMetadata(2.0));
#endregion
#region LineStrokeDashArray property
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public DoubleCollection LineStrokeDashArray
{
get { return (DoubleCollection)GetValue(LineStrokeDashArrayProperty); }
set { SetValue(LineStrokeDashArrayProperty, value); }
}
public static readonly DependencyProperty LineStrokeDashArrayProperty = DependencyProperty.Register(
"LineStrokeDashArray",
typeof(DoubleCollection),
typeof(CursorCoordinateGraph),
new FrameworkPropertyMetadata(DoubleCollectionHelper.Create(3, 3)));
#endregion
}
}