diff --git a/Backup/AssemblyInfo.cs b/Backup/AssemblyInfo.cs
new file mode 100644
index 0000000..6248214
--- /dev/null
+++ b/Backup/AssemblyInfo.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Resources;
+using System.Windows.Markup;
+using System.Runtime.CompilerServices;
+using Microsoft.Research.DynamicDataDisplay;
+using System.Diagnostics.CodeAnalysis;
+using System.Security;
+
+[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
+
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Navigation")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts.Navigation")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.DataSources")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Common.Palettes")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts.Axes")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.PointMarkers")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts.Shapes")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts.Markers")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Converters")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.MarkupExtensions")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.Charts.Isolines")]
+[assembly: XmlnsDefinition(D3AssemblyConstants.DefaultXmlNamespace, "Microsoft.Research.DynamicDataDisplay.ViewportRestrictions")]
+
+[assembly: XmlnsPrefix(D3AssemblyConstants.DefaultXmlNamespace, "d3")]
+
+[assembly: CLSCompliant(true)]
+[assembly: InternalsVisibleTo("DynamicDataDisplay.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010039f88065585acdedaac491218a8836c4c54070b4b0f85bc909bd002856b509349f95fc845d1d4c664ea6b93045f2ada3b4fe70c6cd9b3fb615f94b8b5f67e4ea8ea5decb233e2e3c0ce84b78dc3ca0cd9fd2260792ece12224fca5813f03c7ad57b1faa07e3ca8fafb278fa23976fc7a35b8b4ae4efedacd1e193d89738ac2aa")]
+[assembly: InternalsVisibleTo("DynamicDataDisplay.Maps, PublicKey=002400000480000094000000060200000024000052534131000400000100010039f88065585acdedaac491218a8836c4c54070b4b0f85bc909bd002856b509349f95fc845d1d4c664ea6b93045f2ada3b4fe70c6cd9b3fb615f94b8b5f67e4ea8ea5decb233e2e3c0ce84b78dc3ca0cd9fd2260792ece12224fca5813f03c7ad57b1faa07e3ca8fafb278fa23976fc7a35b8b4ae4efedacd1e193d89738ac2aa")]
+[assembly: InternalsVisibleTo("DynamicDataDisplay.Markers, PublicKey=002400000480000094000000060200000024000052534131000400000100010039f88065585acdedaac491218a8836c4c54070b4b0f85bc909bd002856b509349f95fc845d1d4c664ea6b93045f2ada3b4fe70c6cd9b3fb615f94b8b5f67e4ea8ea5decb233e2e3c0ce84b78dc3ca0cd9fd2260792ece12224fca5813f03c7ad57b1faa07e3ca8fafb278fa23976fc7a35b8b4ae4efedacd1e193d89738ac2aa")]
+
+[assembly: AllowPartiallyTrustedCallers]
+
+namespace Microsoft.Research.DynamicDataDisplay
+{
+ public static class D3AssemblyConstants
+ {
+ public const string DefaultXmlNamespace = "http://research.microsoft.com/DynamicDataDisplay/1.0";
+ }
+}
diff --git a/Backup/ChartPlotter.cs b/Backup/ChartPlotter.cs
new file mode 100644
index 0000000..4325ad8
--- /dev/null
+++ b/Backup/ChartPlotter.cs
@@ -0,0 +1,503 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Research.DynamicDataDisplay.Charts;
+using Microsoft.Research.DynamicDataDisplay.Charts.Navigation;
+using Microsoft.Research.DynamicDataDisplay.Navigation;
+using Microsoft.Research.DynamicDataDisplay.Common;
+using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
+
+namespace Microsoft.Research.DynamicDataDisplay
+{
+ /// Chart plotter is a plotter that renders axis and grid
+ public class ChartPlotter : Plotter2D
+ {
+ private GeneralAxis horizontalAxis = new HorizontalAxis();
+ private GeneralAxis verticalAxis = new VerticalAxis();
+ private AxisGrid axisGrid = new AxisGrid();
+
+ private readonly Legend legend = new Legend();
+
+ private NewLegend newLegend = new NewLegend();
+ public NewLegend NewLegend
+ {
+ get { return newLegend; }
+ set { newLegend = value; }
+ }
+
+ public ItemsPanelTemplate LegendPanelTemplate
+ {
+ get { return newLegend.ItemsPanel; }
+ set { newLegend.ItemsPanel = value; }
+ }
+
+ public Style LegendStyle
+ {
+ get { return newLegend.Style; }
+ set { newLegend.Style = value; }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ChartPlotter()
+ : base()
+ {
+ horizontalAxis.TicksChanged += OnHorizontalAxisTicksChanged;
+ verticalAxis.TicksChanged += OnVerticalAxisTicksChanged;
+
+ SetIsDefaultAxis(horizontalAxis as DependencyObject, true);
+ SetIsDefaultAxis(verticalAxis as DependencyObject, true);
+
+ mouseNavigation = new MouseNavigation();
+ keyboardNavigation = new KeyboardNavigation();
+ defaultContextMenu = new DefaultContextMenu();
+ horizontalAxisNavigation = new AxisNavigation { Placement = AxisPlacement.Bottom };
+ verticalAxisNavigation = new AxisNavigation { Placement = AxisPlacement.Left };
+
+ Children.AddMany(
+ horizontalAxis,
+ verticalAxis,
+ axisGrid,
+ mouseNavigation,
+ keyboardNavigation,
+ defaultContextMenu,
+ horizontalAxisNavigation,
+ legend,
+ verticalAxisNavigation,
+ new LongOperationsIndicator(),
+ newLegend
+ );
+
+#if DEBUG
+ Children.Add(new DebugMenu());
+#endif
+
+ SetAllChildrenAsDefault();
+ }
+
+ ///
+ /// Creates generic plotter from this ChartPlotter.
+ ///
+ ///
+ public GenericChartPlotter GetGenericPlotter()
+ {
+ return new GenericChartPlotter(this);
+ }
+
+ ///
+ /// Creates generic plotter from this ChartPlotter.
+ /// Horizontal and Vertical types of GenericPlotter should correspond to ChartPlotter's actual main axes types.
+ ///
+ /// The type of horizontal values.
+ /// The type of vertical values.
+ /// GenericChartPlotter, associated to this ChartPlotter.
+ public GenericChartPlotter GetGenericPlotter()
+ {
+ return new GenericChartPlotter(this);
+ }
+
+ ///
+ /// Creates generic plotter from this ChartPlotter.
+ ///
+ /// The type of the horizontal axis.
+ /// The type of the vertical axis.
+ /// The horizontal axis to use as data conversion source.
+ /// The vertical axis to use as data conversion source.
+ /// GenericChartPlotter, associated to this ChartPlotter
+ public GenericChartPlotter GetGenericPlotter(AxisBase horizontalAxis, AxisBase verticalAxis)
+ {
+ return new GenericChartPlotter(this, horizontalAxis, verticalAxis);
+ }
+
+ protected ChartPlotter(PlotterLoadMode loadMode) : base(loadMode) { }
+
+ ///
+ /// Creates empty plotter without any axes, navigation, etc.
+ ///
+ /// Empty plotter without any axes, navigation, etc.
+ public static ChartPlotter CreateEmpty()
+ {
+ return new ChartPlotter(PlotterLoadMode.OnlyViewport);
+ }
+
+ public void BeginLongOperation()
+ {
+ LongOperationsIndicator.BeginLongOperation(this);
+ }
+
+ public void EndLongOperation()
+ {
+ LongOperationsIndicator.EndLongOperation(this);
+ }
+
+ #region Default charts
+
+ private MouseNavigation mouseNavigation;
+ ///
+ /// Gets the default mouse navigation of ChartPlotter.
+ ///
+ /// The mouse navigation.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public MouseNavigation MouseNavigation
+ {
+ get { return mouseNavigation; }
+ }
+
+ private KeyboardNavigation keyboardNavigation;
+ ///
+ /// Gets the default keyboard navigation of ChartPlotter.
+ ///
+ /// The keyboard navigation.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public KeyboardNavigation KeyboardNavigation
+ {
+ get { return keyboardNavigation; }
+ }
+
+ private DefaultContextMenu defaultContextMenu;
+ ///
+ /// Gets the default context menu of ChartPlotter.
+ ///
+ /// The default context menu.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public DefaultContextMenu DefaultContextMenu
+ {
+ get { return defaultContextMenu; }
+ }
+
+ private AxisNavigation horizontalAxisNavigation;
+ ///
+ /// Gets the default horizontal axis navigation of ChartPlotter.
+ ///
+ /// The horizontal axis navigation.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public AxisNavigation HorizontalAxisNavigation
+ {
+ get { return horizontalAxisNavigation; }
+ }
+
+ private AxisNavigation verticalAxisNavigation;
+ ///
+ /// Gets the default vertical axis navigation of ChartPlotter.
+ ///
+ /// The vertical axis navigation.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public AxisNavigation VerticalAxisNavigation
+ {
+ get { return verticalAxisNavigation; }
+ }
+
+ ///
+ /// Gets the default axis grid of ChartPlotter.
+ ///
+ /// The axis grid.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public AxisGrid AxisGrid
+ {
+ get { return axisGrid; }
+ }
+
+ #endregion
+
+ private void OnHorizontalAxisTicksChanged(object sender, EventArgs e)
+ {
+ GeneralAxis axis = (GeneralAxis)sender;
+ UpdateHorizontalTicks(axis);
+ }
+
+ private void UpdateHorizontalTicks(GeneralAxis axis)
+ {
+ axisGrid.BeginTicksUpdate();
+
+ if (axis != null)
+ {
+ axisGrid.HorizontalTicks = axis.ScreenTicks;
+ axisGrid.MinorHorizontalTicks = axis.MinorScreenTicks;
+ }
+ else
+ {
+ axisGrid.HorizontalTicks = null;
+ axisGrid.MinorHorizontalTicks = null;
+ }
+
+ axisGrid.EndTicksUpdate();
+ }
+
+ private void OnVerticalAxisTicksChanged(object sender, EventArgs e)
+ {
+ GeneralAxis axis = (GeneralAxis)sender;
+ UpdateVerticalTicks(axis);
+ }
+
+ private void UpdateVerticalTicks(GeneralAxis axis)
+ {
+ axisGrid.BeginTicksUpdate();
+
+ if (axis != null)
+ {
+ axisGrid.VerticalTicks = axis.ScreenTicks;
+ axisGrid.MinorVerticalTicks = axis.MinorScreenTicks;
+ }
+ else
+ {
+ axisGrid.VerticalTicks = null;
+ axisGrid.MinorVerticalTicks = null;
+ }
+
+ axisGrid.EndTicksUpdate();
+ }
+
+ bool keepOldAxis = false;
+ bool updatingAxis = false;
+
+ ///
+ /// Gets or sets the main vertical axis of ChartPlotter.
+ /// Main vertical axis of ChartPlotter is axis which ticks are used to draw horizontal lines on AxisGrid.
+ /// Value can be set to null to completely remove main vertical axis.
+ ///
+ /// The main vertical axis.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public GeneralAxis MainVerticalAxis
+ {
+ get { return verticalAxis; }
+ set
+ {
+ if (updatingAxis)
+ return;
+
+ if (value == null && verticalAxis != null)
+ {
+ if (!keepOldAxis)
+ {
+ Children.Remove(verticalAxis);
+ }
+ verticalAxis.TicksChanged -= OnVerticalAxisTicksChanged;
+ verticalAxis = null;
+
+ UpdateVerticalTicks(verticalAxis);
+
+ return;
+ }
+
+ VerifyAxisType(value.Placement, AxisType.Vertical);
+
+ if (value != verticalAxis)
+ {
+ ValidateVerticalAxis(value);
+
+ updatingAxis = true;
+
+ if (verticalAxis != null)
+ {
+ verticalAxis.TicksChanged -= OnVerticalAxisTicksChanged;
+ SetIsDefaultAxis(verticalAxis, false);
+ if (!keepOldAxis)
+ {
+ Children.Remove(verticalAxis);
+ }
+ }
+ SetIsDefaultAxis(value, true);
+
+ verticalAxis = value;
+ verticalAxis.TicksChanged += OnVerticalAxisTicksChanged;
+
+ if (!Children.Contains(value))
+ {
+ Children.Add(value);
+ }
+
+ UpdateVerticalTicks(value);
+ OnVerticalAxisChanged();
+
+ updatingAxis = false;
+ }
+ }
+ }
+
+ protected virtual void OnVerticalAxisChanged() { }
+ protected virtual void ValidateVerticalAxis(GeneralAxis axis) { }
+
+ ///
+ /// Gets or sets the main horizontal axis visibility.
+ ///
+ /// The main horizontal axis visibility.
+ public Visibility MainHorizontalAxisVisibility
+ {
+ get { return MainHorizontalAxis != null ? ((UIElement)MainHorizontalAxis).Visibility : Visibility.Hidden; }
+ set
+ {
+ if (MainHorizontalAxis != null)
+ {
+ ((UIElement)MainHorizontalAxis).Visibility = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the main vertical axis visibility.
+ ///
+ /// The main vertical axis visibility.
+ public Visibility MainVerticalAxisVisibility
+ {
+ get { return MainVerticalAxis != null ? ((UIElement)MainVerticalAxis).Visibility : Visibility.Hidden; }
+ set
+ {
+ if (MainVerticalAxis != null)
+ {
+ ((UIElement)MainVerticalAxis).Visibility = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the main horizontal axis of ChartPlotter.
+ /// Main horizontal axis of ChartPlotter is axis which ticks are used to draw vertical lines on AxisGrid.
+ /// Value can be set to null to completely remove main horizontal axis.
+ ///
+ /// The main horizontal axis.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public GeneralAxis MainHorizontalAxis
+ {
+ get { return horizontalAxis; }
+ set
+ {
+ if (updatingAxis)
+ return;
+
+ if (value == null && horizontalAxis != null)
+ {
+ Children.Remove(horizontalAxis);
+ horizontalAxis.TicksChanged -= OnHorizontalAxisTicksChanged;
+ horizontalAxis = null;
+
+ UpdateHorizontalTicks(horizontalAxis);
+
+ return;
+ }
+
+ VerifyAxisType(value.Placement, AxisType.Horizontal);
+
+ if (value != horizontalAxis)
+ {
+ ValidateHorizontalAxis(value);
+
+ updatingAxis = true;
+
+ if (horizontalAxis != null)
+ {
+ horizontalAxis.TicksChanged -= OnHorizontalAxisTicksChanged;
+ SetIsDefaultAxis(horizontalAxis, false);
+ if (!keepOldAxis)
+ {
+ Children.Remove(horizontalAxis);
+ }
+ }
+ SetIsDefaultAxis(value, true);
+
+ horizontalAxis = value;
+ horizontalAxis.TicksChanged += OnHorizontalAxisTicksChanged;
+
+ if (!Children.Contains(value))
+ {
+ Children.Add(value);
+ }
+
+ UpdateHorizontalTicks(value);
+ OnHorizontalAxisChanged();
+
+ updatingAxis = false;
+ }
+ }
+ }
+
+ protected virtual void OnHorizontalAxisChanged() { }
+ protected virtual void ValidateHorizontalAxis(GeneralAxis axis) { }
+
+ private static void VerifyAxisType(AxisPlacement axisPlacement, AxisType axisType)
+ {
+ bool result = false;
+ switch (axisPlacement)
+ {
+ case AxisPlacement.Left:
+ case AxisPlacement.Right:
+ result = axisType == AxisType.Vertical;
+ break;
+ case AxisPlacement.Top:
+ case AxisPlacement.Bottom:
+ result = axisType == AxisType.Horizontal;
+ break;
+ default:
+ break;
+ }
+
+ if (!result)
+ throw new ArgumentException(Strings.Exceptions.InvalidAxisPlacement);
+ }
+
+ protected override void OnIsDefaultAxisChangedCore(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ GeneralAxis axis = d as GeneralAxis;
+ if (axis != null)
+ {
+ bool value = (bool)e.NewValue;
+ bool oldKeepOldAxis = keepOldAxis;
+
+ bool horizontal = axis.Placement == AxisPlacement.Bottom || axis.Placement == AxisPlacement.Top;
+ keepOldAxis = true;
+
+ if (value && horizontal)
+ {
+ MainHorizontalAxis = axis;
+ }
+ else if (value && !horizontal)
+ {
+ MainVerticalAxis = axis;
+ }
+ else if (!value && horizontal)
+ {
+ MainHorizontalAxis = null;
+ }
+ else if (!value && !horizontal)
+ {
+ MainVerticalAxis = null;
+ }
+
+ keepOldAxis = oldKeepOldAxis;
+ }
+ }
+
+ ///
+ /// Gets the default legend of ChartPlotter.
+ ///
+ /// The legend.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Legend Legend
+ {
+ get { return legend; }
+ }
+
+ ///
+ /// Gets or sets the visibility of legend.
+ ///
+ /// The legend visibility.
+ public Visibility LegendVisibility
+ {
+ get { return legend.Visibility; }
+ set { legend.Visibility = value; }
+ }
+
+ public bool NewLegendVisible
+ {
+ get { return newLegend.LegendVisible; }
+ set { newLegend.LegendVisible = value; }
+ }
+
+ private enum AxisType
+ {
+ Horizontal,
+ Vertical
+ }
+ }
+}
\ No newline at end of file
diff --git a/Backup/Charts/Axes/AxisBase.cs b/Backup/Charts/Axes/AxisBase.cs
new file mode 100644
index 0000000..6c87394
--- /dev/null
+++ b/Backup/Charts/Axes/AxisBase.cs
@@ -0,0 +1,411 @@
+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;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/AxisControl.cs b/Backup/Charts/Axes/AxisControl.cs
new file mode 100644
index 0000000..ef1ac05
--- /dev/null
+++ b/Backup/Charts/Axes/AxisControl.cs
@@ -0,0 +1,1231 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
+using System.Windows.Data;
+using Microsoft.Research.DynamicDataDisplay.Common;
+using System.Windows.Threading;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Defines a base class for axis UI representation.
+ /// Contains a number of properties that can be used to adjust ticks set and their look.
+ ///
+ ///
+ [TemplatePart(Name = "PART_AdditionalLabelsCanvas", Type = typeof(StackCanvas))]
+ [TemplatePart(Name = "PART_CommonLabelsCanvas", Type = typeof(StackCanvas))]
+ [TemplatePart(Name = "PART_TicksPath", Type = typeof(Path))]
+ [TemplatePart(Name = "PART_ContentsGrid", Type = typeof(Grid))]
+ public abstract class AxisControl : AxisControlBase
+ {
+ private const string templateKey = "axisControlTemplate";
+ private const string additionalLabelTransformKey = "additionalLabelsTransform";
+
+ private const string PART_AdditionalLabelsCanvas = "PART_AdditionalLabelsCanvas";
+ private const string PART_CommonLabelsCanvas = "PART_CommonLabelsCanvas";
+ private const string PART_TicksPath = "PART_TicksPath";
+ private const string PART_ContentsGrid = "PART_ContentsGrid";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected AxisControl()
+ {
+ HorizontalContentAlignment = HorizontalAlignment.Stretch;
+ VerticalContentAlignment = VerticalAlignment.Stretch;
+
+ Background = Brushes.Transparent;
+ //ClipToBounds = true;
+ Focusable = false;
+
+ UpdateUIResources();
+ UpdateSizeGetters();
+ }
+
+ internal void MakeDependent()
+ {
+ independent = false;
+ }
+
+ ///
+ /// This conversion is performed to make horizontal one-string and two-string labels
+ /// stay at one height.
+ ///
+ ///
+ ///
+ private static AxisPlacement GetBetterPlacement(AxisPlacement placement)
+ {
+ switch (placement)
+ {
+ case AxisPlacement.Left:
+ return AxisPlacement.Left;
+ case AxisPlacement.Right:
+ return AxisPlacement.Right;
+ case AxisPlacement.Top:
+ return AxisPlacement.Top;
+ case AxisPlacement.Bottom:
+ return AxisPlacement.Bottom;
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ #region Properties
+
+ private AxisPlacement placement = AxisPlacement.Bottom;
+ ///
+ /// Gets or sets the placement of axis control.
+ /// Relative positioning of parts of axis depends on this value.
+ ///
+ /// The placement.
+ public AxisPlacement Placement
+ {
+ get { return placement; }
+ set
+ {
+ if (placement != value)
+ {
+ placement = value;
+ UpdateUIResources();
+ UpdateSizeGetters();
+ }
+ }
+ }
+
+ private void UpdateSizeGetters()
+ {
+ switch (placement)
+ {
+ case AxisPlacement.Left:
+ case AxisPlacement.Right:
+ getSize = size => size.Height;
+ getCoordinate = p => p.Y;
+ createScreenPoint1 = d => new Point(scrCoord1, d);
+ createScreenPoint2 = (d, size) => new Point(scrCoord2 * size, d);
+ break;
+ case AxisPlacement.Top:
+ case AxisPlacement.Bottom:
+ getSize = size => size.Width;
+ getCoordinate = p => p.X;
+ createScreenPoint1 = d => new Point(d, scrCoord1);
+ createScreenPoint2 = (d, size) => new Point(d, scrCoord2 * size);
+ break;
+ default:
+ break;
+ }
+
+ switch (placement)
+ {
+ case AxisPlacement.Left:
+ createDataPoint = d => new Point(0, d);
+ break;
+ case AxisPlacement.Right:
+ createDataPoint = d => new Point(1, d);
+ break;
+ case AxisPlacement.Top:
+ createDataPoint = d => new Point(d, 1);
+ break;
+ case AxisPlacement.Bottom:
+ createDataPoint = d => new Point(d, 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void UpdateUIResources()
+ {
+ ResourceDictionary resources = new ResourceDictionary
+ {
+ Source = new Uri("/DynamicDataDisplay;component/Charts/Axes/AxisControlStyle.xaml", UriKind.Relative)
+ };
+
+ AxisPlacement placement = GetBetterPlacement(this.placement);
+ ControlTemplate template = (ControlTemplate)resources[templateKey + placement.ToString()];
+ Verify.AssertNotNull(template);
+ var content = (FrameworkElement)template.LoadContent();
+
+ if (ticksPath != null && ticksPath.Data != null)
+ {
+ GeometryGroup group = (GeometryGroup)ticksPath.Data;
+ foreach (var child in group.Children)
+ {
+ LineGeometry geometry = (LineGeometry)child;
+ lineGeomPool.Put(geometry);
+ }
+ group.Children.Clear();
+ }
+
+ ticksPath = (Path)content.FindName(PART_TicksPath);
+ ticksPath.SnapsToDevicePixels = true;
+ Verify.AssertNotNull(ticksPath);
+
+ // as this method can be called not only on loading of axisControl, but when its placement changes, internal panels
+ // can be not empty and their contents should be released
+ if (commonLabelsCanvas != null && labelProvider != null)
+ {
+ foreach (UIElement child in commonLabelsCanvas.Children)
+ {
+ if (child != null)
+ {
+ labelProvider.ReleaseLabel(child);
+ }
+ }
+
+ labels = null;
+ commonLabelsCanvas.Children.Clear();
+ }
+
+ commonLabelsCanvas = (StackCanvas)content.FindName(PART_CommonLabelsCanvas);
+ Verify.AssertNotNull(commonLabelsCanvas);
+ commonLabelsCanvas.Placement = placement;
+
+ if (additionalLabelsCanvas != null && majorLabelProvider != null)
+ {
+ foreach (UIElement child in additionalLabelsCanvas.Children)
+ {
+ if (child != null)
+ {
+ majorLabelProvider.ReleaseLabel(child);
+ }
+ }
+ }
+
+ additionalLabelsCanvas = (StackCanvas)content.FindName(PART_AdditionalLabelsCanvas);
+ Verify.AssertNotNull(additionalLabelsCanvas);
+ additionalLabelsCanvas.Placement = placement;
+
+ mainGrid = (Grid)content.FindName(PART_ContentsGrid);
+ Verify.AssertNotNull(mainGrid);
+
+ mainGrid.SetBinding(Control.BackgroundProperty, new Binding { Path = new PropertyPath("Background"), Source = this });
+ mainGrid.SizeChanged += new SizeChangedEventHandler(mainGrid_SizeChanged);
+
+ Content = mainGrid;
+
+ string transformKey = additionalLabelTransformKey + placement.ToString();
+ if (resources.Contains(transformKey))
+ {
+ additionalLabelTransform = (Transform)resources[transformKey];
+ }
+ }
+
+ void mainGrid_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (placement.IsBottomOrTop() && e.WidthChanged ||
+ e.HeightChanged)
+ {
+ // this is performed because if not, whole axisControl's size was measured wrongly.
+ InvalidateMeasure();
+ UpdateUI();
+ }
+ }
+
+ private bool updateOnCommonChange = true;
+
+ internal IDisposable OpenUpdateRegion(bool forceUpdate)
+ {
+ return new UpdateRegionHolder(this, forceUpdate);
+ }
+
+ private sealed class UpdateRegionHolder : IDisposable
+ {
+ private Range prevRange;
+ private CoordinateTransform prevTransform;
+ private AxisControl owner;
+ private bool forceUpdate = false;
+
+ public UpdateRegionHolder(AxisControl owner) : this(owner, false) { }
+
+ public UpdateRegionHolder(AxisControl owner, bool forceUpdate)
+ {
+ this.owner = owner;
+ owner.updateOnCommonChange = false;
+
+ prevTransform = owner.transform;
+ prevRange = owner.range;
+ this.forceUpdate = forceUpdate;
+ }
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ owner.updateOnCommonChange = true;
+
+ bool shouldUpdate = owner.range != prevRange;
+
+ var screenRect = owner.Transform.ScreenRect;
+ var prevScreenRect = prevTransform.ScreenRect;
+ if (owner.placement.IsBottomOrTop())
+ {
+ shouldUpdate |= prevScreenRect.Width != screenRect.Width;
+ }
+ else
+ {
+ shouldUpdate |= prevScreenRect.Height != screenRect.Height;
+ }
+
+ shouldUpdate |= owner.transform.DataTransform != prevTransform.DataTransform;
+ shouldUpdate |= forceUpdate;
+
+ if (shouldUpdate)
+ {
+ owner.UpdateUI();
+ }
+ owner = null;
+ }
+
+ #endregion
+ }
+
+ private Range range;
+ ///
+ /// Gets or sets the range, which ticks are generated for.
+ ///
+ /// The range.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Range Range
+ {
+ get { return range; }
+ set
+ {
+ range = value;
+ if (updateOnCommonChange)
+ {
+ UpdateUI();
+ }
+ }
+ }
+
+ private bool drawMinorTicks = true;
+ ///
+ /// Gets or sets a value indicating whether to show minor ticks.
+ ///
+ /// true if show minor ticks; otherwise, false.
+ public bool DrawMinorTicks
+ {
+ get { return drawMinorTicks; }
+ set
+ {
+ if (drawMinorTicks != value)
+ {
+ drawMinorTicks = value;
+ UpdateUI();
+ }
+ }
+ }
+
+ private bool drawMajorLabels = true;
+ ///
+ /// Gets or sets a value indicating whether to show major labels.
+ ///
+ /// true if show major labels; otherwise, false.
+ public bool DrawMajorLabels
+ {
+ get { return drawMajorLabels; }
+ set
+ {
+ if (drawMajorLabels != value)
+ {
+ drawMajorLabels = value;
+ UpdateUI();
+ }
+ }
+ }
+
+ private bool drawTicks = true;
+ public bool DrawTicks
+ {
+ get { return drawTicks; }
+ set
+ {
+ if (drawTicks != value)
+ {
+ drawTicks = value;
+ UpdateUI();
+ }
+ }
+ }
+
+ #region TicksProvider
+
+ private ITicksProvider ticksProvider;
+ ///
+ /// Gets or sets the ticks provider - generator of ticks for given range.
+ ///
+ /// Should not be null.
+ ///
+ /// The ticks provider.
+ public ITicksProvider TicksProvider
+ {
+ get { return ticksProvider; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (ticksProvider != value)
+ {
+ DetachTicksProvider();
+
+ ticksProvider = value;
+
+ AttachTicksProvider();
+
+ UpdateUI();
+ }
+ }
+ }
+
+ private void AttachTicksProvider()
+ {
+ if (ticksProvider != null)
+ {
+ ticksProvider.Changed += ticksProvider_Changed;
+ }
+ }
+
+ private void ticksProvider_Changed(object sender, EventArgs e)
+ {
+ UpdateUI();
+ }
+
+ private void DetachTicksProvider()
+ {
+ if (ticksProvider != null)
+ {
+ ticksProvider.Changed -= ticksProvider_Changed;
+ }
+ }
+
+ #endregion
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool ShouldSerializeContent()
+ {
+ return false;
+ }
+
+ protected override bool ShouldSerializeProperty(DependencyProperty dp)
+ {
+ // do not serialize template - for XAML serialization
+ if (dp == TemplateProperty) return false;
+
+ return base.ShouldSerializeProperty(dp);
+ }
+
+ #region MajorLabelProvider
+
+ private LabelProviderBase majorLabelProvider;
+ ///
+ /// 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 majorLabelProvider; }
+ set
+ {
+ if (majorLabelProvider != value)
+ {
+ DetachMajorLabelProvider();
+
+ majorLabelProvider = value;
+
+ AttachMajorLabelProvider();
+
+ UpdateUI();
+ }
+ }
+ }
+
+ private void AttachMajorLabelProvider()
+ {
+ if (majorLabelProvider != null)
+ {
+ majorLabelProvider.Changed += majorLabelProvider_Changed;
+ }
+ }
+
+ private void majorLabelProvider_Changed(object sender, EventArgs e)
+ {
+ UpdateUI();
+ }
+
+ private void DetachMajorLabelProvider()
+ {
+ if (majorLabelProvider != null)
+ {
+ majorLabelProvider.Changed -= majorLabelProvider_Changed;
+ }
+ }
+
+ #endregion
+
+ #region LabelProvider
+
+ private LabelProviderBase labelProvider;
+ ///
+ /// Gets or sets the label provider, which generates labels for axis ticks.
+ /// Should not be null.
+ ///
+ /// The label provider.
+ [NotNull]
+ public LabelProviderBase LabelProvider
+ {
+ get { return labelProvider; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (labelProvider != value)
+ {
+ DetachLabelProvider();
+
+ labelProvider = value;
+
+ AttachLabelProvider();
+
+ UpdateUI();
+ }
+ }
+ }
+
+ private void AttachLabelProvider()
+ {
+ if (labelProvider != null)
+ {
+ labelProvider.Changed += labelProvider_Changed;
+ }
+ }
+
+ private void labelProvider_Changed(object sender, EventArgs e)
+ {
+ UpdateUI();
+ }
+
+ private void DetachLabelProvider()
+ {
+ if (labelProvider != null)
+ {
+ labelProvider.Changed -= labelProvider_Changed;
+ }
+ }
+
+ #endregion
+
+ private CoordinateTransform transform = CoordinateTransform.CreateDefault();
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public CoordinateTransform Transform
+ {
+ get { return transform; }
+ set
+ {
+ transform = value;
+ if (updateOnCommonChange)
+ {
+ UpdateUI();
+ }
+ }
+ }
+
+ #endregion
+
+ private const double defaultSmallerSize = 1;
+ private const double defaultBiggerSize = 150;
+ protected override Size MeasureOverride(Size constraint)
+ {
+ var baseSize = base.MeasureOverride(constraint);
+
+ mainGrid.Measure(constraint);
+ Size gridSize = mainGrid.DesiredSize;
+ Size result = gridSize;
+
+ bool isHorizontal = placement == AxisPlacement.Bottom || placement == AxisPlacement.Top;
+ if (Double.IsInfinity(constraint.Width) && isHorizontal)
+ {
+ result = new Size(defaultBiggerSize, gridSize.Height != 0 ? gridSize.Height : defaultSmallerSize);
+ }
+ else if (Double.IsInfinity(constraint.Height) && !isHorizontal)
+ {
+ result = new Size(gridSize.Width != 0 ? gridSize.Width : defaultSmallerSize, defaultBiggerSize);
+ }
+
+ return result;
+ }
+
+ //protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+ //{
+ // base.OnRenderSizeChanged(sizeInfo);
+
+ // bool isHorizontal = placement == AxisPlacement.Top || placement == AxisPlacement.Bottom;
+ // if (isHorizontal && sizeInfo.WidthChanged || !isHorizontal && sizeInfo.HeightChanged)
+ // {
+ // UpdateUIRepresentation();
+ // }
+ //}
+
+ private void InitTransform(Size newRenderSize)
+ {
+ Rect dataRect = CreateDataRect();
+
+ transform = transform.WithRects(dataRect, new Rect(newRenderSize));
+ }
+
+ private Rect CreateDataRect()
+ {
+ double min = convertToDouble(range.Min);
+ double max = convertToDouble(range.Max);
+
+ Rect dataRect;
+ switch (placement)
+ {
+ case AxisPlacement.Left:
+ case AxisPlacement.Right:
+ dataRect = new Rect(new Point(min, min), new Point(max, max));
+ break;
+ case AxisPlacement.Top:
+ case AxisPlacement.Bottom:
+ dataRect = new Rect(new Point(min, min), new Point(max, max));
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ return dataRect;
+ }
+
+ ///
+ /// Gets the Path with ticks strokes.
+ ///
+ /// The ticks path.
+ public Path TicksPath
+ {
+ get { return ticksPath; }
+ }
+
+ private Grid mainGrid;
+ private StackCanvas additionalLabelsCanvas;
+ private StackCanvas commonLabelsCanvas;
+ private Path ticksPath;
+ private bool rendered = false;
+ protected override void OnRender(DrawingContext dc)
+ {
+ base.OnRender(dc);
+
+ if (!rendered)
+ {
+ UpdateUI();
+ }
+ rendered = true;
+ }
+
+ private bool independent = true;
+
+ private double scrCoord1 = 0; // px
+ private double scrCoord2 = 10; // px
+ ///
+ /// Gets or sets the size of main axis ticks.
+ ///
+ /// The size of the tick.
+ public double TickSize
+ {
+ get { return scrCoord2; }
+ set
+ {
+ if (scrCoord2 != value)
+ {
+ scrCoord2 = value;
+ UpdateUI();
+ }
+ }
+ }
+
+ private GeometryGroup geomGroup = new GeometryGroup();
+ internal void UpdateUI()
+ {
+ if (range.IsEmpty)
+ return;
+
+ if (transform == null) return;
+
+ if (independent)
+ {
+ InitTransform(RenderSize);
+ }
+
+ bool isHorizontal = Placement == AxisPlacement.Bottom || Placement == AxisPlacement.Top;
+ if (transform.ScreenRect.Width == 0 && isHorizontal
+ || transform.ScreenRect.Height == 0 && !isHorizontal)
+ return;
+
+ if (!IsMeasureValid)
+ {
+ InvalidateMeasure();
+ }
+
+ CreateTicks();
+
+ // removing unfinite screen ticks
+ var tempTicks = new List(ticks);
+ var tempScreenTicks = new List(ticks.Length);
+ var tempLabels = new List(labels);
+
+ int i = 0;
+ while (i < tempTicks.Count)
+ {
+ T tick = tempTicks[i];
+ double screenTick = getCoordinate(createDataPoint(convertToDouble(tick)).DataToScreen(transform));
+ if (screenTick.IsFinite())
+ {
+ tempScreenTicks.Add(screenTick);
+ i++;
+ }
+ else
+ {
+ tempTicks.RemoveAt(i);
+ tempLabels.RemoveAt(i);
+ }
+ }
+
+ ticks = tempTicks.ToArray();
+ screenTicks = tempScreenTicks.ToArray();
+ labels = tempLabels.ToArray();
+
+ // saving generated lines into pool
+ for (i = 0; i < geomGroup.Children.Count; i++)
+ {
+ var geometry = (LineGeometry)geomGroup.Children[i];
+ lineGeomPool.Put(geometry);
+ }
+
+ geomGroup = new GeometryGroup();
+ geomGroup.Children = new GeometryCollection(lineGeomPool.Count);
+
+ if (drawTicks)
+ DoDrawTicks(screenTicks, geomGroup.Children);
+
+ if (drawMinorTicks)
+ DoDrawMinorTicks(geomGroup.Children);
+
+ ticksPath.Data = geomGroup;
+
+ DoDrawCommonLabels(screenTicks);
+
+ if (drawMajorLabels)
+ DoDrawMajorLabels();
+
+ ScreenTicksChanged.Raise(this);
+ }
+
+ bool drawTicksOnEmptyLabel = false;
+ ///
+ /// Gets or sets a value indicating whether to draw ticks on empty label.
+ ///
+ ///
+ /// true if draw ticks on empty label; otherwise, false.
+ ///
+ public bool DrawTicksOnEmptyLabel
+ {
+ get { return drawTicksOnEmptyLabel; }
+ set
+ {
+ if (drawTicksOnEmptyLabel != value)
+ {
+ drawTicksOnEmptyLabel = value;
+ UpdateUI();
+ }
+ }
+ }
+
+ private readonly ResourcePool lineGeomPool = new ResourcePool();
+ private void DoDrawTicks(double[] screenTicksX, ICollection lines)
+ {
+ for (int i = 0; i < screenTicksX.Length; i++)
+ {
+ if (labels[i] == null && !drawTicksOnEmptyLabel)
+ continue;
+
+ Point p1 = createScreenPoint1(screenTicksX[i]);
+ Point p2 = createScreenPoint2(screenTicksX[i], 1);
+
+ LineGeometry line = lineGeomPool.GetOrCreate();
+
+ line.StartPoint = p1;
+ line.EndPoint = p2;
+ lines.Add(line);
+ }
+ }
+
+ private double GetRangesRatio(Range nominator, Range denominator)
+ {
+ double nomMin = ConvertToDouble(nominator.Min);
+ double nomMax = ConvertToDouble(nominator.Max);
+ double denMin = ConvertToDouble(denominator.Min);
+ double denMax = ConvertToDouble(denominator.Max);
+
+ return (nomMax - nomMin) / (denMax - denMin);
+ }
+
+ Transform additionalLabelTransform = null;
+ private void DoDrawMajorLabels()
+ {
+ ITicksProvider majorTicksProvider = ticksProvider.MajorProvider;
+ additionalLabelsCanvas.Children.Clear();
+
+ if (majorTicksProvider != null && majorLabelProvider != null)
+ {
+ additionalLabelsCanvas.Visibility = Visibility.Visible;
+
+ Size renderSize = RenderSize;
+ var majorTicks = majorTicksProvider.GetTicks(range, DefaultTicksProvider.DefaultTicksCount);
+
+ double[] screenCoords = majorTicks.Ticks.Select(tick => createDataPoint(convertToDouble(tick))).
+ Select(p => p.DataToScreen(transform)).Select(p => getCoordinate(p)).ToArray();
+
+ // todo this is not the best decision - when displaying, for example,
+ // milliseconds, it causes to create hundreds and thousands of textBlocks.
+ double rangesRatio = GetRangesRatio(majorTicks.Ticks.GetPairs().ToArray()[0], range);
+
+ object info = majorTicks.Info;
+ MajorLabelsInfo newInfo = new MajorLabelsInfo
+ {
+ Info = info,
+ MajorLabelsCount = (int)Math.Ceiling(rangesRatio)
+ };
+
+ var newMajorTicks = new TicksInfo
+ {
+ Info = newInfo,
+ Ticks = majorTicks.Ticks,
+ TickSizes = majorTicks.TickSizes
+ };
+
+ UIElement[] additionalLabels = MajorLabelProvider.CreateLabels(newMajorTicks);
+
+ for (int i = 0; i < additionalLabels.Length; i++)
+ {
+ if (screenCoords[i].IsNaN())
+ continue;
+
+ UIElement tickLabel = additionalLabels[i];
+
+ tickLabel.Measure(renderSize);
+
+ StackCanvas.SetCoordinate(tickLabel, screenCoords[i]);
+ StackCanvas.SetEndCoordinate(tickLabel, screenCoords[i + 1]);
+
+ if (tickLabel is FrameworkElement)
+ ((FrameworkElement)tickLabel).LayoutTransform = additionalLabelTransform;
+
+ additionalLabelsCanvas.Children.Add(tickLabel);
+ }
+ }
+ else
+ {
+ additionalLabelsCanvas.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ private int prevMinorTicksCount = DefaultTicksProvider.DefaultTicksCount;
+ private const int maxTickArrangeIterations = 12;
+ private void DoDrawMinorTicks(ICollection lines)
+ {
+ ITicksProvider minorTicksProvider = ticksProvider.MinorProvider;
+ if (minorTicksProvider != null)
+ {
+ int minorTicksCount = prevMinorTicksCount;
+ int prevActualTicksCount = -1;
+ ITicksInfo minorTicks;
+ TickCountChange result = TickCountChange.OK;
+ TickCountChange prevResult;
+ int iteration = 0;
+ do
+ {
+ Verify.IsTrue(++iteration < maxTickArrangeIterations);
+
+ minorTicks = minorTicksProvider.GetTicks(range, minorTicksCount);
+
+ prevActualTicksCount = minorTicks.Ticks.Length;
+ prevResult = result;
+ result = CheckMinorTicksArrangement(minorTicks);
+ if (prevResult == TickCountChange.Decrease && result == TickCountChange.Increase)
+ {
+ // stop tick number oscillating
+ result = TickCountChange.OK;
+ }
+ if (result == TickCountChange.Decrease)
+ {
+ int newMinorTicksCount = minorTicksProvider.DecreaseTickCount(minorTicksCount);
+ if (newMinorTicksCount == minorTicksCount)
+ {
+ result = TickCountChange.OK;
+ }
+ minorTicksCount = newMinorTicksCount;
+ }
+ else if (result == TickCountChange.Increase)
+ {
+ int newCount = minorTicksProvider.IncreaseTickCount(minorTicksCount);
+ if (newCount == minorTicksCount)
+ {
+ result = TickCountChange.OK;
+ }
+ minorTicksCount = newCount;
+ }
+
+ } while (result != TickCountChange.OK);
+ prevMinorTicksCount = minorTicksCount;
+
+ double[] sizes = minorTicks.TickSizes;
+
+ double[] screenCoords = minorTicks.Ticks.Select(
+ coord => getCoordinate(createDataPoint(convertToDouble(coord)).
+ DataToScreen(transform))).ToArray();
+
+ minorScreenTicks = new MinorTickInfo[screenCoords.Length];
+ for (int i = 0; i < screenCoords.Length; i++)
+ {
+ minorScreenTicks[i] = new MinorTickInfo(sizes[i], screenCoords[i]);
+ }
+
+ for (int i = 0; i < screenCoords.Length; i++)
+ {
+ double screenCoord = screenCoords[i];
+
+ Point p1 = createScreenPoint1(screenCoord);
+ Point p2 = createScreenPoint2(screenCoord, sizes[i]);
+
+ LineGeometry line = lineGeomPool.GetOrCreate();
+ line.StartPoint = p1;
+ line.EndPoint = p2;
+
+ lines.Add(line);
+ }
+ }
+ }
+
+ private TickCountChange CheckMinorTicksArrangement(ITicksInfo minorTicks)
+ {
+ Size renderSize = RenderSize;
+ TickCountChange result = TickCountChange.OK;
+ if (minorTicks.Ticks.Length * 3 > getSize(renderSize))
+ result = TickCountChange.Decrease;
+ else if (minorTicks.Ticks.Length * 6 < getSize(renderSize))
+ result = TickCountChange.Increase;
+ return result;
+ }
+
+ private bool isStaticAxis = false;
+ ///
+ /// Gets or sets a value indicating whether this instance is a static axis.
+ /// If axis is static, its labels from sides are shifted so that they are not clipped by axis bounds.
+ ///
+ ///
+ /// true if this instance is static axis; otherwise, false.
+ ///
+ public bool IsStaticAxis
+ {
+ get { return isStaticAxis; }
+ set
+ {
+ if (isStaticAxis != value)
+ {
+ isStaticAxis = value;
+ UpdateUI();
+ }
+ }
+ }
+
+ private double ToScreen(T value)
+ {
+ return getCoordinate(createDataPoint(convertToDouble(value)).DataToScreen(transform));
+ }
+
+ private double staticAxisMargin = 1; // px
+
+ private void DoDrawCommonLabels(double[] screenTicksX)
+ {
+ Size renderSize = RenderSize;
+
+ commonLabelsCanvas.Children.Clear();
+
+#if DEBUG
+ if (labels != null)
+ {
+ foreach (FrameworkElement item in labels)
+ {
+ if (item != null)
+ Debug.Assert(item.Parent == null);
+ }
+ }
+#endif
+
+ double minCoordUnsorted = ToScreen(range.Min);
+ double maxCoordUnsorted = ToScreen(range.Max);
+
+ double minCoord = Math.Min(minCoordUnsorted, maxCoordUnsorted);
+ double maxCoord = Math.Max(minCoordUnsorted, maxCoordUnsorted);
+
+ double maxCoordDiff = (maxCoord - minCoord) / labels.Length / 2.0;
+
+ double minCoordToAdd = minCoord - maxCoordDiff;
+ double maxCoordToAdd = maxCoord + maxCoordDiff;
+
+ for (int i = 0; i < ticks.Length; i++)
+ {
+ FrameworkElement tickLabel = (FrameworkElement)labels[i];
+ if (tickLabel == null) continue;
+
+ Debug.Assert(((FrameworkElement)tickLabel).Parent == null);
+
+ tickLabel.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
+
+ double screenX = screenTicksX[i];
+ double coord = screenX;
+
+ tickLabel.HorizontalAlignment = HorizontalAlignment.Center;
+ tickLabel.VerticalAlignment = VerticalAlignment.Center;
+
+ if (isStaticAxis)
+ {
+ // getting real size of label
+ tickLabel.Measure(renderSize);
+ Size tickLabelSize = tickLabel.DesiredSize;
+
+ if (Math.Abs(screenX - minCoord) < maxCoordDiff)
+ {
+ coord = minCoord + staticAxisMargin;
+ if (placement.IsBottomOrTop())
+ tickLabel.HorizontalAlignment = HorizontalAlignment.Left;
+ else
+ tickLabel.VerticalAlignment = VerticalAlignment.Top;
+ }
+ else if (Math.Abs(screenX - maxCoord) < maxCoordDiff)
+ {
+ coord = maxCoord - getSize(tickLabelSize) / 2 - staticAxisMargin;
+ if (!placement.IsBottomOrTop())
+ {
+ tickLabel.VerticalAlignment = VerticalAlignment.Bottom;
+ coord = maxCoord - staticAxisMargin;
+ }
+ }
+ }
+
+ // label is out of visible area
+ if (coord < minCoord || coord > maxCoord)
+ {
+ continue;
+ }
+
+ if (coord.IsNaN())
+ continue;
+
+ StackCanvas.SetCoordinate(tickLabel, coord);
+
+ commonLabelsCanvas.Children.Add(tickLabel);
+ }
+ }
+
+ private double GetCoordinateFromTick(T tick)
+ {
+ return getCoordinate(createDataPoint(convertToDouble(tick)).DataToScreen(transform));
+ }
+
+ private Func convertToDouble;
+ ///
+ /// Gets or sets the convertion of tick to double.
+ /// Should not be null.
+ ///
+ /// The convert to double.
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Func ConvertToDouble
+ {
+ get { return convertToDouble; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ convertToDouble = value;
+ UpdateUI();
+ }
+ }
+
+ internal event EventHandler ScreenTicksChanged;
+ private double[] screenTicks;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public double[] ScreenTicks
+ {
+ get { return screenTicks; }
+ }
+
+ private MinorTickInfo[] minorScreenTicks;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MinorTickInfo[] MinorScreenTicks
+ {
+ get { return minorScreenTicks; }
+ }
+
+ ITicksInfo ticksInfo;
+ private T[] ticks;
+ private UIElement[] labels;
+ private const double increaseRatio = 3.0;
+ private const double decreaseRatio = 1.6;
+
+ private Func getSize = size => size.Width;
+ private Func getCoordinate = p => p.X;
+ private Func createDataPoint = d => new Point(d, 0);
+
+ private Func createScreenPoint1 = d => new Point(d, 0);
+ private Func createScreenPoint2 = (d, size) => new Point(d, size);
+
+ private int previousTickCount = DefaultTicksProvider.DefaultTicksCount;
+ private void CreateTicks()
+ {
+ TickCountChange result = TickCountChange.OK;
+ TickCountChange prevResult;
+
+ int prevActualTickCount = -1;
+
+ int tickCount = previousTickCount;
+ int iteration = 0;
+
+ do
+ {
+ Verify.IsTrue(++iteration < maxTickArrangeIterations);
+
+ ticksInfo = ticksProvider.GetTicks(range, tickCount);
+ ticks = ticksInfo.Ticks;
+
+ if (ticks.Length == prevActualTickCount)
+ {
+ result = TickCountChange.OK;
+ break;
+ }
+
+ prevActualTickCount = ticks.Length;
+
+ if (labels != null)
+ {
+ for (int i = 0; i < labels.Length; i++)
+ {
+ labelProvider.ReleaseLabel(labels[i]);
+ }
+ }
+
+ labels = labelProvider.CreateLabels(ticksInfo);
+
+ prevResult = result;
+ result = CheckLabelsArrangement(labels, ticks);
+
+ if (prevResult == TickCountChange.Decrease && result == TickCountChange.Increase)
+ {
+ // stop tick number oscillating
+ result = TickCountChange.OK;
+ }
+
+ if (result != TickCountChange.OK)
+ {
+ int prevTickCount = tickCount;
+ if (result == TickCountChange.Decrease)
+ tickCount = ticksProvider.DecreaseTickCount(tickCount);
+ else
+ {
+ tickCount = ticksProvider.IncreaseTickCount(tickCount);
+ //DebugVerify.Is(tickCount >= prevTickCount);
+ }
+
+ // ticks provider could not create less ticks or tick number didn't change
+ if (tickCount == 0 || prevTickCount == tickCount)
+ {
+ tickCount = prevTickCount;
+ result = TickCountChange.OK;
+ }
+ }
+ } while (result != TickCountChange.OK);
+
+ previousTickCount = tickCount;
+ }
+
+ private TickCountChange CheckLabelsArrangement(UIElement[] labels, T[] ticks)
+ {
+ var actualLabels = labels.Select((label, i) => new { Label = label, Index = i })
+ .Where(el => el.Label != null)
+ .Select(el => new { Label = el.Label, Tick = ticks[el.Index] })
+ .ToList();
+
+ actualLabels.ForEach(item => item.Label.Measure(RenderSize));
+
+ var sizeInfos = actualLabels.Select(item =>
+ new { X = GetCoordinateFromTick(item.Tick), Size = getSize(item.Label.DesiredSize) })
+ .OrderBy(item => item.X).ToArray();
+
+ TickCountChange res = TickCountChange.OK;
+
+ int increaseCount = 0;
+ for (int i = 0; i < sizeInfos.Length - 1; i++)
+ {
+ if ((sizeInfos[i].X + sizeInfos[i].Size * decreaseRatio) > sizeInfos[i + 1].X)
+ {
+ res = TickCountChange.Decrease;
+ break;
+ }
+ if ((sizeInfos[i].X + sizeInfos[i].Size * increaseRatio) < sizeInfos[i + 1].X)
+ {
+ increaseCount++;
+ }
+ }
+ if (increaseCount > sizeInfos.Length / 2)
+ res = TickCountChange.Increase;
+
+ return res;
+ }
+ }
+
+ [DebuggerDisplay("{X} + {Size}")]
+ internal sealed class SizeInfo : IComparable
+ {
+ public double Size { get; set; }
+ public double X { get; set; }
+
+
+ public int CompareTo(SizeInfo other)
+ {
+ return X.CompareTo(other.X);
+ }
+ }
+
+ internal enum TickCountChange
+ {
+ Increase = -1,
+ OK = 0,
+ Decrease = 1
+ }
+
+ ///
+ /// Represents an auxiliary structure for storing additional info during major DateTime labels generation.
+ ///
+ public struct MajorLabelsInfo
+ {
+ public object Info { get; set; }
+ public int MajorLabelsCount { get; set; }
+
+ public override string ToString()
+ {
+ return String.Format("{0}, Count={1}", Info, MajorLabelsCount);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/AxisControlBase.cs b/Backup/Charts/Axes/AxisControlBase.cs
new file mode 100644
index 0000000..6d58ec2
--- /dev/null
+++ b/Backup/Charts/Axes/AxisControlBase.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public abstract class AxisControlBase : ContentControl
+ {
+ #region Properties
+
+ public HorizontalAlignment LabelsHorizontalAlignment
+ {
+ get { return (HorizontalAlignment)GetValue(LabelsHorizontalAlignmentProperty); }
+ set { SetValue(LabelsHorizontalAlignmentProperty, value); }
+ }
+
+ public static readonly DependencyProperty LabelsHorizontalAlignmentProperty = DependencyProperty.Register(
+ "LabelsHorizontalAlignment",
+ typeof(HorizontalAlignment),
+ typeof(AxisControlBase),
+ new FrameworkPropertyMetadata(HorizontalAlignment.Center));
+
+
+ public VerticalAlignment LabelsVerticalAlignment
+ {
+ get { return (VerticalAlignment)GetValue(LabelsVerticalAlignmentProperty); }
+ set { SetValue(LabelsVerticalAlignmentProperty, value); }
+ }
+
+ public static readonly DependencyProperty LabelsVerticalAlignmentProperty = DependencyProperty.Register(
+ "LabelsVerticalAlignment",
+ typeof(VerticalAlignment),
+ typeof(AxisControlBase),
+ new FrameworkPropertyMetadata(VerticalAlignment.Center));
+
+ #endregion // end of Properties
+
+ }
+}
diff --git a/Backup/Charts/Axes/AxisControlStyle.xaml b/Backup/Charts/Axes/AxisControlStyle.xaml
new file mode 100644
index 0000000..5099441
--- /dev/null
+++ b/Backup/Charts/Axes/AxisControlStyle.xaml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Backup/Charts/Axes/AxisGrid.cs b/Backup/Charts/Axes/AxisGrid.cs
new file mode 100644
index 0000000..819e040
--- /dev/null
+++ b/Backup/Charts/Axes/AxisGrid.cs
@@ -0,0 +1,287 @@
+using System.Windows;
+using System.Windows.Media;
+using Microsoft.Research.DynamicDataDisplay.Charts;
+using System.Windows.Controls;
+using System;
+using System.Windows.Shapes;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Research.DynamicDataDisplay.Common;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+
+namespace Microsoft.Research.DynamicDataDisplay
+{
+ ///
+ /// Draws grid over viewport. Number of
+ /// grid lines depends on Plotter's MainHorizontalAxis and MainVerticalAxis ticks.
+ ///
+ public class AxisGrid : ContentControl, IPlotterElement
+ {
+ static AxisGrid()
+ {
+ Type thisType = typeof(AxisGrid);
+ Panel.ZIndexProperty.OverrideMetadata(thisType, new FrameworkPropertyMetadata(-1));
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
+ internal void BeginTicksUpdate()
+ {
+ }
+ internal void EndTicksUpdate()
+ {
+ UpdateUIRepresentation();
+ }
+
+ protected internal MinorTickInfo[] MinorHorizontalTicks { get; set; }
+
+ protected internal MinorTickInfo[] MinorVerticalTicks { get; set; }
+
+ protected internal double[] HorizontalTicks { get; set; }
+
+ protected internal double[] VerticalTicks { get; set; }
+
+
+ private bool drawVerticalTicks = true;
+ ///
+ /// Gets or sets a value indicating whether to draw vertical tick lines.
+ ///
+ /// true if draw vertical ticks; otherwise, false.
+ public bool DrawVerticalTicks
+ {
+ get { return drawVerticalTicks; }
+ set
+ {
+ if (drawVerticalTicks != value)
+ {
+ drawVerticalTicks = value;
+ UpdateUIRepresentation();
+ }
+ }
+ }
+
+ private bool drawHorizontalTicks = true;
+ ///
+ /// Gets or sets a value indicating whether to draw horizontal tick lines.
+ ///
+ /// true if draw horizontal ticks; otherwise, false.
+ public bool DrawHorizontalTicks
+ {
+ get { return drawHorizontalTicks; }
+ set
+ {
+ if (drawHorizontalTicks != value)
+ {
+ drawHorizontalTicks = value;
+ UpdateUIRepresentation();
+ }
+ }
+ }
+
+ private bool drawHorizontalMinorTicks = false;
+ ///
+ /// Gets or sets a value indicating whether to draw horizontal minor ticks.
+ ///
+ ///
+ /// true if draw horizontal minor ticks; otherwise, false.
+ ///
+ public bool DrawHorizontalMinorTicks
+ {
+ get { return drawHorizontalMinorTicks; }
+ set
+ {
+ if (drawHorizontalMinorTicks != value)
+ {
+ drawHorizontalMinorTicks = value;
+ UpdateUIRepresentation();
+ }
+ }
+ }
+
+ private bool drawVerticalMinorTicks = false;
+ ///
+ /// Gets or sets a value indicating whether to draw vertical minor ticks.
+ ///
+ ///
+ /// true if draw vertical minor ticks; otherwise, false.
+ ///
+ public bool DrawVerticalMinorTicks
+ {
+ get { return drawVerticalMinorTicks; }
+ set
+ {
+ if (drawVerticalMinorTicks != value)
+ {
+ drawVerticalMinorTicks = value;
+ UpdateUIRepresentation();
+ }
+ }
+ }
+
+ private double gridBrushThickness = 1;
+
+ private Path path = new Path();
+ private Canvas canvas = new Canvas();
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AxisGrid()
+ {
+ IsHitTestVisible = false;
+
+ canvas.ClipToBounds = true;
+
+ path.Stroke = Brushes.LightGray;
+ path.StrokeThickness = gridBrushThickness;
+
+ Content = canvas;
+ }
+
+ private readonly ResourcePool lineGeometryPool = new ResourcePool();
+ private readonly ResourcePool linePool = new ResourcePool();
+
+ private void UpdateUIRepresentation()
+ {
+ foreach (UIElement item in canvas.Children)
+ {
+ Line line = item as Line;
+ if (line != null)
+ {
+ linePool.Put(line);
+ }
+ }
+
+ canvas.Children.Clear();
+ Size size = RenderSize;
+
+ DrawMinorHorizontalTicks();
+ DrawMinorVerticalTicks();
+
+ GeometryGroup prevGroup = path.Data as GeometryGroup;
+ if (prevGroup != null)
+ {
+ foreach (LineGeometry geometry in prevGroup.Children)
+ {
+ lineGeometryPool.Put(geometry);
+ }
+ }
+
+ GeometryGroup group = new GeometryGroup();
+ if (HorizontalTicks != null && drawHorizontalTicks)
+ {
+ double minY = 0;
+ double maxY = size.Height;
+
+ for (int i = 0; i < HorizontalTicks.Length; i++)
+ {
+ double screenX = HorizontalTicks[i];
+ LineGeometry line = lineGeometryPool.GetOrCreate();
+ line.StartPoint = new Point(screenX, minY);
+ line.EndPoint = new Point(screenX, maxY);
+ group.Children.Add(line);
+ }
+ }
+
+ if (VerticalTicks != null && drawVerticalTicks)
+ {
+ double minX = 0;
+ double maxX = size.Width;
+
+ for (int i = 0; i < VerticalTicks.Length; i++)
+ {
+ double screenY = VerticalTicks[i];
+ LineGeometry line = lineGeometryPool.GetOrCreate();
+ line.StartPoint = new Point(minX, screenY);
+ line.EndPoint = new Point(maxX, screenY);
+ group.Children.Add(line);
+ }
+ }
+
+ canvas.Children.Add(path);
+ path.Data = group;
+ }
+
+ private void DrawMinorVerticalTicks()
+ {
+ Size size = RenderSize;
+ if (MinorVerticalTicks != null && drawVerticalMinorTicks)
+ {
+ double minX = 0;
+ double maxX = size.Width;
+
+ for (int i = 0; i < MinorVerticalTicks.Length; i++)
+ {
+ double screenY = MinorVerticalTicks[i].Tick;
+ if (screenY < 0)
+ continue;
+ if (screenY > size.Height)
+ continue;
+
+ Line line = linePool.GetOrCreate();
+
+ line.Y1 = screenY;
+ line.Y2 = screenY;
+ line.X1 = minX;
+ line.X2 = maxX;
+ line.Stroke = Brushes.LightGray;
+ line.StrokeThickness = MinorVerticalTicks[i].Value * gridBrushThickness;
+
+ canvas.Children.Add(line);
+ }
+ }
+ }
+
+ private void DrawMinorHorizontalTicks()
+ {
+ Size size = RenderSize;
+ if (MinorHorizontalTicks != null && drawHorizontalMinorTicks)
+ {
+ double minY = 0;
+ double maxY = size.Height;
+
+ for (int i = 0; i < MinorHorizontalTicks.Length; i++)
+ {
+ double screenX = MinorHorizontalTicks[i].Tick;
+ if (screenX < 0)
+ continue;
+ if (screenX > size.Width)
+ continue;
+
+ Line line = linePool.GetOrCreate();
+ line.X1 = screenX;
+ line.X2 = screenX;
+ line.Y1 = minY;
+ line.Y2 = maxY;
+ line.Stroke = Brushes.LightGray;
+ line.StrokeThickness = MinorHorizontalTicks[i].Value * gridBrushThickness;
+
+ canvas.Children.Add(line);
+ }
+ }
+ }
+
+ #region IPlotterElement Members
+
+ void IPlotterElement.OnPlotterAttached(Plotter plotter)
+ {
+ this.plotter = plotter;
+ plotter.CentralGrid.Children.Add(this);
+ }
+
+ void IPlotterElement.OnPlotterDetaching(Plotter plotter)
+ {
+ plotter.CentralGrid.Children.Remove(this);
+ this.plotter = null;
+ }
+
+ private Plotter plotter;
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Plotter Plotter
+ {
+ get { return plotter; }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Backup/Charts/Axes/AxisPlacement.cs b/Backup/Charts/Axes/AxisPlacement.cs
new file mode 100644
index 0000000..5c6ffd2
--- /dev/null
+++ b/Backup/Charts/Axes/AxisPlacement.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Defines the position of axis inside ChartPlotter.
+ ///
+ public enum AxisPlacement
+ {
+ ///
+ /// Axis is placed to the left.
+ ///
+ Left,
+ ///
+ /// Axis is placed to the right.
+ ///
+ Right,
+ ///
+ /// Axis is placed to the top.
+ ///
+ Top,
+ ///
+ /// Axis is placed to the bottom.
+ ///
+ Bottom
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeAxis.cs b/Backup/Charts/Axes/DateTime/DateTimeAxis.cs
new file mode 100644
index 0000000..2d71b19
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeAxis.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.ViewportRestrictions;
+using System.Windows.Media;
+using System.Windows;
+using System.Windows.Data;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Represents an axis with ticks of type.
+ ///
+ public class DateTimeAxis : AxisBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DateTimeAxis()
+ : base(new DateTimeAxisControl(), DoubleToDate,
+ dt => dt.Ticks / 10000000000.0)
+ {
+ AxisControl.SetBinding(MajorLabelBackgroundBrushProperty, new Binding("MajorLabelBackgroundBrush") { Source = this });
+ AxisControl.SetBinding(MajorLabelRectangleBorderPropertyProperty, new Binding("MajorLabelRectangleBorderProperty") { Source = this });
+ }
+
+ #region VisualProperties
+
+ ///
+ /// Gets or sets the major tick labels' background brush. This is a DependencyProperty.
+ ///
+ /// The major label background brush.
+ public Brush MajorLabelBackgroundBrush
+ {
+ get { return (Brush)GetValue(MajorLabelBackgroundBrushProperty); }
+ set { SetValue(MajorLabelBackgroundBrushProperty, value); }
+ }
+
+ public static readonly DependencyProperty MajorLabelBackgroundBrushProperty = DependencyProperty.Register(
+ "MajorLabelBackgroundBrush",
+ typeof(Brush),
+ typeof(DateTimeAxis),
+ new FrameworkPropertyMetadata(Brushes.Beige));
+
+
+ public Brush MajorLabelRectangleBorderProperty
+ {
+ get { return (Brush)GetValue(MajorLabelRectangleBorderPropertyProperty); }
+ set { SetValue(MajorLabelRectangleBorderPropertyProperty, value); }
+ }
+
+ public static readonly DependencyProperty MajorLabelRectangleBorderPropertyProperty = DependencyProperty.Register(
+ "MajorLabelRectangleBorderProperty",
+ typeof(Brush),
+ typeof(DateTimeAxis),
+ new FrameworkPropertyMetadata(Brushes.Peru));
+
+ #endregion // end of VisualProperties
+
+ private ViewportRestriction restriction = new DateTimeHorizontalAxisRestriction();
+ protected ViewportRestriction Restriction
+ {
+ get { return restriction; }
+ set { restriction = value; }
+ }
+
+ protected override void OnPlotterAttached(Plotter2D plotter)
+ {
+ base.OnPlotterAttached(plotter);
+
+ plotter.Viewport.Restrictions.Add(restriction);
+ }
+
+ protected override void OnPlotterDetaching(Plotter2D plotter)
+ {
+ plotter.Viewport.Restrictions.Remove(restriction);
+
+ base.OnPlotterDetaching(plotter);
+ }
+
+ private static readonly long minTicks = DateTime.MinValue.Ticks;
+ private static readonly long maxTicks = DateTime.MaxValue.Ticks;
+ private static DateTime DoubleToDate(double d)
+ {
+ long ticks = (long)(d * 10000000000L);
+
+ // todo should we throw an exception if number of ticks is too big or small?
+ if (ticks < minTicks)
+ ticks = minTicks;
+ else if (ticks > maxTicks)
+ ticks = maxTicks;
+
+ return new DateTime(ticks);
+ }
+
+ ///
+ /// 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 override void SetConversion(double min, DateTime minValue, double max, DateTime maxValue)
+ {
+ var conversion = new DateTimeToDoubleConversion(min, minValue, max, maxValue);
+
+ ConvertToDouble = conversion.ToDouble;
+ ConvertFromDouble = conversion.FromDouble;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeAxisControl.cs b/Backup/Charts/Axes/DateTime/DateTimeAxisControl.cs
new file mode 100644
index 0000000..c180cee
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeAxisControl.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// AxisControl for DateTime axes.
+ ///
+ public class DateTimeAxisControl : AxisControl
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DateTimeAxisControl()
+ {
+ LabelProvider = new DateTimeLabelProvider();
+ TicksProvider = new DateTimeTicksProvider();
+ MajorLabelProvider = new MajorDateTimeLabelProvider();
+
+ ConvertToDouble = dt => dt.Ticks;
+
+ Range = new Range(DateTime.Now, DateTime.Now.AddYears(1));
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeLabelProvider.cs b/Backup/Charts/Axes/DateTime/DateTimeLabelProvider.cs
new file mode 100644
index 0000000..21ab784
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeLabelProvider.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Represents a label provider for ticks.
+ ///
+ public class DateTimeLabelProvider : DateTimeLabelProviderBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DateTimeLabelProvider() { }
+
+ public override UIElement[] CreateLabels(ITicksInfo ticksInfo)
+ {
+ object info = ticksInfo.Info;
+ var ticks = ticksInfo.Ticks;
+
+ if (info is DifferenceIn)
+ {
+ DifferenceIn diff = (DifferenceIn)info;
+ DateFormat = GetDateFormat(diff);
+ }
+
+ LabelTickInfo tickInfo = new LabelTickInfo { Info = info };
+
+ UIElement[] res = new UIElement[ticks.Length];
+ for (int i = 0; i < ticks.Length; i++)
+ {
+ tickInfo.Tick = ticks[i];
+
+ string tickText = GetString(tickInfo);
+ UIElement label = new TextBlock { Text = tickText, ToolTip = ticks[i] };
+ ApplyCustomView(tickInfo, label);
+ res[i] = label;
+ }
+
+ return res;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeLabelProviderBase.cs b/Backup/Charts/Axes/DateTime/DateTimeLabelProviderBase.cs
new file mode 100644
index 0000000..37f3c86
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeLabelProviderBase.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
+using System.Globalization;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public abstract class DateTimeLabelProviderBase : LabelProviderBase
+ {
+ private string dateFormat;
+ protected string DateFormat
+ {
+ get { return dateFormat; }
+ set { dateFormat = value; }
+ }
+
+ protected override string GetStringCore(LabelTickInfo tickInfo)
+ {
+ return tickInfo.Tick.ToString(dateFormat);
+ }
+
+ protected virtual string GetDateFormat(DifferenceIn diff)
+ {
+ string format = null;
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ format = "yyyy";
+ break;
+ case DifferenceIn.Month:
+ format = "MMM";
+ break;
+ case DifferenceIn.Day:
+ format = "%d";
+ break;
+ case DifferenceIn.Hour:
+ format = "HH:mm";
+ break;
+ case DifferenceIn.Minute:
+ format = "%m";
+ break;
+ case DifferenceIn.Second:
+ format = "ss";
+ break;
+ case DifferenceIn.Millisecond:
+ format = "fff";
+ break;
+ default:
+ break;
+ }
+
+ return format;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeTicksProvider.cs b/Backup/Charts/Axes/DateTime/DateTimeTicksProvider.cs
new file mode 100644
index 0000000..fb9e88a
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeTicksProvider.cs
@@ -0,0 +1,307 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+using System.Globalization;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Represents a ticks provider for ticks of type.
+ ///
+ public class DateTimeTicksProvider : TimeTicksProviderBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DateTimeTicksProvider() { }
+
+ static DateTimeTicksProvider()
+ {
+ Providers.Add(DifferenceIn.Year, new YearDateTimeProvider());
+ Providers.Add(DifferenceIn.Month, new MonthDateTimeProvider());
+ Providers.Add(DifferenceIn.Day, new DayDateTimeProvider());
+ Providers.Add(DifferenceIn.Hour, new HourDateTimeProvider());
+ Providers.Add(DifferenceIn.Minute, new MinuteDateTimeProvider());
+ Providers.Add(DifferenceIn.Second, new SecondDateTimeProvider());
+ Providers.Add(DifferenceIn.Millisecond, new MillisecondDateTimeProvider());
+
+ MinorProviders.Add(DifferenceIn.Year, new MinorDateTimeProvider(new YearDateTimeProvider()));
+ MinorProviders.Add(DifferenceIn.Month, new MinorDateTimeProvider(new MonthDateTimeProvider()));
+ MinorProviders.Add(DifferenceIn.Day, new MinorDateTimeProvider(new DayDateTimeProvider()));
+ MinorProviders.Add(DifferenceIn.Hour, new MinorDateTimeProvider(new HourDateTimeProvider()));
+ MinorProviders.Add(DifferenceIn.Minute, new MinorDateTimeProvider(new MinuteDateTimeProvider()));
+ MinorProviders.Add(DifferenceIn.Second, new MinorDateTimeProvider(new SecondDateTimeProvider()));
+ MinorProviders.Add(DifferenceIn.Millisecond, new MinorDateTimeProvider(new MillisecondDateTimeProvider()));
+ }
+
+ protected sealed override TimeSpan GetDifference(DateTime start, DateTime end)
+ {
+ return end - start;
+ }
+ }
+
+ internal static class DateTimeArrayExtensions
+ {
+ internal static int GetIndex(this DateTime[] array, DateTime value)
+ {
+ for (int i = 0; i < array.Length - 1; i++)
+ {
+ if (array[i] <= value && value < array[i + 1])
+ return i;
+ }
+
+ return array.Length - 1;
+ }
+ }
+
+ internal sealed class MinorDateTimeProvider : MinorTimeProviderBase
+ {
+ public MinorDateTimeProvider(ITicksProvider owner) : base(owner) { }
+
+ protected override bool IsInside(DateTime value, Range range)
+ {
+ return range.Min < value && value < range.Max;
+ }
+ }
+
+ internal sealed class YearDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Year;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 20, 10, 5, 4, 2, 1 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return dt.Year;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ int year = start.Year;
+ int newYear = (year / step) * step;
+ if (newYear == 0) newYear = 1;
+
+ return new DateTime(newYear, 1, 1);
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return dt.Year == DateTime.MinValue.Year;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ if (dt.Year + step > DateTime.MaxValue.Year)
+ return DateTime.MaxValue;
+
+ return dt.AddYears(step);
+ }
+ }
+
+ internal sealed class MonthDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Month;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 12, 6, 4, 3, 2, 1 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return dt.Month + (dt.Year - start.Year) * 12;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ return new DateTime(start.Year, 1, 1);
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return dt.Month == DateTime.MinValue.Month;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ return dt.AddMonths(step);
+ }
+ }
+
+ internal sealed class DayDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Day;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 30, 15, 10, 5, 2, 1 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return (dt - start).Days;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ return start.Date;
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return dt.Day == 1;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ return dt.AddDays(step);
+ }
+ }
+
+ internal sealed class HourDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Hour;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 24, 12, 6, 4, 3, 2, 1 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return (int)(dt - start).TotalHours;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ return start.Date;
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return false;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ return dt.AddHours(step);
+ }
+ }
+
+ internal sealed class MinuteDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Minute;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 60, 30, 20, 15, 10, 5, 4, 3, 2 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return (int)(dt - start).TotalMinutes;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ return start.Date.AddHours(start.Hour);
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return false;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ return dt.AddMinutes(step);
+ }
+ }
+
+ internal sealed class SecondDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Second;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 60, 30, 20, 15, 10, 5, 4, 3, 2 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return (int)(dt - start).TotalSeconds;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ return start.Date.AddHours(start.Hour).AddMinutes(start.Minute);
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return false;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ return dt.AddSeconds(step);
+ }
+ }
+
+ internal sealed class MillisecondDateTimeProvider : DatePeriodTicksProvider
+ {
+ protected override DifferenceIn GetDifferenceCore()
+ {
+ return DifferenceIn.Millisecond;
+ }
+
+ protected override int[] GetTickCountsCore()
+ {
+ return new int[] { 100, 50, 40, 25, 20, 10, 5, 4, 2 };
+ }
+
+ protected override int GetSpecificValue(DateTime start, DateTime dt)
+ {
+ return (int)(dt - start).TotalMilliseconds;
+ }
+
+ protected override DateTime GetStart(DateTime start, int value, int step)
+ {
+ return start.Date.AddHours(start.Hour).AddMinutes(start.Minute).AddSeconds(start.Second);
+ }
+
+ protected override bool IsMinDate(DateTime dt)
+ {
+ return false;
+ }
+
+ protected override DateTime AddStep(DateTime dt, int step)
+ {
+ return dt.AddMilliseconds(step);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeTicksProviderBase.cs b/Backup/Charts/Axes/DateTime/DateTimeTicksProviderBase.cs
new file mode 100644
index 0000000..b93564c
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeTicksProviderBase.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public abstract class DateTimeTicksProviderBase : ITicksProvider
+ {
+ public event EventHandler Changed;
+ protected void RaiseChanged()
+ {
+ if (Changed != null)
+ {
+ Changed(this, EventArgs.Empty);
+ }
+ }
+
+ protected static DateTime Shift(DateTime dateTime, DifferenceIn diff)
+ {
+ DateTime res = dateTime;
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ res = res.AddYears(1);
+ break;
+ case DifferenceIn.Month:
+ res = res.AddMonths(1);
+ break;
+ case DifferenceIn.Day:
+ res = res.AddDays(1);
+ break;
+ case DifferenceIn.Hour:
+ res = res.AddHours(1);
+ break;
+ case DifferenceIn.Minute:
+ res = res.AddMinutes(1);
+ break;
+ case DifferenceIn.Second:
+ res = res.AddSeconds(1);
+ break;
+ case DifferenceIn.Millisecond:
+ res = res.AddMilliseconds(1);
+ break;
+ default:
+ break;
+ }
+
+ return res;
+ }
+
+ protected static DateTime RoundDown(DateTime dateTime, DifferenceIn diff)
+ {
+ DateTime res = dateTime;
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ res = new DateTime(dateTime.Year, 1, 1);
+ break;
+ case DifferenceIn.Month:
+ res = new DateTime(dateTime.Year, dateTime.Month, 1);
+ break;
+ case DifferenceIn.Day:
+ res = dateTime.Date;
+ break;
+ case DifferenceIn.Hour:
+ res = dateTime.Date.AddHours(dateTime.Hour);
+ break;
+ case DifferenceIn.Minute:
+ res = dateTime.Date.AddHours(dateTime.Hour).AddMinutes(dateTime.Minute);
+ break;
+ case DifferenceIn.Second:
+ res = dateTime.Date.AddHours(dateTime.Hour).AddMinutes(dateTime.Minute).AddSeconds(dateTime.Second);
+ break;
+ case DifferenceIn.Millisecond:
+ res = dateTime.Date.AddHours(dateTime.Hour).AddMinutes(dateTime.Minute).AddSeconds(dateTime.Second).AddMilliseconds(dateTime.Millisecond);
+ break;
+ default:
+ break;
+ }
+
+ DebugVerify.Is(res <= dateTime);
+
+ return res;
+ }
+
+ protected static DateTime RoundUp(DateTime dateTime, DifferenceIn diff)
+ {
+ DateTime res = RoundDown(dateTime, diff);
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ res = res.AddYears(1);
+ break;
+ case DifferenceIn.Month:
+ res = res.AddMonths(1);
+ break;
+ case DifferenceIn.Day:
+ res = res.AddDays(1);
+ break;
+ case DifferenceIn.Hour:
+ res = res.AddHours(1);
+ break;
+ case DifferenceIn.Minute:
+ res = res.AddMinutes(1);
+ break;
+ case DifferenceIn.Second:
+ res = res.AddSeconds(1);
+ break;
+ case DifferenceIn.Millisecond:
+ res = res.AddMilliseconds(1);
+ break;
+ default:
+ break;
+ }
+
+ return res;
+ }
+
+ #region ITicksProvider Members
+
+ public abstract ITicksInfo GetTicks(Range range, int ticksCount);
+ public abstract int DecreaseTickCount(int ticksCount);
+ public abstract int IncreaseTickCount(int ticksCount);
+ public abstract ITicksProvider MinorProvider { get; }
+ public abstract ITicksProvider MajorProvider { get; }
+
+ #endregion
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DateTimeToDoubleConversion.cs b/Backup/Charts/Axes/DateTime/DateTimeToDoubleConversion.cs
new file mode 100644
index 0000000..cf9ce58
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DateTimeToDoubleConversion.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ internal sealed class DateTimeToDoubleConversion
+ {
+ public DateTimeToDoubleConversion(double min, DateTime minDate, double max, DateTime maxDate)
+ {
+ this.min = min;
+ this.length = max - min;
+ this.ticksMin = minDate.Ticks;
+ this.ticksLength = maxDate.Ticks - ticksMin;
+ }
+
+ private double min;
+ private double length;
+ private long ticksMin;
+ private long ticksLength;
+
+ internal DateTime FromDouble(double d)
+ {
+ double ratio = (d - min) / length;
+ long tick = (long)(ticksMin + ticksLength * ratio);
+
+ tick = MathHelper.Clamp(tick, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks);
+
+ return new DateTime(tick);
+ }
+
+ internal double ToDouble(DateTime dt)
+ {
+ double ratio = (dt.Ticks - ticksMin) / (double)ticksLength;
+ return min + ratio * length;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/DifferenceIn.cs b/Backup/Charts/Axes/DateTime/DifferenceIn.cs
new file mode 100644
index 0000000..2e06414
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/DifferenceIn.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public enum DifferenceIn
+ {
+ Biggest = Year,
+
+ Year = 6,
+ Month = 5,
+ Day = 4,
+ Hour = 3,
+ Minute = 2,
+ Second = 1,
+ Millisecond = 0,
+
+ Smallest = Millisecond
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/HorizontalDateTimeAxis.cs b/Backup/Charts/Axes/DateTime/HorizontalDateTimeAxis.cs
new file mode 100644
index 0000000..98f19ae
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/HorizontalDateTimeAxis.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Represents an axis with ticks of type, which can be placed only from bottom or top of .
+ /// By default is placed from bottom.
+ ///
+ public class HorizontalDateTimeAxis : DateTimeAxis
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public HorizontalDateTimeAxis()
+ {
+ Placement = AxisPlacement.Bottom;
+ }
+
+ protected override void ValidatePlacement(AxisPlacement newPlacement)
+ {
+ if (newPlacement == AxisPlacement.Left || newPlacement == AxisPlacement.Right)
+ throw new ArgumentException(Strings.Exceptions.HorizontalAxisCannotBeVertical);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/MajorDateTimeLabelProvider.cs b/Backup/Charts/Axes/DateTime/MajorDateTimeLabelProvider.cs
new file mode 100644
index 0000000..33b3eda
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/MajorDateTimeLabelProvider.cs
@@ -0,0 +1,133 @@
+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.Shapes;
+using System.Diagnostics;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
+using System.Windows.Data;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Represents a label provider for major ticks of type.
+ ///
+ public class MajorDateTimeLabelProvider : DateTimeLabelProviderBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MajorDateTimeLabelProvider() { }
+
+ public override UIElement[] CreateLabels(ITicksInfo ticksInfo)
+ {
+ object info = ticksInfo.Info;
+ var ticks = ticksInfo.Ticks;
+ UIElement[] res = new UIElement[ticks.Length - 1];
+ int labelsNum = 3;
+
+ if (info is DifferenceIn)
+ {
+ DifferenceIn diff = (DifferenceIn)info;
+ DateFormat = GetDateFormat(diff);
+ }
+ else if (info is MajorLabelsInfo)
+ {
+ MajorLabelsInfo mInfo = (MajorLabelsInfo)info;
+ DifferenceIn diff = (DifferenceIn)mInfo.Info;
+ DateFormat = GetDateFormat(diff);
+ labelsNum = mInfo.MajorLabelsCount + 1;
+
+ //DebugVerify.Is(labelsNum < 100);
+ }
+
+ DebugVerify.Is(ticks.Length < 10);
+
+ LabelTickInfo tickInfo = new LabelTickInfo();
+ for (int i = 0; i < ticks.Length - 1; i++)
+ {
+ tickInfo.Info = info;
+ tickInfo.Tick = ticks[i];
+
+ string tickText = GetString(tickInfo);
+
+ Grid grid = new Grid { };
+
+ // doing binding as described at http://sdolha.spaces.live.com/blog/cns!4121802308C5AB4E!3724.entry?wa=wsignin1.0&sa=835372863
+
+ grid.SetBinding(Grid.BackgroundProperty, new Binding { Path = new PropertyPath("(0)", DateTimeAxis.MajorLabelBackgroundBrushProperty), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor) { AncestorType = typeof(AxisControlBase) } });
+ Rectangle rect = new Rectangle
+ {
+ StrokeThickness = 2
+ };
+ rect.SetBinding(Rectangle.StrokeProperty, new Binding { Path = new PropertyPath("(0)", DateTimeAxis.MajorLabelRectangleBorderPropertyProperty), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor) { AncestorType = typeof(AxisControlBase) } });
+
+ Grid.SetColumn(rect, 0);
+ Grid.SetColumnSpan(rect, labelsNum);
+
+ for (int j = 0; j < labelsNum; j++)
+ {
+ grid.ColumnDefinitions.Add(new ColumnDefinition());
+ }
+
+ grid.Children.Add(rect);
+
+ for (int j = 0; j < labelsNum; j++)
+ {
+ var tb = new TextBlock
+ {
+ Text = tickText,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ Margin = new Thickness(0, 3, 0, 3)
+ };
+ Grid.SetColumn(tb, j);
+ grid.Children.Add(tb);
+ }
+
+ ApplyCustomView(tickInfo, grid);
+
+ res[i] = grid;
+ }
+
+ return res;
+ }
+
+ protected override string GetDateFormat(DifferenceIn diff)
+ {
+ string format = null;
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ format = "yyyy";
+ break;
+ case DifferenceIn.Month:
+ format = "MMMM yyyy";
+ break;
+ case DifferenceIn.Day:
+ format = "%d MMMM yyyy";
+ break;
+ case DifferenceIn.Hour:
+ format = "HH:mm %d MMMM yyyy";
+ break;
+ case DifferenceIn.Minute:
+ format = "HH:mm %d MMMM yyyy";
+ break;
+ case DifferenceIn.Second:
+ format = "HH:mm:ss %d MMMM yyyy";
+ break;
+ case DifferenceIn.Millisecond:
+ format = "fff";
+ break;
+ default:
+ break;
+ }
+
+ return format;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/MinorTimeProviderBase.cs b/Backup/Charts/Axes/DateTime/MinorTimeProviderBase.cs
new file mode 100644
index 0000000..a004f4b
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/MinorTimeProviderBase.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ internal abstract class MinorTimeProviderBase : ITicksProvider
+ {
+ public event EventHandler Changed;
+ protected void RaiseChanged()
+ {
+ if (Changed != null)
+ {
+ Changed(this, EventArgs.Empty);
+ }
+ }
+
+ private readonly ITicksProvider provider;
+ public MinorTimeProviderBase(ITicksProvider provider)
+ {
+ this.provider = provider;
+ }
+
+ private T[] majorTicks = new T[] { };
+ internal void SetTicks(T[] ticks)
+ {
+ this.majorTicks = ticks;
+ }
+
+ private double ticksSize = 0.5;
+ public ITicksInfo GetTicks(Range range, int ticksCount)
+ {
+ if (majorTicks.Length == 0)
+ return new TicksInfo();
+
+ ticksCount /= majorTicks.Length;
+ if (ticksCount == 0)
+ ticksCount = 2;
+
+ var ticks = majorTicks.GetPairs().Select(r => Clip(provider.GetTicks(r, ticksCount), r)).
+ SelectMany(t => t.Ticks).ToArray();
+
+ var res = new TicksInfo
+ {
+ Ticks = ticks,
+ TickSizes = ArrayExtensions.CreateArray(ticks.Length, ticksSize)
+ };
+ return res;
+ }
+
+ private ITicksInfo Clip(ITicksInfo ticks, Range range)
+ {
+ var newTicks = new List(ticks.Ticks.Length);
+ var newSizes = new List(ticks.TickSizes.Length);
+
+ for (int i = 0; i < ticks.Ticks.Length; i++)
+ {
+ T tick = ticks.Ticks[i];
+ if (IsInside(tick, range))
+ {
+ newTicks.Add(tick);
+ newSizes.Add(ticks.TickSizes[i]);
+ }
+ }
+
+ return new TicksInfo
+ {
+ Ticks = newTicks.ToArray(),
+ TickSizes = newSizes.ToArray(),
+ Info = ticks.Info
+ };
+ }
+
+ protected abstract bool IsInside(T value, Range range);
+
+ public int DecreaseTickCount(int ticksCount)
+ {
+ if (majorTicks.Length > 0)
+ ticksCount /= majorTicks.Length;
+
+ int minorTicksCount = provider.DecreaseTickCount(ticksCount);
+
+ if (majorTicks.Length > 0)
+ minorTicksCount *= majorTicks.Length;
+
+ return minorTicksCount;
+ }
+
+ public int IncreaseTickCount(int ticksCount)
+ {
+ if (majorTicks.Length > 0)
+ ticksCount /= majorTicks.Length;
+
+ int minorTicksCount = provider.IncreaseTickCount(ticksCount);
+
+ if (majorTicks.Length > 0)
+ minorTicksCount *= majorTicks.Length;
+
+ return minorTicksCount;
+ }
+
+ public ITicksProvider MinorProvider
+ {
+ get { return null; }
+ }
+
+ public ITicksProvider MajorProvider
+ {
+ get { return null; }
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/Strategies/DefaultDateTimeTicksStrategy.cs b/Backup/Charts/Axes/DateTime/Strategies/DefaultDateTimeTicksStrategy.cs
new file mode 100644
index 0000000..9bbc2c3
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/Strategies/DefaultDateTimeTicksStrategy.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public class DefaultDateTimeTicksStrategy : IDateTimeTicksStrategy
+ {
+ public virtual DifferenceIn GetDifference(TimeSpan span)
+ {
+ span = span.Duration();
+
+ DifferenceIn diff;
+ if (span.Days > 365)
+ diff = DifferenceIn.Year;
+ else if (span.Days > 30)
+ diff = DifferenceIn.Month;
+ else if (span.Days > 0)
+ diff = DifferenceIn.Day;
+ else if (span.Hours > 0)
+ diff = DifferenceIn.Hour;
+ else if (span.Minutes > 0)
+ diff = DifferenceIn.Minute;
+ else if (span.Seconds > 0)
+ diff = DifferenceIn.Second;
+ else
+ diff = DifferenceIn.Millisecond;
+
+ return diff;
+ }
+
+ public virtual bool TryGetLowerDiff(DifferenceIn diff, out DifferenceIn lowerDiff)
+ {
+ lowerDiff = diff;
+
+ int code = (int)diff;
+ bool res = code > (int)DifferenceIn.Smallest;
+ if (res)
+ {
+ lowerDiff = (DifferenceIn)(code - 1);
+ }
+ return res;
+ }
+
+ public virtual bool TryGetBiggerDiff(DifferenceIn diff, out DifferenceIn biggerDiff)
+ {
+ biggerDiff = diff;
+
+ int code = (int)diff;
+ bool res = code < (int)DifferenceIn.Biggest;
+ if (res)
+ {
+ biggerDiff = (DifferenceIn)(code + 1);
+ }
+ return res;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/Strategies/DelegateStrategy.cs b/Backup/Charts/Axes/DateTime/Strategies/DelegateStrategy.cs
new file mode 100644
index 0000000..56e8c53
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/Strategies/DelegateStrategy.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.DateTime.Strategies
+{
+ public class DelegateDateTimeStrategy : DefaultDateTimeTicksStrategy
+ {
+ private readonly Func function;
+ public DelegateDateTimeStrategy(Func function)
+ {
+ if (function == null)
+ throw new ArgumentNullException("function");
+
+ this.function = function;
+ }
+
+ public override DifferenceIn GetDifference(TimeSpan span)
+ {
+ DifferenceIn? customResult = function(span);
+
+ DifferenceIn result = customResult.HasValue ?
+ customResult.Value :
+ base.GetDifference(span);
+
+ return result;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/Strategies/ExtendedDaysStrategy.cs b/Backup/Charts/Axes/DateTime/Strategies/ExtendedDaysStrategy.cs
new file mode 100644
index 0000000..bdf5a46
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/Strategies/ExtendedDaysStrategy.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public class ExtendedDaysStrategy : IDateTimeTicksStrategy
+ {
+ private static readonly DifferenceIn[] diffs = new DifferenceIn[] {
+ DifferenceIn.Year,
+ DifferenceIn.Day,
+ DifferenceIn.Hour,
+ DifferenceIn.Minute,
+ DifferenceIn.Second,
+ DifferenceIn.Millisecond
+ };
+
+ public DifferenceIn GetDifference(TimeSpan span)
+ {
+ span = span.Duration();
+
+ DifferenceIn diff;
+ if (span.Days > 365)
+ diff = DifferenceIn.Year;
+ else if (span.Days > 0)
+ diff = DifferenceIn.Day;
+ else if (span.Hours > 0)
+ diff = DifferenceIn.Hour;
+ else if (span.Minutes > 0)
+ diff = DifferenceIn.Minute;
+ else if (span.Seconds > 0)
+ diff = DifferenceIn.Second;
+ else
+ diff = DifferenceIn.Millisecond;
+
+ return diff;
+ }
+
+ public bool TryGetLowerDiff(DifferenceIn diff, out DifferenceIn lowerDiff)
+ {
+ lowerDiff = diff;
+
+ int index = Array.IndexOf(diffs, diff);
+ if (index == -1)
+ return false;
+
+ if (index == diffs.Length - 1)
+ return false;
+
+ lowerDiff = diffs[index + 1];
+ return true;
+ }
+
+ public bool TryGetBiggerDiff(DifferenceIn diff, out DifferenceIn biggerDiff)
+ {
+ biggerDiff = diff;
+
+ int index = Array.IndexOf(diffs, diff);
+ if (index == -1 || index == 0)
+ return false;
+
+ biggerDiff = diffs[index - 1];
+ return true;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/Strategies/IDateTimeTicksStrategy.cs b/Backup/Charts/Axes/DateTime/Strategies/IDateTimeTicksStrategy.cs
new file mode 100644
index 0000000..7df39b2
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/Strategies/IDateTimeTicksStrategy.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public interface IDateTimeTicksStrategy
+ {
+ DifferenceIn GetDifference(TimeSpan span);
+ bool TryGetLowerDiff(DifferenceIn diff, out DifferenceIn lowerDiff);
+ bool TryGetBiggerDiff(DifferenceIn diff, out DifferenceIn biggerDiff);
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/TimePeriodTicksProvider.cs b/Backup/Charts/Axes/DateTime/TimePeriodTicksProvider.cs
new file mode 100644
index 0000000..bf1219a
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/TimePeriodTicksProvider.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ internal abstract class TimePeriodTicksProvider : ITicksProvider
+ {
+ public event EventHandler Changed;
+ protected void RaiseChanged()
+ {
+ if (Changed != null)
+ {
+ Changed(this, EventArgs.Empty);
+ }
+ }
+
+ protected abstract T RoundUp(T time, DifferenceIn diff);
+ protected abstract T RoundDown(T time, DifferenceIn diff);
+
+ private bool differenceInited = false;
+ private DifferenceIn difference;
+ protected DifferenceIn Difference
+ {
+ get
+ {
+ if (!differenceInited)
+ {
+ difference = GetDifferenceCore();
+ differenceInited = true;
+ }
+ return difference;
+ }
+ }
+ protected abstract DifferenceIn GetDifferenceCore();
+
+ private int[] tickCounts = null;
+ protected int[] TickCounts
+ {
+ get
+ {
+ if (tickCounts == null)
+ tickCounts = GetTickCountsCore();
+ return tickCounts;
+ }
+ }
+ protected abstract int[] GetTickCountsCore();
+
+ public int DecreaseTickCount(int ticksCount)
+ {
+ if (ticksCount > TickCounts[0]) return TickCounts[0];
+
+ for (int i = 0; i < TickCounts.Length; i++)
+ if (ticksCount > TickCounts[i])
+ return TickCounts[i];
+
+ return TickCounts.Last();
+ }
+
+ public int IncreaseTickCount(int ticksCount)
+ {
+ if (ticksCount >= TickCounts[0]) return TickCounts[0];
+
+ for (int i = TickCounts.Length - 1; i >= 0; i--)
+ if (ticksCount < TickCounts[i])
+ return TickCounts[i];
+
+ return TickCounts.Last();
+ }
+
+ protected abstract int GetSpecificValue(T start, T dt);
+ protected abstract T GetStart(T start, int value, int step);
+ protected abstract bool IsMinDate(T dt);
+ protected abstract T AddStep(T dt, int step);
+
+ public ITicksInfo GetTicks(Range range, int ticksCount)
+ {
+ T start = range.Min;
+ T end = range.Max;
+ DifferenceIn diff = Difference;
+ start = RoundDown(start, end);
+ end = RoundUp(start, end);
+
+ RoundingInfo bounds = RoundingHelper.CreateRoundedRange(
+ GetSpecificValue(start, start),
+ GetSpecificValue(start, end));
+
+ int delta = (int)(bounds.Max - bounds.Min);
+ if (delta == 0)
+ return new TicksInfo { Ticks = new T[] { start } };
+
+ int step = delta / ticksCount;
+
+ if (step == 0) step = 1;
+
+ T tick = GetStart(start, (int)bounds.Min, step);
+ bool isMinDateTime = IsMinDate(tick) && step != 1;
+ if (isMinDateTime)
+ step--;
+
+ List ticks = new List();
+ T finishTick = AddStep(range.Max, step);
+ while (Continue(tick, finishTick))
+ {
+ ticks.Add(tick);
+ tick = AddStep(tick, step);
+ if (isMinDateTime)
+ {
+ isMinDateTime = false;
+ step++;
+ }
+ }
+
+ ticks = Trim(ticks, range);
+
+ TicksInfo res = new TicksInfo { Ticks = ticks.ToArray(), Info = diff };
+ return res;
+ }
+
+ protected abstract bool Continue(T current, T end);
+
+ protected abstract T RoundUp(T start, T end);
+
+ protected abstract T RoundDown(T start, T end);
+
+ protected abstract List Trim(List ticks, Range range);
+
+ public ITicksProvider MinorProvider
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public ITicksProvider MajorProvider
+ {
+ get { throw new NotSupportedException(); }
+ }
+ }
+
+ internal abstract class DatePeriodTicksProvider : TimePeriodTicksProvider
+ {
+ protected sealed override bool Continue(DateTime current, DateTime end)
+ {
+ return current < end;
+ }
+
+ protected sealed override List Trim(List ticks, Range range)
+ {
+ int startIndex = 0;
+ for (int i = 0; i < ticks.Count - 1; i++)
+ {
+ if (ticks[i] <= range.Min && range.Min <= ticks[i + 1])
+ {
+ startIndex = i;
+ break;
+ }
+ }
+
+ int endIndex = ticks.Count - 1;
+ for (int i = ticks.Count - 1; i >= 1; i--)
+ {
+ if (ticks[i] >= range.Max && range.Max > ticks[i - 1])
+ {
+ endIndex = i;
+ break;
+ }
+ }
+
+ List res = new List(endIndex - startIndex + 1);
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ res.Add(ticks[i]);
+ }
+
+ return res;
+ }
+
+ protected sealed override DateTime RoundUp(DateTime start, DateTime end)
+ {
+ bool isPositive = (end - start).Ticks > 0;
+ return isPositive ? SafelyRoundUp(end) : RoundDown(end, Difference);
+ }
+
+ private DateTime SafelyRoundUp(DateTime dt)
+ {
+ if (AddStep(dt, 1) == DateTime.MaxValue)
+ return DateTime.MaxValue;
+
+ return RoundUp(dt, Difference);
+ }
+
+ protected sealed override DateTime RoundDown(DateTime start, DateTime end)
+ {
+ bool isPositive = (end - start).Ticks > 0;
+ return isPositive ? RoundDown(start, Difference) : SafelyRoundUp(start);
+ }
+
+ protected sealed override DateTime RoundDown(DateTime time, DifferenceIn diff)
+ {
+ DateTime res = time;
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ res = new DateTime(time.Year, 1, 1);
+ break;
+ case DifferenceIn.Month:
+ res = new DateTime(time.Year, time.Month, 1);
+ break;
+ case DifferenceIn.Day:
+ res = time.Date;
+ break;
+ case DifferenceIn.Hour:
+ res = time.Date.AddHours(time.Hour);
+ break;
+ case DifferenceIn.Minute:
+ res = time.Date.AddHours(time.Hour).AddMinutes(time.Minute);
+ break;
+ case DifferenceIn.Second:
+ res = time.Date.AddHours(time.Hour).AddMinutes(time.Minute).AddSeconds(time.Second);
+ break;
+ case DifferenceIn.Millisecond:
+ res = time.Date.AddHours(time.Hour).AddMinutes(time.Minute).AddSeconds(time.Second).AddMilliseconds(time.Millisecond);
+ break;
+ default:
+ break;
+ }
+
+ DebugVerify.Is(res <= time);
+
+ return res;
+ }
+
+ protected override DateTime RoundUp(DateTime dateTime, DifferenceIn diff)
+ {
+ DateTime res = RoundDown(dateTime, diff);
+
+ switch (diff)
+ {
+ case DifferenceIn.Year:
+ res = res.AddYears(1);
+ break;
+ case DifferenceIn.Month:
+ res = res.AddMonths(1);
+ break;
+ case DifferenceIn.Day:
+ res = res.AddDays(1);
+ break;
+ case DifferenceIn.Hour:
+ res = res.AddHours(1);
+ break;
+ case DifferenceIn.Minute:
+ res = res.AddMinutes(1);
+ break;
+ case DifferenceIn.Second:
+ res = res.AddSeconds(1);
+ break;
+ case DifferenceIn.Millisecond:
+ res = res.AddMilliseconds(1);
+ break;
+ default:
+ break;
+ }
+
+ return res;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DateTime/VerticalDateTimeAxis.cs b/Backup/Charts/Axes/DateTime/VerticalDateTimeAxis.cs
new file mode 100644
index 0000000..ded1af8
--- /dev/null
+++ b/Backup/Charts/Axes/DateTime/VerticalDateTimeAxis.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.ViewportRestrictions;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public class VerticalDateTimeAxis : DateTimeAxis
+ {
+ public VerticalDateTimeAxis()
+ {
+ Placement = AxisPlacement.Left;
+ Restriction = new DateTimeVerticalAxisRestriction();
+ }
+
+ protected override void ValidatePlacement(AxisPlacement newPlacement)
+ {
+ if (newPlacement == AxisPlacement.Bottom || newPlacement == AxisPlacement.Top)
+ throw new ArgumentException(Strings.Exceptions.VerticalAxisCannotBeHorizontal);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/DefaultAxisConversions.cs b/Backup/Charts/Axes/DefaultAxisConversions.cs
new file mode 100644
index 0000000..d2ea6c1
--- /dev/null
+++ b/Backup/Charts/Axes/DefaultAxisConversions.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Contains default axis value conversions.
+ ///
+ public static class DefaultAxisConversions
+ {
+ #region double
+
+ private static readonly Func doubleToDouble = d => d;
+ public static Func DoubleToDouble
+ {
+ get { return DefaultAxisConversions.doubleToDouble; }
+ }
+
+ private static readonly Func doubleFromDouble = d => d;
+ public static Func DoubleFromDouble
+ {
+ get { return DefaultAxisConversions.doubleFromDouble; }
+ }
+
+ #endregion
+
+ #region DateTime
+
+ private static readonly long minDateTimeTicks = DateTime.MinValue.Ticks;
+ private static readonly long maxDateTimeTicks = DateTime.MaxValue.Ticks;
+ private static readonly Func dateTimeFromDouble = d =>
+ {
+ long ticks = (long)(d * 10000000000L);
+
+ // todo should we throw an exception if number of ticks is too big or small?
+ if (ticks < minDateTimeTicks)
+ ticks = minDateTimeTicks;
+ else if (ticks > maxDateTimeTicks)
+ ticks = maxDateTimeTicks;
+
+ return new DateTime(ticks);
+ };
+ public static Func DateTimeFromDouble
+ {
+ get { return dateTimeFromDouble; }
+ }
+
+ private static readonly Func dateTimeToDouble = dt => dt.Ticks / 10000000000.0;
+ public static Func DateTimeToDouble
+ {
+ get { return DefaultAxisConversions.dateTimeToDouble; }
+ }
+
+ #endregion
+
+ #region TimeSpan
+
+ private static readonly long minTimeSpanTicks = TimeSpan.MinValue.Ticks;
+ private static readonly long maxTimeSpanTicks = TimeSpan.MaxValue.Ticks;
+
+ private static readonly Func timeSpanFromDouble = d =>
+ {
+ long ticks = (long)(d * 10000000000L);
+
+ // todo should we throw an exception if number of ticks is too big or small?
+ if (ticks < minTimeSpanTicks)
+ ticks = minTimeSpanTicks;
+ else if (ticks > maxTimeSpanTicks)
+ ticks = maxTimeSpanTicks;
+
+ return new TimeSpan(ticks);
+ };
+
+ public static Func TimeSpanFromDouble
+ {
+ get { return DefaultAxisConversions.timeSpanFromDouble; }
+ }
+
+ private static readonly Func timeSpanToDouble = timeSpan =>
+ {
+ return timeSpan.Ticks / 10000000000.0;
+ };
+
+ public static Func TimeSpanToDouble
+ {
+ get { return DefaultAxisConversions.timeSpanToDouble; }
+ }
+
+ #endregion
+
+ #region integer
+
+ private readonly static Func intFromDouble = d => (int)d;
+ public static Func IntFromDouble
+ {
+ get { return DefaultAxisConversions.intFromDouble; }
+ }
+
+ private readonly static Func intToDouble = i => (double)i;
+ public static Func IntToDouble
+ {
+ get { return DefaultAxisConversions.intToDouble; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Backup/Charts/Axes/DefaultTicksProvider.cs b/Backup/Charts/Axes/DefaultTicksProvider.cs
new file mode 100644
index 0000000..9955c97
--- /dev/null
+++ b/Backup/Charts/Axes/DefaultTicksProvider.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ internal static class DefaultTicksProvider
+ {
+ internal static readonly int DefaultTicksCount = 10;
+
+ internal static ITicksInfo GetTicks(this ITicksProvider provider, Range range)
+ {
+ return provider.GetTicks(range, DefaultTicksCount);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/GeneralAxis.cs b/Backup/Charts/Axes/GeneralAxis.cs
new file mode 100644
index 0000000..09261dd
--- /dev/null
+++ b/Backup/Charts/Axes/GeneralAxis.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.ComponentModel;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ ///
+ /// Represents a base class for all DynamicDataDisplay's axes.
+ /// Has several axis-specific and all WPF-specific properties.
+ ///
+ public abstract class GeneralAxis : ContentControl, IPlotterElement
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected GeneralAxis() { }
+
+ #region Placement property
+
+ private AxisPlacement placement = AxisPlacement.Bottom;
+ ///
+ /// Gets or sets the placement of axis - place in ChartPlotter where it should be placed.
+ ///
+ /// The placement.
+ public AxisPlacement Placement
+ {
+ get { return placement; }
+ set
+ {
+ if (placement != value)
+ {
+ ValidatePlacement(value);
+ AxisPlacement oldPlacement = placement;
+ placement = value;
+ OnPlacementChanged(oldPlacement, placement);
+ }
+ }
+ }
+
+ protected virtual void OnPlacementChanged(AxisPlacement oldPlacement, AxisPlacement newPlacement) { }
+
+ protected Panel GetPanelByPlacement(AxisPlacement placement)
+ {
+ Panel panel = null;
+ switch (placement)
+ {
+ case AxisPlacement.Left:
+ panel = ParentPlotter.LeftPanel;
+ break;
+ case AxisPlacement.Right:
+ panel = ParentPlotter.RightPanel;
+ break;
+ case AxisPlacement.Top:
+ panel = ParentPlotter.TopPanel;
+ break;
+ case AxisPlacement.Bottom:
+ panel = ParentPlotter.BottomPanel;
+ break;
+ default:
+ break;
+ }
+ return panel;
+ }
+
+ ///
+ /// Validates the placement - e.g., vertical axis should not be placed from top or bottom, etc.
+ /// If proposed placement is wrong, throws an ArgumentException.
+ ///
+ /// The new placement.
+ protected virtual void ValidatePlacement(AxisPlacement newPlacement) { }
+
+ #endregion
+
+ protected void RaiseTicksChanged()
+ {
+ TicksChanged.Raise(this);
+ }
+
+ public abstract void ForceUpdate();
+
+ ///
+ /// Occurs when ticks changes.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler TicksChanged;
+
+ ///
+ /// Gets the screen coordinates of axis ticks.
+ ///
+ /// The screen ticks.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract double[] ScreenTicks { get; }
+
+ ///
+ /// Gets the screen coordinates of minor ticks.
+ ///
+ /// The minor screen ticks.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract MinorTickInfo[] MinorScreenTicks { get; }
+
+ #region IPlotterElement Members
+
+ private Plotter2D plotter;
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Plotter2D ParentPlotter
+ {
+ get { return plotter; }
+ }
+
+ void IPlotterElement.OnPlotterAttached(Plotter plotter)
+ {
+ this.plotter = (Plotter2D)plotter;
+ OnPlotterAttached(this.plotter);
+ }
+
+ protected virtual void OnPlotterAttached(Plotter2D plotter) { }
+
+ void IPlotterElement.OnPlotterDetaching(Plotter plotter)
+ {
+ OnPlotterDetaching(this.plotter);
+ this.plotter = null;
+ }
+
+ protected virtual void OnPlotterDetaching(Plotter2D plotter) { }
+
+ public Plotter2D Plotter
+ {
+ get { return plotter; }
+ }
+
+ Plotter IPlotterElement.Plotter
+ {
+ get { return plotter; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Backup/Charts/Axes/GenericLabelProvider.cs b/Backup/Charts/Axes/GenericLabelProvider.cs
new file mode 100644
index 0000000..31ae882
--- /dev/null
+++ b/Backup/Charts/Axes/GenericLabelProvider.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ ///
+ /// Represents default implementation of label provider for specified type.
+ ///
+ /// Axis values type.
+ public class GenericLabelProvider : LabelProviderBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GenericLabelProvider() { }
+
+ #region ILabelProvider Members
+
+ ///
+ /// Creates the labels by given ticks info.
+ ///
+ /// The ticks info.
+ ///
+ /// Array of s, which are axis labels for specified axis ticks.
+ ///
+ public override UIElement[] CreateLabels(ITicksInfo ticksInfo)
+ {
+ var ticks = ticksInfo.Ticks;
+ var info = ticksInfo.Info;
+
+ LabelTickInfo tickInfo = new LabelTickInfo();
+ UIElement[] res = new UIElement[ticks.Length];
+ for (int i = 0; i < res.Length; i++)
+ {
+ tickInfo.Tick = ticks[i];
+ tickInfo.Info = info;
+
+ string text = GetString(tickInfo);
+
+ res[i] = new TextBlock
+ {
+ Text = text,
+ ToolTip = ticks[i].ToString()
+ };
+ }
+ return res;
+ }
+
+ #endregion
+ }
+}
diff --git a/Backup/Charts/Axes/GenericLocational/GenericLocationalLabelProvider.cs b/Backup/Charts/Axes/GenericLocational/GenericLocationalLabelProvider.cs
new file mode 100644
index 0000000..7ecc0e6
--- /dev/null
+++ b/Backup/Charts/Axes/GenericLocational/GenericLocationalLabelProvider.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.GenericLocational
+{
+ public class GenericLocationalLabelProvider : LabelProviderBase
+ {
+ private readonly IList collection;
+ private readonly Func displayMemberMapping;
+
+ public GenericLocationalLabelProvider(IList collection, Func displayMemberMapping)
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+ if (displayMemberMapping == null)
+ throw new ArgumentNullException("displayMemberMapping");
+
+ this.collection = collection;
+ this.displayMemberMapping = displayMemberMapping;
+ }
+
+ int startIndex;
+ public override UIElement[] CreateLabels(ITicksInfo ticksInfo)
+ {
+ var ticks = ticksInfo.Ticks;
+
+ if (ticks.Length == 0)
+ return EmptyLabelsArray;
+
+ startIndex = (int)ticksInfo.Info;
+
+ UIElement[] result = new UIElement[ticks.Length];
+
+ LabelTickInfo labelInfo = new LabelTickInfo { Info = ticksInfo.Info };
+
+ for (int i = 0; i < result.Length; i++)
+ {
+ var tick = ticks[i];
+ labelInfo.Tick = tick;
+ labelInfo.Index = i;
+
+ string labelText = GetString(labelInfo);
+
+ TextBlock label = new TextBlock { Text = labelText };
+
+ ApplyCustomView(labelInfo, label);
+
+ result[i] = label;
+ }
+
+ return result;
+ }
+
+ protected override string GetStringCore(LabelTickInfo tickInfo)
+ {
+ return displayMemberMapping(collection[tickInfo.Index + startIndex]);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/GenericLocational/GenericLocationalTicksProvider.cs b/Backup/Charts/Axes/GenericLocational/GenericLocationalTicksProvider.cs
new file mode 100644
index 0000000..ed02ab1
--- /dev/null
+++ b/Backup/Charts/Axes/GenericLocational/GenericLocationalTicksProvider.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Common.DataSearch;
+using System.Windows;
+using System.Collections;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.GenericLocational
+{
+ public class GenericLocationalTicksProvider : ITicksProvider where TAxis : IComparable
+ {
+ private IList collection;
+ public IList Collection
+ {
+ get { return collection; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ Changed.Raise(this);
+ collection = value;
+ }
+ }
+
+ private Func axisMapping;
+ public Func AxisMapping
+ {
+ get { return axisMapping; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ Changed.Raise(this);
+ axisMapping = value;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GenericLocationalTicksProvider() { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The collection of axis ticks and labels.
+ public GenericLocationalTicksProvider(IList collection)
+ {
+ Collection = collection;
+ }
+
+ public GenericLocationalTicksProvider(IList collection, Func coordinateMapping)
+ {
+ Collection = collection;
+ AxisMapping = coordinateMapping;
+ }
+
+ #region ITicksProvider Members
+
+ SearchResult1d minResult = SearchResult1d.Empty;
+ SearchResult1d maxResult = SearchResult1d.Empty;
+ GenericSearcher1d searcher;
+ ///
+ /// Generates ticks for given range and preferred ticks count.
+ ///
+ /// The range.
+ /// The ticks count.
+ ///
+ public ITicksInfo GetTicks(Range range, int ticksCount)
+ {
+ EnsureSearcher();
+
+ //minResult = searcher.SearchBetween(range.Min, minResult);
+ //maxResult = searcher.SearchBetween(range.Max, maxResult);
+
+ minResult = searcher.SearchFirstLess(range.Min);
+ maxResult = searcher.SearchGreater(range.Max);
+
+ if (!(minResult.IsEmpty && maxResult.IsEmpty))
+ {
+ int startIndex = !minResult.IsEmpty ? minResult.Index : 0;
+ int endIndex = !maxResult.IsEmpty ? maxResult.Index : collection.Count - 1;
+
+ int count = endIndex - startIndex + 1;
+
+ TAxis[] ticks = new TAxis[count];
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ ticks[i - startIndex] = axisMapping(collection[i]);
+ }
+
+ TicksInfo result = new TicksInfo
+ {
+ Info = startIndex,
+ TickSizes = ArrayExtensions.CreateArray(count, 1.0),
+ Ticks = ticks
+ };
+
+ return result;
+ }
+ else
+ {
+ return TicksInfo.Empty;
+ }
+ }
+
+ private void EnsureSearcher()
+ {
+ if (searcher == null)
+ {
+ if (collection == null || axisMapping == null)
+ throw new InvalidOperationException(Strings.Exceptions.GenericLocationalProviderInvalidState);
+
+ searcher = new GenericSearcher1d(collection, axisMapping);
+ }
+ }
+
+ public int DecreaseTickCount(int ticksCount)
+ {
+ return collection.Count;
+ }
+
+ public int IncreaseTickCount(int ticksCount)
+ {
+ return collection.Count;
+ }
+
+ public ITicksProvider MinorProvider
+ {
+ get { return null; }
+ }
+
+ public ITicksProvider MajorProvider
+ {
+ get { return null; }
+ }
+
+ public event EventHandler Changed;
+
+ #endregion
+ }
+}
diff --git a/Backup/Charts/Axes/ITicksProvider.cs b/Backup/Charts/Axes/ITicksProvider.cs
new file mode 100644
index 0000000..1f9efca
--- /dev/null
+++ b/Backup/Charts/Axes/ITicksProvider.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Contains information about one minor tick - its value (relative size) and its tick.
+ ///
+ ///
+ [DebuggerDisplay("{Value} @ {Tick}")]
+ public struct MinorTickInfo
+ {
+ internal MinorTickInfo(double value, T tick)
+ {
+ this.value = value;
+ this.tick = tick;
+ }
+
+ private readonly double value;
+ private readonly T tick;
+
+ public double Value { get { return value; } }
+ public T Tick { get { return tick; } }
+
+ public override string ToString()
+ {
+ return String.Format("{0} @ {1}", value, tick);
+ }
+ }
+
+ ///
+ /// Contains data for all generated ticks.
+ /// Used by TicksLabelProvider.
+ ///
+ /// Type of axis tick.
+ public interface ITicksInfo
+ {
+ ///
+ /// Gets the array of axis ticks.
+ ///
+ /// The ticks.
+ T[] Ticks { get; }
+ ///
+ /// Gets the tick sizes.
+ ///
+ /// The tick sizes.
+ double[] TickSizes { get; }
+ ///
+ /// Gets the additional information, added to ticks info and specifying range's features.
+ ///
+ /// The info.
+ object Info { get; }
+ }
+
+ internal class TicksInfo : ITicksInfo
+ {
+ private T[] ticks = { };
+ ///
+ /// Gets the array of axis ticks.
+ ///
+ /// The ticks.
+ public T[] Ticks
+ {
+ get { return ticks; }
+ internal set { ticks = value; }
+ }
+
+ private double[] tickSizes = { };
+ ///
+ /// Gets the tick sizes.
+ ///
+ /// The tick sizes.
+ public double[] TickSizes
+ {
+ get
+ {
+ if (tickSizes.Length != ticks.Length)
+ tickSizes = ArrayExtensions.CreateArray(ticks.Length, 1.0);
+
+ return tickSizes;
+ }
+ internal set { tickSizes = value; }
+ }
+
+ private object info = null;
+ ///
+ /// Gets the additional information, added to ticks info and specifying range's features.
+ ///
+ /// The info.
+ public object Info
+ {
+ get { return info; }
+ internal set { info = value; }
+ }
+
+ private static readonly TicksInfo empty = new TicksInfo { info = null, ticks = new T[0], tickSizes = new double[0] };
+ internal static TicksInfo Empty
+ {
+ get { return empty; }
+ }
+ }
+
+ ///
+ /// Base interface for ticks generator.
+ ///
+ ///
+ public interface ITicksProvider
+ {
+ ///
+ /// Generates ticks for given range and preferred ticks count.
+ ///
+ /// The range.
+ /// The ticks count.
+ ///
+ ITicksInfo GetTicks(Range range, int ticksCount);
+ ///
+ /// Decreases the tick count.
+ /// Returned value should be later passed as ticksCount parameter to GetTicks method.
+ ///
+ /// The ticks count.
+ /// Decreased ticks count.
+ int DecreaseTickCount(int ticksCount);
+ ///
+ /// Increases the tick count.
+ /// Returned value should be later passed as ticksCount parameter to GetTicks method.
+ ///
+ /// The ticks count.
+ /// Increased ticks count.
+ int IncreaseTickCount(int ticksCount);
+
+ ///
+ /// Gets the minor ticks provider, used to generate ticks between each two adjacent ticks.
+ ///
+ /// The minor provider. If there is no minor provider available, returns null.
+ ITicksProvider MinorProvider { get; }
+ ///
+ /// Gets the major provider, used to generate major ticks - for example, years for common ticks as months.
+ ///
+ /// The major provider. If there is no major provider available, returns null.
+ ITicksProvider MajorProvider { get; }
+
+ ///
+ /// Occurs when properties of ticks provider changeds.
+ /// Notifies axis to rebuild its view.
+ ///
+ event EventHandler Changed;
+ }
+}
diff --git a/Backup/Charts/Axes/ITypedAxis.cs b/Backup/Charts/Axes/ITypedAxis.cs
new file mode 100644
index 0000000..68a69f8
--- /dev/null
+++ b/Backup/Charts/Axes/ITypedAxis.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ ///
+ /// Describes axis as having ticks type.
+ /// Provides access to some typed properties.
+ ///
+ /// Axis tick's type.
+ public interface ITypedAxis
+ {
+ ///
+ /// Gets the ticks provider.
+ ///
+ /// The ticks provider.
+ ITicksProvider TicksProvider { get; }
+ ///
+ /// Gets the label provider.
+ ///
+ /// The label provider.
+ LabelProviderBase LabelProvider { get; }
+
+ ///
+ /// Gets or sets the convertion of tick from double.
+ /// Should not be null.
+ ///
+ /// The convert from double.
+ Func ConvertFromDouble { get; set; }
+ ///
+ /// Gets or sets the convertion of tick to double.
+ /// Should not be null.
+ ///
+ /// The convert to double.
+ Func ConvertToDouble { get; set; }
+ }
+}
diff --git a/Backup/Charts/Axes/IValueConversion.cs b/Backup/Charts/Axes/IValueConversion.cs
new file mode 100644
index 0000000..0e41d67
--- /dev/null
+++ b/Backup/Charts/Axes/IValueConversion.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts
+{
+ public interface IValueConversion
+ {
+ Func ConvertToDouble { get; set; }
+ Func ConvertFromDouble { get; set; }
+ }
+}
diff --git a/Backup/Charts/Axes/Integer/CollectionLabelProvider.cs b/Backup/Charts/Axes/Integer/CollectionLabelProvider.cs
new file mode 100644
index 0000000..37e536c
--- /dev/null
+++ b/Backup/Charts/Axes/Integer/CollectionLabelProvider.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ public class CollectionLabelProvider : LabelProviderBase
+ {
+ private IList collection;
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
+ public IList Collection
+ {
+ get { return collection; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (collection != value)
+ {
+ DetachCollection();
+
+ collection = value;
+
+ AttachCollection();
+
+ RaiseChanged();
+ }
+ }
+ }
+
+ #region Collection changed
+
+ private void AttachCollection()
+ {
+ INotifyCollectionChanged observableCollection = collection as INotifyCollectionChanged;
+ if (observableCollection != null)
+ {
+ observableCollection.CollectionChanged += OnCollectionChanged;
+ }
+ }
+
+ private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ RaiseChanged();
+ }
+
+ private void DetachCollection()
+ {
+ INotifyCollectionChanged observableCollection = collection as INotifyCollectionChanged;
+ if (observableCollection != null)
+ {
+ observableCollection.CollectionChanged -= OnCollectionChanged;
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Initializes a new instance of the class with empty labels collection.
+ ///
+ public CollectionLabelProvider() { }
+
+ public CollectionLabelProvider(IList collection)
+ : this()
+ {
+ Collection = collection;
+ }
+
+ public override UIElement[] CreateLabels(ITicksInfo ticksInfo)
+ {
+ var ticks = ticksInfo.Ticks;
+
+ UIElement[] res = new UIElement[ticks.Length];
+
+ var tickInfo = new LabelTickInfo { Info = ticksInfo.Info };
+
+ for (int i = 0; i < res.Length; i++)
+ {
+ int tick = ticks[i];
+ tickInfo.Tick = tick;
+
+ if (0 <= tick && tick < collection.Count)
+ {
+ string text = collection[tick].ToString();
+ res[i] = new TextBlock
+ {
+ Text = text,
+ ToolTip = text
+ };
+ }
+ else
+ {
+ res[i] = null;
+ }
+ }
+ return res;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/Integer/HorizontalIntegerAxis.cs b/Backup/Charts/Axes/Integer/HorizontalIntegerAxis.cs
new file mode 100644
index 0000000..b0f31f5
--- /dev/null
+++ b/Backup/Charts/Axes/Integer/HorizontalIntegerAxis.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ public class HorizontalIntegerAxis : IntegerAxis
+ {
+ public HorizontalIntegerAxis()
+ {
+ Placement = AxisPlacement.Bottom;
+ }
+
+ protected override void ValidatePlacement(AxisPlacement newPlacement)
+ {
+ if (newPlacement == AxisPlacement.Left || newPlacement == AxisPlacement.Right)
+ throw new ArgumentException(Strings.Exceptions.HorizontalAxisCannotBeVertical);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/Integer/IntegerAxis.cs b/Backup/Charts/Axes/Integer/IntegerAxis.cs
new file mode 100644
index 0000000..1484ce0
--- /dev/null
+++ b/Backup/Charts/Axes/Integer/IntegerAxis.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ public class IntegerAxis : AxisBase
+ {
+ public IntegerAxis()
+ : base(new IntegerAxisControl(),
+ d => (int)d,
+ i => (double)i)
+ {
+
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/Integer/IntegerAxisControl.cs b/Backup/Charts/Axes/Integer/IntegerAxisControl.cs
new file mode 100644
index 0000000..29e3817
--- /dev/null
+++ b/Backup/Charts/Axes/Integer/IntegerAxisControl.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ public class IntegerAxisControl : AxisControl
+ {
+ public IntegerAxisControl()
+ {
+ LabelProvider = new GenericLabelProvider();
+ TicksProvider = new IntegerTicksProvider();
+ ConvertToDouble = i => (double)i;
+ Range = new Range(0, 1);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/Integer/IntegerTicksProvider.cs b/Backup/Charts/Axes/Integer/IntegerTicksProvider.cs
new file mode 100644
index 0000000..a5f586e
--- /dev/null
+++ b/Backup/Charts/Axes/Integer/IntegerTicksProvider.cs
@@ -0,0 +1,189 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ ///
+ /// Represents a ticks provider for intefer values.
+ ///
+ public class IntegerTicksProvider : ITicksProvider
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public IntegerTicksProvider() { }
+
+ private int minStep = 0;
+ ///
+ /// Gets or sets the minimal step between ticks.
+ ///
+ /// The min step.
+ public int MinStep
+ {
+ get { return minStep; }
+ set
+ {
+ Verify.IsTrue(value >= 0, "value");
+ if (minStep != value)
+ {
+ minStep = value;
+ RaiseChangedEvent();
+ }
+ }
+ }
+
+ private int maxStep = Int32.MaxValue;
+ ///
+ /// Gets or sets the maximal step between ticks.
+ ///
+ /// The max step.
+ public int MaxStep
+ {
+ get { return maxStep; }
+ set
+ {
+ if (maxStep != value)
+ {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException("value", Strings.Exceptions.ParameterShouldBePositive);
+
+ maxStep = value;
+ RaiseChangedEvent();
+ }
+ }
+ }
+
+ #region ITicksProvider Members
+
+ ///
+ /// Generates ticks for given range and preferred ticks count.
+ ///
+ /// The range.
+ /// The ticks count.
+ ///
+ public ITicksInfo GetTicks(Range range, int ticksCount)
+ {
+ double start = range.Min;
+ double finish = range.Max;
+
+ double delta = finish - start;
+
+ int log = (int)Math.Round(Math.Log10(delta));
+
+ double newStart = RoundingHelper.Round(start, log);
+ double newFinish = RoundingHelper.Round(finish, log);
+ if (newStart == newFinish)
+ {
+ log--;
+ newStart = RoundingHelper.Round(start, log);
+ newFinish = RoundingHelper.Round(finish, log);
+ }
+
+ // calculating step between ticks
+ double unroundedStep = (newFinish - newStart) / ticksCount;
+ int stepLog = log;
+ // trying to round step
+ int step = (int)RoundingHelper.Round(unroundedStep, stepLog);
+ if (step == 0)
+ {
+ stepLog--;
+ step = (int)RoundingHelper.Round(unroundedStep, stepLog);
+ if (step == 0)
+ {
+ // step will not be rounded if attempts to be rounded to zero.
+ step = (int)unroundedStep;
+ }
+ }
+
+ if (step < minStep)
+ step = minStep;
+ if (step > maxStep)
+ step = maxStep;
+
+ if (step <= 0)
+ step = 1;
+
+ int[] ticks = CreateTicks(start, finish, step);
+
+ TicksInfo res = new TicksInfo { Info = log, Ticks = ticks };
+
+ return res;
+ }
+
+ private static int[] CreateTicks(double start, double finish, int step)
+ {
+ DebugVerify.Is(step != 0);
+
+ int x = (int)(step * Math.Floor(start / (double)step));
+ List res = new List();
+
+ checked
+ {
+ double increasedFinish = finish + step * 1.05;
+ while (x <= increasedFinish)
+ {
+ res.Add(x);
+ x += step;
+ }
+ }
+ return res.ToArray();
+ }
+
+ private static int[] tickCounts = new int[] { 20, 10, 5, 4, 2, 1 };
+
+ ///
+ /// Decreases the tick count.
+ /// Returned value should be later passed as ticksCount parameter to GetTicks method.
+ ///
+ /// The ticks count.
+ /// Decreased ticks count.
+ public int DecreaseTickCount(int ticksCount)
+ {
+ return tickCounts.FirstOrDefault(tick => tick < ticksCount);
+ }
+
+ ///
+ /// Increases the tick count.
+ /// Returned value should be later passed as ticksCount parameter to GetTicks method.
+ ///
+ /// The ticks count.
+ /// Increased ticks count.
+ public int IncreaseTickCount(int ticksCount)
+ {
+ int newTickCount = tickCounts.Reverse().FirstOrDefault(tick => tick > ticksCount);
+ if (newTickCount == 0)
+ newTickCount = tickCounts[0];
+
+ return newTickCount;
+ }
+
+ ///
+ /// Gets the minor ticks provider, used to generate ticks between each two adjacent ticks.
+ ///
+ /// The minor provider.
+ public ITicksProvider MinorProvider
+ {
+ get { return null; }
+ }
+
+ ///
+ /// Gets the major provider, used to generate major ticks - for example, years for common ticks as months.
+ ///
+ /// The major provider.
+ public ITicksProvider MajorProvider
+ {
+ get { return null; }
+ }
+
+ protected void RaiseChangedEvent()
+ {
+ Changed.Raise(this);
+ }
+ public event EventHandler Changed;
+
+ #endregion
+ }
+}
diff --git a/Backup/Charts/Axes/Integer/VerticalIntegerAxis.cs b/Backup/Charts/Axes/Integer/VerticalIntegerAxis.cs
new file mode 100644
index 0000000..9b1c95a
--- /dev/null
+++ b/Backup/Charts/Axes/Integer/VerticalIntegerAxis.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ public class VerticalIntegerAxis : IntegerAxis
+ {
+ public VerticalIntegerAxis()
+ {
+ Placement = AxisPlacement.Left;
+ }
+
+ protected override void ValidatePlacement(AxisPlacement newPlacement)
+ {
+ if (newPlacement == AxisPlacement.Bottom || newPlacement == AxisPlacement.Top)
+ throw new ArgumentException(Strings.Exceptions.VerticalAxisCannotBeHorizontal);
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/LabelProvider.cs b/Backup/Charts/Axes/LabelProvider.cs
new file mode 100644
index 0000000..10152d4
--- /dev/null
+++ b/Backup/Charts/Axes/LabelProvider.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ public abstract class LabelProvider : LabelProviderBase
+ {
+ public override UIElement[] CreateLabels(ITicksInfo ticksInfo)
+ {
+ var ticks = ticksInfo.Ticks;
+
+ UIElement[] res = new UIElement[ticks.Length];
+ LabelTickInfo labelInfo = new LabelTickInfo { Info = ticksInfo.Info };
+
+ for (int i = 0; i < res.Length; i++)
+ {
+ labelInfo.Tick = ticks[i];
+ labelInfo.Index = i;
+
+ string labelText = GetString(labelInfo);
+
+ TextBlock label = (TextBlock)GetResourceFromPool();
+ if (label == null)
+ {
+ label = new TextBlock();
+ }
+
+ label.Text = labelText;
+ label.ToolTip = ticks[i].ToString();
+
+ res[i] = label;
+
+ ApplyCustomView(labelInfo, label);
+ }
+
+ return res;
+ }
+ }
+}
diff --git a/Backup/Charts/Axes/LabelProviderBase.cs b/Backup/Charts/Axes/LabelProviderBase.cs
new file mode 100644
index 0000000..8414b89
--- /dev/null
+++ b/Backup/Charts/Axes/LabelProviderBase.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
+using Microsoft.Research.DynamicDataDisplay.Common;
+using System.ComponentModel;
+
+namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
+{
+ ///
+ /// Contains data for custom generation of tick's label.
+ ///
+ /// Type of ticks
+ public sealed class LabelTickInfo
+ {
+ internal LabelTickInfo() { }
+
+ ///
+ /// Gets or sets the tick.
+ ///
+ /// The tick.
+ public T Tick { get; internal set; }
+ ///
+ /// Gets or sets additional info about ticks range.
+ ///
+ /// The info.
+ public object Info { get; internal set; }
+ ///
+ /// Gets or sets the index of tick in ticks array.
+ ///
+ /// The index.
+ public int Index { get; internal set; }
+ }
+
+ ///
+ /// Base class for all label providers.
+ /// Contains a number of properties that can be used to adjust generated labels.
+ ///
+ /// Type of ticks, which labels are generated for
+ ///
+ /// Order of apllication of custom label string properties:
+ /// If CustomFormatter is not null, it is called first.
+ /// Then, if it was null or if it returned null string,
+ /// virtual GetStringCore method is called. It can be overloaded in subclasses. GetStringCore should not return null.
+ /// Then if LabelStringFormat is not null, it is applied.
+ /// After label's UI was created, you can change it by setting CustomView delegate - it allows you to adjust
+ /// UI properties of label. Note: not all labelProviders takes CustomView into account.
+ ///
+ public abstract class LabelProviderBase
+ {
+
+ #region Private
+
+ private string labelStringFormat = null;
+ private Func, string> customFormatter = null;
+ private Action, UIElement> customView = null;
+
+ #endregion
+
+ private static readonly UIElement[] emptyLabelsArray = new UIElement[0];
+ protected static UIElement[] EmptyLabelsArray
+ {
+ get { return emptyLabelsArray; }
+ }
+
+ ///
+ /// Creates labels by given ticks info.
+ /// Is not intended to be called from your code.
+ ///
+ /// The ticks info.
+ /// Array of s, which are axis labels for specified axis ticks.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract UIElement[] CreateLabels(ITicksInfo ticksInfo);
+
+ ///
+ /// Gets or sets the label string format.
+ ///
+ /// The label string format.
+ public string LabelStringFormat
+ {
+ get { return labelStringFormat; }
+ set
+ {
+ if (labelStringFormat != value)
+ {
+ labelStringFormat = value;
+ RaiseChanged();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the custom formatter - delegate that can be called to create custom string representation of tick.
+ ///
+ /// The custom formatter.
+ public Func, string> CustomFormatter
+ {
+ get { return customFormatter; }
+ set
+ {
+ if (customFormatter != value)
+ {
+ customFormatter = value;
+ RaiseChanged();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the custom view - delegate that is used to create a custom, non-default look of axis label.
+ /// Can be used to adjust some UI properties of generated label.
+ ///
+ /// The custom view.
+ public Action, UIElement> CustomView
+ {
+ get { return customView; }
+ set
+ {
+ if (customView != value)
+ {
+ customView = value;
+ RaiseChanged();
+ }
+ }
+ }
+
+ ///
+ /// Sets the custom formatter.
+ /// This is alternative to CustomFormatter property setter, the only difference is that Visual Studio shows
+ /// more convenient tooltip for methods rather than for properties' setters.
+ ///
+ /// The formatter.
+ public void SetCustomFormatter(Func, string> formatter)
+ {
+ CustomFormatter = formatter;
+ }
+
+ ///
+ /// Sets the custom view.
+ /// This is alternative to CustomView property setter, the only difference is that Visual Studio shows
+ /// more convenient tooltip for methods rather than for properties' setters.
+ ///
+ /// The view.
+ public void SetCustomView(Action, UIElement> view)
+ {
+ CustomView = view;
+ }
+
+ protected virtual string GetString(LabelTickInfo tickInfo)
+ {
+ string text = null;
+ if (CustomFormatter != null)
+ {
+ text = CustomFormatter(tickInfo);
+ }
+ if (text == null)
+ {
+ text = GetStringCore(tickInfo);
+
+ if (text == null)
+ throw new ArgumentNullException(Strings.Exceptions.TextOfTickShouldNotBeNull);
+ }
+ if (LabelStringFormat != null)
+ {
+ text = String.Format(LabelStringFormat, text);
+ }
+
+ return text;
+ }
+
+ protected virtual string GetStringCore(LabelTickInfo tickInfo)
+ {
+ return tickInfo.Tick.ToString();
+ }
+
+ protected void ApplyCustomView(LabelTickInfo info, UIElement label)
+ {
+ if (CustomView != null)
+ {
+ CustomView(info, label);
+ }
+ }
+
+ ///