Files
DynamicDataDisplay/ChartPlotter.cs
2024-02-23 00:46:06 -05:00

503 lines
13 KiB
C#

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
{
/// <summary>Chart plotter is a plotter that renders axis and grid</summary>
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; }
}
/// <summary>
/// Initializes a new instance of the <see cref="ChartPlotter"/> class.
/// </summary>
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();
}
/// <summary>
/// Creates generic plotter from this ChartPlotter.
/// </summary>
/// <returns></returns>
public GenericChartPlotter<double, double> GetGenericPlotter()
{
return new GenericChartPlotter<double, double>(this);
}
/// <summary>
/// Creates generic plotter from this ChartPlotter.
/// Horizontal and Vertical types of GenericPlotter should correspond to ChartPlotter's actual main axes types.
/// </summary>
/// <typeparam name="THorizontal">The type of horizontal values.</typeparam>
/// <typeparam name="TVertical">The type of vertical values.</typeparam>
/// <returns>GenericChartPlotter, associated to this ChartPlotter.</returns>
public GenericChartPlotter<THorizontal, TVertical> GetGenericPlotter<THorizontal, TVertical>()
{
return new GenericChartPlotter<THorizontal, TVertical>(this);
}
/// <summary>
/// Creates generic plotter from this ChartPlotter.
/// </summary>
/// <typeparam name="THorizontal">The type of the horizontal axis.</typeparam>
/// <typeparam name="TVertical">The type of the vertical axis.</typeparam>
/// <param name="horizontalAxis">The horizontal axis to use as data conversion source.</param>
/// <param name="verticalAxis">The vertical axis to use as data conversion source.</param>
/// <returns>GenericChartPlotter, associated to this ChartPlotter</returns>
public GenericChartPlotter<THorizontal, TVertical> GetGenericPlotter<THorizontal, TVertical>(AxisBase<THorizontal> horizontalAxis, AxisBase<TVertical> verticalAxis)
{
return new GenericChartPlotter<THorizontal, TVertical>(this, horizontalAxis, verticalAxis);
}
protected ChartPlotter(PlotterLoadMode loadMode) : base(loadMode) { }
/// <summary>
/// Creates empty plotter without any axes, navigation, etc.
/// </summary>
/// <returns>Empty plotter without any axes, navigation, etc.</returns>
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;
/// <summary>
/// Gets the default mouse navigation of ChartPlotter.
/// </summary>
/// <value>The mouse navigation.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MouseNavigation MouseNavigation
{
get { return mouseNavigation; }
}
private KeyboardNavigation keyboardNavigation;
/// <summary>
/// Gets the default keyboard navigation of ChartPlotter.
/// </summary>
/// <value>The keyboard navigation.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public KeyboardNavigation KeyboardNavigation
{
get { return keyboardNavigation; }
}
private DefaultContextMenu defaultContextMenu;
/// <summary>
/// Gets the default context menu of ChartPlotter.
/// </summary>
/// <value>The default context menu.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public DefaultContextMenu DefaultContextMenu
{
get { return defaultContextMenu; }
}
private AxisNavigation horizontalAxisNavigation;
/// <summary>
/// Gets the default horizontal axis navigation of ChartPlotter.
/// </summary>
/// <value>The horizontal axis navigation.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public AxisNavigation HorizontalAxisNavigation
{
get { return horizontalAxisNavigation; }
}
private AxisNavigation verticalAxisNavigation;
/// <summary>
/// Gets the default vertical axis navigation of ChartPlotter.
/// </summary>
/// <value>The vertical axis navigation.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public AxisNavigation VerticalAxisNavigation
{
get { return verticalAxisNavigation; }
}
/// <summary>
/// Gets the default axis grid of ChartPlotter.
/// </summary>
/// <value>The axis grid.</value>
[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;
/// <summary>
/// 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.
/// </summary>
/// <value>The main vertical axis.</value>
[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) { }
/// <summary>
/// Gets or sets the main horizontal axis visibility.
/// </summary>
/// <value>The main horizontal axis visibility.</value>
public Visibility MainHorizontalAxisVisibility
{
get { return MainHorizontalAxis != null ? ((UIElement)MainHorizontalAxis).Visibility : Visibility.Hidden; }
set
{
if (MainHorizontalAxis != null)
{
((UIElement)MainHorizontalAxis).Visibility = value;
}
}
}
/// <summary>
/// Gets or sets the main vertical axis visibility.
/// </summary>
/// <value>The main vertical axis visibility.</value>
public Visibility MainVerticalAxisVisibility
{
get { return MainVerticalAxis != null ? ((UIElement)MainVerticalAxis).Visibility : Visibility.Hidden; }
set
{
if (MainVerticalAxis != null)
{
((UIElement)MainVerticalAxis).Visibility = value;
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <value>The main horizontal axis.</value>
[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;
}
}
/// <summary>
/// Gets the default legend of ChartPlotter.
/// </summary>
/// <value>The legend.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Legend Legend
{
get { return legend; }
}
/// <summary>
/// Gets or sets the visibility of legend.
/// </summary>
/// <value>The legend visibility.</value>
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
}
}
}