// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace System.Windows.Controls.DataVisualization.Charting
{
///
/// Represents a control that displays a data point.
///
/// Preview
[TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)]
[TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)]
[TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)]
[TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)]
[TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)]
[TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)]
public abstract partial class DataPoint : Control
{
#region CommonStates
///
/// Common state group.
///
internal const string GroupCommonStates = "CommonStates";
///
/// Normal state of the Common group.
///
internal const string StateCommonNormal = "Normal";
///
/// MouseOver state of the Common group.
///
internal const string StateCommonMouseOver = "MouseOver";
#endregion CommonStates
#region SelectionStates
///
/// Selection state group.
///
internal const string GroupSelectionStates = "SelectionStates";
///
/// Unselected state of the Selection group.
///
internal const string StateSelectionUnselected = "Unselected";
///
/// Selected state of the Selection group.
///
internal const string StateSelectionSelected = "Selected";
#endregion SelectionStates
#region GroupRevealStates
///
/// Reveal state group.
///
internal const string GroupRevealStates = "RevealStates";
///
/// Shown state of the Reveal group.
///
internal const string StateRevealShown = "Shown";
///
/// Hidden state of the Reveal group.
///
internal const string StateRevealHidden = "Hidden";
#endregion GroupRevealStates
#region public bool IsSelectionEnabled
///
/// Gets or sets a value indicating whether selection is enabled.
///
public bool IsSelectionEnabled
{
get { return (bool)GetValue(IsSelectionEnabledProperty); }
set { SetValue(IsSelectionEnabledProperty, value); }
}
///
/// Identifies the IsSelectionEnabled dependency property.
///
public static readonly DependencyProperty IsSelectionEnabledProperty =
DependencyProperty.Register(
"IsSelectionEnabled",
typeof(bool),
typeof(DataPoint),
new PropertyMetadata(false, OnIsSelectionEnabledPropertyChanged));
///
/// IsSelectionEnabledProperty property changed handler.
///
/// Control that changed its IsSelectionEnabled.
/// Event arguments.
private static void OnIsSelectionEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataPoint source = (DataPoint)d;
bool oldValue = (bool)e.OldValue;
bool newValue = (bool)e.NewValue;
source.OnIsSelectionEnabledPropertyChanged(oldValue, newValue);
}
///
/// IsSelectionEnabledProperty property changed handler.
///
/// Old value.
/// New value.
protected virtual void OnIsSelectionEnabledPropertyChanged(bool oldValue, bool newValue)
{
if (newValue == false)
{
IsSelected = false;
IsHovered = false;
}
}
#endregion public bool IsSelectionEnabled
///
/// Gets a value indicating whether the data point is active.
///
internal bool IsActive { get; private set; }
///
/// An event raised when the IsSelected property is changed.
///
internal event RoutedPropertyChangedEventHandler IsSelectedChanged;
///
/// A value indicating whether the mouse is hovering over the data
/// point.
///
private bool _isHovered;
///
/// Gets a value indicating whether the mouse is hovering over
/// the data point.
///
protected bool IsHovered
{
get { return _isHovered; }
private set
{
bool oldValue = _isHovered;
_isHovered = value;
if (oldValue != _isHovered)
{
OnIsHoveredPropertyChanged(oldValue, value);
}
}
}
///
/// IsHoveredProperty property changed handler.
///
/// Old value.
/// New value.
protected virtual void OnIsHoveredPropertyChanged(bool oldValue, bool newValue)
{
VisualStateManager.GoToState(this, (newValue == true) ? StateCommonMouseOver : StateCommonNormal, true);
}
#region internal bool IsSelected
///
/// Gets or sets a value indicating whether the data point is selected.
///
internal bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
///
/// Identifies the IsSelected dependency property.
///
internal static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected",
typeof(bool),
typeof(DataPoint),
new PropertyMetadata(false, OnIsSelectedPropertyChanged));
///
/// IsSelectedProperty property changed handler.
///
/// Control that changed its IsSelected.
/// Event arguments.
private static void OnIsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataPoint source = (DataPoint)d;
bool oldValue = (bool)e.OldValue;
bool newValue = (bool)e.NewValue;
source.OnIsSelectedPropertyChanged(oldValue, newValue);
}
///
/// IsSelectedProperty property changed handler.
///
/// The value to be replaced.
/// The new value.
protected virtual void OnIsSelectedPropertyChanged(bool oldValue, bool newValue)
{
VisualStateManager.GoToState(this, newValue ? StateSelectionSelected : StateSelectionUnselected, true);
RoutedPropertyChangedEventHandler handler = this.IsSelectedChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
}
#endregion internal bool IsSelected
///
/// Event raised when the actual dependent value of the data point is changed.
///
internal event RoutedPropertyChangedEventHandler ActualDependentValueChanged;
#region public IComparable ActualDependentValue
///
/// Gets or sets the actual dependent value displayed in the chart.
///
public IComparable ActualDependentValue
{
get { return (IComparable)GetValue(ActualDependentValueProperty); }
set { SetValue(ActualDependentValueProperty, value); }
}
///
/// Identifies the ActualDependentValue dependency property.
///
public static readonly System.Windows.DependencyProperty ActualDependentValueProperty =
System.Windows.DependencyProperty.Register(
"ActualDependentValue",
typeof(IComparable),
typeof(DataPoint),
new System.Windows.PropertyMetadata(0.0, OnActualDependentValuePropertyChanged));
///
/// Called when the value of the ActualDependentValue property changes.
///
/// Control that changed its ActualDependentValue.
/// Event arguments.
private static void OnActualDependentValuePropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
{
DataPoint source = (DataPoint)d;
IComparable oldValue = (IComparable)e.OldValue;
IComparable newValue = (IComparable)e.NewValue;
source.OnActualDependentValuePropertyChanged(oldValue, newValue);
}
///
/// A value indicating whether the actual independent value is being
/// coerced.
///
private bool _isCoercingActualDependentValue;
///
/// The preserved previous actual dependent value before coercion.
///
private IComparable _oldActualDependentValueBeforeCoercion;
///
/// Called when the value of the ActualDependentValue property changes.
///
/// The value to be replaced.
/// The new value.
protected virtual void OnActualDependentValuePropertyChanged(IComparable oldValue, IComparable newValue)
{
double coercedValue = 0.0;
if (!(newValue is double) && ValueHelper.TryConvert(newValue, out coercedValue))
{
_isCoercingActualDependentValue = true;
_oldActualDependentValueBeforeCoercion = oldValue;
}
if (!_isCoercingActualDependentValue)
{
if (_oldActualDependentValueBeforeCoercion != null)
{
oldValue = _oldActualDependentValueBeforeCoercion;
_oldActualDependentValueBeforeCoercion = null;
}
RoutedPropertyChangedEventHandler handler = this.ActualDependentValueChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
}
if (_isCoercingActualDependentValue)
{
_isCoercingActualDependentValue = false;
this.ActualDependentValue = coercedValue;
}
}
#endregion public IComparable ActualDependentValue
///
/// This event is raised when the dependent value of the data point is
/// changed.
///
internal event RoutedPropertyChangedEventHandler DependentValueChanged;
#region public IComparable DependentValue
///
/// Gets or sets the dependent value of the Control.
///
public IComparable DependentValue
{
get { return (IComparable) GetValue(DependentValueProperty); }
set { SetValue(DependentValueProperty, value); }
}
///
/// Identifies the DependentValue dependency property.
///
public static readonly DependencyProperty DependentValueProperty =
DependencyProperty.Register(
"DependentValue",
typeof(IComparable),
typeof(DataPoint),
new PropertyMetadata(null, OnDependentValuePropertyChanged));
///
/// Called when the DependentValue property changes.
///
/// Control that changed its DependentValue.
/// Event arguments.
private static void OnDependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataPoint source = (DataPoint)d;
IComparable oldValue = (IComparable) e.OldValue;
IComparable newValue = (IComparable) e.NewValue;
source.OnDependentValuePropertyChanged(oldValue, newValue);
}
///
/// Called when the DependentValue property changes.
///
/// The value to be replaced.
/// The new value.
protected virtual void OnDependentValuePropertyChanged(IComparable oldValue, IComparable newValue)
{
SetFormattedProperty(FormattedDependentValueProperty, DependentValueStringFormat, newValue);
RoutedPropertyChangedEventHandler handler = this.DependentValueChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
if (this.State == DataPointState.Created)
{
// Prefer setting the value as a double...
double coercedNewValue;
if (ValueHelper.TryConvert(newValue, out coercedNewValue))
{
ActualDependentValue = coercedNewValue;
}
else
{
// ... but fall back otherwise
ActualDependentValue = newValue;
}
}
}
#endregion public IComparable DependentValue
#region public string DependentValueStringFormat
///
/// Gets or sets the format string for the FormattedDependentValue property.
///
public string DependentValueStringFormat
{
get { return GetValue(DependentValueStringFormatProperty) as string; }
set { SetValue(DependentValueStringFormatProperty, value); }
}
///
/// Identifies the DependentValueStringFormat dependency property.
///
public static readonly DependencyProperty DependentValueStringFormatProperty =
DependencyProperty.Register(
"DependentValueStringFormat",
typeof(string),
typeof(DataPoint),
new PropertyMetadata(null, OnDependentValueStringFormatPropertyChanged));
///
/// Called when DependentValueStringFormat property changes.
///
/// Control that changed its DependentValueStringFormat.
/// Event arguments.
private static void OnDependentValueStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataPoint source = d as DataPoint;
string oldValue = e.OldValue as string;
string newValue = e.NewValue as string;
source.OnDependentValueStringFormatPropertyChanged(oldValue, newValue);
}
///
/// Called when DependentValueStringFormat property changes.
///
/// The value to be replaced.
/// The new value.
protected virtual void OnDependentValueStringFormatPropertyChanged(string oldValue, string newValue)
{
SetFormattedProperty(FormattedDependentValueProperty, newValue, DependentValue);
}
#endregion public string DependentValueStringFormat
#region public string FormattedDependentValue
///
/// Gets the DependentValue as formatted by the DependentValueStringFormat property.
///
public string FormattedDependentValue
{
get { return GetValue(FormattedDependentValueProperty) as string; }
}
///
/// Identifies the FormattedDependentValue dependency property.
///
public static readonly DependencyProperty FormattedDependentValueProperty =
DependencyProperty.Register(
"FormattedDependentValue",
typeof(string),
typeof(DataPoint),
null);
#endregion public string FormattedDependentValue
#region public string FormattedIndependentValue
///
/// Gets the IndependentValue as formatted by the IndependentValueStringFormat property.
///
public string FormattedIndependentValue
{
get { return GetValue(FormattedIndependentValueProperty) as string; }
}
///
/// Identifies the FormattedIndependentValue dependency property.
///
public static readonly DependencyProperty FormattedIndependentValueProperty =
DependencyProperty.Register(
"FormattedIndependentValue",
typeof(string),
typeof(DataPoint),
null);
#endregion public string FormattedIndependentValue
///
/// Called when the independent value of the data point is changed.
///
internal event RoutedPropertyChangedEventHandler