using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Data;
using System.Diagnostics;
using System.ComponentModel;
using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
using Microsoft.Research.DynamicDataDisplay.Common;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
///
/// Represents a base class for all axes in ChartPlotter.
/// Contains a real UI representation of axis - AxisControl, and means to adjust number of ticks, algorythms of their generating and
/// look of ticks' labels.
///
/// Type of each tick's value
public abstract class AxisBase : GeneralAxis, ITypedAxis, IValueConversion
{
///
/// Initializes a new instance of the class.
///
/// The axis control.
/// The convert from double.
/// The convert to double.
protected AxisBase(AxisControl axisControl, Func convertFromDouble, Func convertToDouble)
{
if (axisControl == null)
throw new ArgumentNullException("axisControl");
if (convertFromDouble == null)
throw new ArgumentNullException("convertFromDouble");
if (convertToDouble == null)
throw new ArgumentNullException("convertToDouble");
this.convertToDouble = convertToDouble;
this.convertFromDouble = convertFromDouble;
this.axisControl = axisControl;
axisControl.MakeDependent();
axisControl.ConvertToDouble = convertToDouble;
axisControl.ScreenTicksChanged += axisControl_ScreenTicksChanged;
Content = axisControl;
axisControl.SetBinding(Control.BackgroundProperty, new Binding("Background") { Source = this });
Focusable = false;
Loaded += OnLoaded;
}
public override void ForceUpdate()
{
axisControl.UpdateUI();
}
private void axisControl_ScreenTicksChanged(object sender, EventArgs e)
{
RaiseTicksChanged();
}
///
/// Gets or sets a value indicating whether this axis is default axis.
/// ChartPlotter's AxisGrid gets axis ticks to display from two default axes - horizontal and vertical.
///
///
/// true if this instance is default axis; otherwise, false.
///
public bool IsDefaultAxis
{
get { return Microsoft.Research.DynamicDataDisplay.Plotter.GetIsDefaultAxis(this); }
set { Microsoft.Research.DynamicDataDisplay.Plotter.SetIsDefaultAxis(this, value); }
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
RaiseTicksChanged();
}
///
/// Gets the screen coordinates of axis ticks.
///
/// The screen ticks.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override double[] ScreenTicks
{
get { return axisControl.ScreenTicks; }
}
///
/// Gets the screen coordinates of minor ticks.
///
/// The minor screen ticks.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override MinorTickInfo[] MinorScreenTicks
{
get { return axisControl.MinorScreenTicks; }
}
private AxisControl axisControl;
///
/// Gets the axis control - actual UI representation of axis.
///
/// The axis control.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public AxisControl AxisControl
{
get { return axisControl; }
}
///
/// Gets or sets the ticks provider, which is used to generate ticks in given range.
///
/// The ticks provider.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ITicksProvider TicksProvider
{
get { return axisControl.TicksProvider; }
set { axisControl.TicksProvider = value; }
}
///
/// Gets or sets the label provider, that is used to create UI look of axis ticks.
///
/// Should not be null.
///
/// The label provider.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[NotNull]
public LabelProviderBase LabelProvider
{
get { return axisControl.LabelProvider; }
set { axisControl.LabelProvider = value; }
}
///
/// Gets or sets the major label provider, which creates labels for major ticks.
/// If null, major labels will not be shown.
///
/// The major label provider.
public LabelProviderBase MajorLabelProvider
{
get { return axisControl.MajorLabelProvider; }
set { axisControl.MajorLabelProvider = value; }
}
///
/// Gets or sets the label string format, used to create simple formats of each tick's label, such as
/// changing tick label from "1.2" to "$1.2".
/// Should be in format "*{0}*", where '*' is any number of any chars.
///
/// If value is null, format string will not be used.
///
/// The label string format.
public string LabelStringFormat
{
get { return LabelProvider.LabelStringFormat; }
set { LabelProvider.LabelStringFormat = value; }
}
///
/// Gets or sets a value indicating whether to show minor ticks.
///
/// true if show minor ticks; otherwise, false.
public bool ShowMinorTicks
{
get { return axisControl.DrawMinorTicks; }
set { axisControl.DrawMinorTicks = value; }
}
///
/// Gets or sets a value indicating whether to show major labels.
///
/// true if show major labels; otherwise, false.
public bool ShowMajorLabels
{
get { return axisControl.DrawMajorLabels; }
set { axisControl.DrawMajorLabels = value; }
}
protected override void OnPlotterAttached(Plotter2D plotter)
{
plotter.Viewport.PropertyChanged += OnViewportPropertyChanged;
Panel panel = GetPanelByPlacement(Placement);
if (panel != null)
{
int index = GetInsertionIndexByPlacement(Placement, panel);
panel.Children.Insert(index, this);
}
using (axisControl.OpenUpdateRegion(true))
{
UpdateAxisControl(plotter);
}
}
private void UpdateAxisControl(Plotter2D plotter2d)
{
axisControl.Transform = plotter2d.Viewport.Transform;
axisControl.Range = CreateRangeFromRect(plotter2d.Visible.ViewportToData(plotter2d.Viewport.Transform));
}
private int GetInsertionIndexByPlacement(AxisPlacement placement, Panel panel)
{
int index = panel.Children.Count;
switch (placement)
{
case AxisPlacement.Left:
index = 0;
break;
case AxisPlacement.Top:
index = 0;
break;
default:
break;
}
return index;
}
ExtendedPropertyChangedEventArgs visibleChangedEventArgs;
int viewportPropertyChangedEnters = 0;
DataRect prevDataRect = DataRect.Empty;
private void OnViewportPropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
{
if (viewportPropertyChangedEnters > 4)
{
if (e.PropertyName == "Visible")
{
visibleChangedEventArgs = e;
}
return;
}
viewportPropertyChangedEnters++;
Viewport2D viewport = (Viewport2D)sender;
DataRect visible = viewport.Visible;
DataRect dataRect = visible.ViewportToData(viewport.Transform);
bool forceUpdate = dataRect != prevDataRect;
prevDataRect = dataRect;
Range range = CreateRangeFromRect(dataRect);
using (axisControl.OpenUpdateRegion(false)) // todo was forceUpdate
{
axisControl.Range = range;
axisControl.Transform = viewport.Transform;
}
Dispatcher.BeginInvoke(() =>
{
viewportPropertyChangedEnters--;
if (visibleChangedEventArgs != null)
{
OnViewportPropertyChanged(Plotter.Viewport, visibleChangedEventArgs);
}
visibleChangedEventArgs = null;
}, DispatcherPriority.Render);
}
private Func convertFromDouble;
///
/// Gets or sets the delegate that is used to create each tick from double.
/// Is used to create typed range to display for internal AxisControl.
/// If changed, ConvertToDouble should be changed appropriately, too.
/// Should not be null.
///
/// The convert from double.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[NotNull]
public Func ConvertFromDouble
{
get { return convertFromDouble; }
set
{
if (value == null)
throw new ArgumentNullException("value");
if (convertFromDouble != value)
{
convertFromDouble = value;
if (ParentPlotter != null)
{
UpdateAxisControl(ParentPlotter);
}
}
}
}
private Func convertToDouble;
///
/// Gets or sets the delegate that is used to convert each tick to double.
/// Is used by internal AxisControl to convert tick to double to get tick's coordinates inside of viewport.
/// If changed, ConvertFromDouble should be changed appropriately, too.
/// Should not be null.
///
/// The convert to double.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[NotNull]
public Func ConvertToDouble
{
get { return convertToDouble; }
set
{
if (value == null)
throw new ArgumentNullException("value");
if (convertToDouble != value)
{
convertToDouble = value;
axisControl.ConvertToDouble = value;
}
}
}
///
/// Sets conversions of axis - functions used to convert values of axis type to and from double values of viewport.
/// Sets both ConvertToDouble and ConvertFromDouble properties.
///
/// The minimal viewport value.
/// The value of axis type, corresponding to minimal viewport value.
/// The maximal viewport value.
/// The value of axis type, corresponding to maximal viewport value.
public virtual void SetConversion(double min, T minValue, double max, T maxValue)
{
throw new NotImplementedException();
}
private Range CreateRangeFromRect(DataRect visible)
{
T min, max;
Range range;
switch (Placement)
{
case AxisPlacement.Left:
case AxisPlacement.Right:
min = ConvertFromDouble(visible.YMin);
max = ConvertFromDouble(visible.YMax);
break;
case AxisPlacement.Top:
case AxisPlacement.Bottom:
min = ConvertFromDouble(visible.XMin);
max = ConvertFromDouble(visible.XMax);
break;
default:
throw new NotSupportedException();
}
TrySort(ref min, ref max);
range = new Range(min, max);
return range;
}
private static void TrySort(ref TS min, ref TS max)
{
if (min is IComparable)
{
IComparable c1 = (IComparable)min;
// if min > max
if (c1.CompareTo(max) > 0)
{
TS temp = min;
min = max;
max = temp;
}
}
}
protected override void OnPlacementChanged(AxisPlacement oldPlacement, AxisPlacement newPlacement)
{
axisControl.Placement = Placement;
if (ParentPlotter != null)
{
Panel panel = GetPanelByPlacement(oldPlacement);
panel.Children.Remove(this);
Panel newPanel = GetPanelByPlacement(newPlacement);
int index = GetInsertionIndexByPlacement(newPlacement, newPanel);
newPanel.Children.Insert(index, this);
}
}
protected override void OnPlotterDetaching(Plotter2D plotter)
{
if (plotter == null)
return;
Panel panel = GetPanelByPlacement(Placement);
if (panel != null)
{
panel.Children.Remove(this);
}
plotter.Viewport.PropertyChanged -= OnViewportPropertyChanged;
axisControl.Transform = null;
}
}
}