// (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.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace System.Windows.Controls.DataVisualization.Charting
{
///
/// Represents a data point used for a pie series.
///
/// Preview
[TemplatePart(Name = SliceName, Type = typeof(UIElement))]
[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 class PieDataPoint : DataPoint
{
///
/// The name of the slice template part.
///
private const string SliceName = "Slice";
///
/// Name of the ActualDataPointStyle property.
///
internal const string ActualDataPointStyleName = "ActualDataPointStyle";
#region public Geometry Geometry
///
/// Gets or sets the Geometry property which defines the shape of the
/// data point.
///
public Geometry Geometry
{
get { return GetValue(GeometryProperty) as Geometry; }
set { SetValue(GeometryProperty, value); }
}
///
/// Identifies the Geometry dependency property.
///
public static readonly DependencyProperty GeometryProperty =
DependencyProperty.Register(
"Geometry",
typeof(Geometry),
typeof(PieDataPoint),
null);
#endregion public Geometry Geometry
// GeometrySelection and GeometryHighlight exist on Silverlight because
// a single Geometry object can not be the target of multiple
// TemplateBindings - yet the default template has 3 Paths that bind.
#region public Geometry GeometrySelection
///
/// Gets or sets the Geometry which defines the shape of a point. The
/// GeometrySelection property is a copy of the Geometry property.
///
public Geometry GeometrySelection
{
get { return GetValue(GeometrySelectionProperty) as Geometry; }
set { SetValue(GeometrySelectionProperty, value); }
}
///
/// Identifies the GeometrySelection dependency property.
///
public static readonly DependencyProperty GeometrySelectionProperty =
DependencyProperty.Register(
"GeometrySelection",
typeof(Geometry),
typeof(PieDataPoint),
null);
#endregion public Geometry GeometrySelection
#region public Geometry GeometryHighlight
///
/// Gets or sets the GeometryHighlight property which is a clone of the
/// Geometry property.
///
public Geometry GeometryHighlight
{
get { return GetValue(GeometryHighlightProperty) as Geometry; }
set { SetValue(GeometryHighlightProperty, value); }
}
///
/// Identifies the GeometryHighlight dependency property.
///
public static readonly DependencyProperty GeometryHighlightProperty =
DependencyProperty.Register(
"GeometryHighlight",
typeof(Geometry),
typeof(PieDataPoint),
null);
#endregion public Geometry GeometryHighlight
///
/// Occurs when the actual offset ratio of the pie data point changes.
///
internal event RoutedPropertyChangedEventHandler ActualOffsetRatioChanged;
#region public double ActualOffsetRatio
///
/// Gets or sets the offset ratio that is displayed on the screen.
///
public double ActualOffsetRatio
{
get { return (double)GetValue(ActualOffsetRatioProperty); }
set { SetValue(ActualOffsetRatioProperty, value); }
}
///
/// Identifies the ActualOffsetRatio dependency property.
///
public static readonly DependencyProperty ActualOffsetRatioProperty =
DependencyProperty.Register(
"ActualOffsetRatio",
typeof(double),
typeof(PieDataPoint),
new PropertyMetadata(OnActualOffsetRatioPropertyChanged));
///
/// Called when the value of the ActualOffsetRatioProperty property changes.
///
/// PieDataPoint that changed its ActualOffsetRatio.
/// Event arguments.
private static void OnActualOffsetRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PieDataPoint source = (PieDataPoint)d;
double oldValue = (double)e.OldValue;
double newValue = (double)e.NewValue;
source.OnActualOffsetRatioPropertyChanged(oldValue, newValue);
}
///
/// Called when the value of the ActualOffsetRatioProperty property changes.
///
/// The value to be replaced.
/// The new value.
private void OnActualOffsetRatioPropertyChanged(double oldValue, double newValue)
{
RoutedPropertyChangedEventHandler handler = this.ActualOffsetRatioChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
if (DesignerProperties.GetIsInDesignMode(this))
{
PieSeries.UpdatePieDataPointGeometry(this, ActualWidth, ActualHeight);
}
}
#endregion public double ActualOffsetRatio
///
/// An event raised when the actual ratio of the pie data point is
/// changed.
///
internal event RoutedPropertyChangedEventHandler ActualRatioChanged;
#region public double ActualRatio
///
/// Gets or sets the ratio displayed on the screen.
///
public double ActualRatio
{
get { return (double)GetValue(ActualRatioProperty); }
set { SetValue(ActualRatioProperty, value); }
}
///
/// Identifies the ActualRatio dependency property.
///
public static readonly DependencyProperty ActualRatioProperty =
DependencyProperty.Register(
"ActualRatio",
typeof(double),
typeof(PieDataPoint),
new PropertyMetadata(OnActualRatioPropertyChanged));
///
/// Called when the value of the ActualRatioProperty property changes.
///
/// PieDataPoint that changed its ActualRatio.
/// Event arguments.
private static void OnActualRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PieDataPoint source = (PieDataPoint)d;
double oldValue = (double)e.OldValue;
double newValue = (double)e.NewValue;
source.OnActualRatioPropertyChanged(oldValue, newValue);
}
///
/// Called when the value of the ActualRatioProperty property changes.
///
/// The value to be replaced.
/// The new value.
private void OnActualRatioPropertyChanged(double oldValue, double newValue)
{
if (ValueHelper.CanGraph(newValue))
{
RoutedPropertyChangedEventHandler handler = this.ActualRatioChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
}
else
{
this.ActualRatio = 0.0;
}
if (DesignerProperties.GetIsInDesignMode(this))
{
PieSeries.UpdatePieDataPointGeometry(this, ActualWidth, ActualHeight);
}
}
#endregion public double ActualRatio
#region public string FormattedRatio
///
/// Gets the Ratio with the value of the RatioStringFormat property applied.
///
public string FormattedRatio
{
get { return GetValue(FormattedRatioProperty) as string; }
}
///
/// Identifies the FormattedRatio dependency property.
///
public static readonly DependencyProperty FormattedRatioProperty =
DependencyProperty.Register(
"FormattedRatio",
typeof(string),
typeof(PieDataPoint),
null);
#endregion public string FormattedRatio
///
/// An event raised when the offset ratio of the pie data point is
/// changed.
///
internal event RoutedPropertyChangedEventHandler OffsetRatioChanged;
#region public double OffsetRatio
///
/// Gets or sets the offset ratio of the pie data point.
///
public double OffsetRatio
{
get { return (double)GetValue(OffsetRatioProperty); }
set { SetValue(OffsetRatioProperty, value); }
}
///
/// Identifies the OffsetRatio dependency property.
///
public static readonly DependencyProperty OffsetRatioProperty =
DependencyProperty.Register(
"OffsetRatio",
typeof(double),
typeof(PieDataPoint),
new PropertyMetadata(OnOffsetRatioPropertyChanged));
///
/// Called when the value of the OffsetRatioProperty property changes.
///
/// PieDataPoint that changed its OffsetRatio.
/// Event arguments.
private static void OnOffsetRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PieDataPoint source = (PieDataPoint)d;
double oldValue = (double)e.OldValue;
double newValue = (double)e.NewValue;
source.OnOffsetRatioPropertyChanged(oldValue, newValue);
}
///
/// Called when the value of the OffsetRatioProperty property changes.
///
/// The value to be replaced.
/// The new value.
private void OnOffsetRatioPropertyChanged(double oldValue, double newValue)
{
if (ValueHelper.CanGraph(newValue))
{
RoutedPropertyChangedEventHandler handler = this.OffsetRatioChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
if (this.State == DataPointState.Created)
{
ActualOffsetRatio = newValue;
}
}
else
{
this.OffsetRatio = 0.0;
}
}
#endregion public double OffsetRatio
///
/// An event raised when the ratio of the pie data point is
/// changed.
///
internal event RoutedPropertyChangedEventHandler RatioChanged;
#region public double Ratio
///
/// Gets or sets the ratio of the total that the data point
/// represents.
///
public double Ratio
{
get { return (double)GetValue(RatioProperty); }
set { SetValue(RatioProperty, value); }
}
///
/// Identifies the Ratio dependency property.
///
public static readonly DependencyProperty RatioProperty =
DependencyProperty.Register(
"Ratio",
typeof(double),
typeof(PieDataPoint),
new PropertyMetadata(OnRatioPropertyChanged));
///
/// Called when the value of the RatioProperty property changes.
///
/// PieDataPoint that changed its Ratio.
/// Event arguments.
private static void OnRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PieDataPoint source = (PieDataPoint)d;
double oldValue = (double)e.OldValue;
double newValue = (double)e.NewValue;
source.OnRatioPropertyChanged(oldValue, newValue);
}
///
/// Called when the value of the RatioProperty property changes.
///
/// The value to be replaced.
/// The new value.
private void OnRatioPropertyChanged(double oldValue, double newValue)
{
if (ValueHelper.CanGraph(newValue))
{
SetFormattedProperty(FormattedRatioProperty, RatioStringFormat, newValue);
RoutedPropertyChangedEventHandler handler = this.RatioChanged;
if (handler != null)
{
handler(this, new RoutedPropertyChangedEventArgs(oldValue, newValue));
}
if (this.State == DataPointState.Created)
{
ActualRatio = newValue;
}
}
else
{
this.Ratio = 0.0;
}
}
#endregion public double Ratio
#region public string RatioStringFormat
///
/// Gets or sets the format string for the FormattedRatio property.
///
public string RatioStringFormat
{
get { return GetValue(RatioStringFormatProperty) as string; }
set { SetValue(RatioStringFormatProperty, value); }
}
///
/// Identifies the RatioStringFormat dependency property.
///
public static readonly DependencyProperty RatioStringFormatProperty =
DependencyProperty.Register(
"RatioStringFormat",
typeof(string),
typeof(PieDataPoint),
new PropertyMetadata(null, OnRatioStringFormatPropertyChanged));
///
/// Called when the value of the RatioStringFormatProperty property changes.
///
/// PieDataPoint that changed its RatioStringFormat.
/// Event arguments.
private static void OnRatioStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PieDataPoint source = d as PieDataPoint;
string newValue = e.NewValue as string;
source.OnRatioStringFormatPropertyChanged(newValue);
}
///
/// Called when the value of the RatioStringFormatProperty property changes.
///
/// The new value.
private void OnRatioStringFormatPropertyChanged(string newValue)
{
SetFormattedProperty(FormattedRatioProperty, newValue, Ratio);
}
#endregion public string RatioStringFormat
#region internal Style ActualDataPointStyle
///
/// Gets or sets the actual style used for the data points.
///
internal Style ActualDataPointStyle
{
get { return GetValue(ActualDataPointStyleProperty) as Style; }
set { SetValue(ActualDataPointStyleProperty, value); }
}
///
/// Identifies the ActualDataPointStyle dependency property.
///
internal static readonly DependencyProperty ActualDataPointStyleProperty =
DependencyProperty.Register(
ActualDataPointStyleName,
typeof(Style),
typeof(PieDataPoint),
null);
#endregion internal Style ActualDataPointStyle
#region internal Style ActualLegendItemStyle
///
/// Gets or sets the actual style used for the legend item.
///
internal Style ActualLegendItemStyle
{
get { return GetValue(ActualLegendItemStyleProperty) as Style; }
set { SetValue(ActualLegendItemStyleProperty, value); }
}
///
/// Identifies the ActualLegendItemStyle dependency property.
///
internal static readonly DependencyProperty ActualLegendItemStyleProperty =
DependencyProperty.Register(
DataPointSeries.ActualLegendItemStyleName,
typeof(Style),
typeof(PieDataPoint),
null);
#endregion protected Style ActualLegendItemStyle
///
/// Gets the Palette-dispensed ResourceDictionary for the Series.
///
protected internal ResourceDictionary PaletteResources { get; internal set; }
///
/// Gets or sets the element that represents the pie slice.
///
private UIElement SliceElement { get; set; }
#if !SILVERLIGHT
///
/// Initializes the static members of the PieDataPoint class.
///
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")]
static PieDataPoint()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PieDataPoint), new FrameworkPropertyMetadata(typeof(PieDataPoint)));
}
#endif
///
/// Initializes a new instance of the PieDataPoint class.
///
public PieDataPoint()
{
#if SILVERLIGHT
DefaultStyleKey = typeof(PieDataPoint);
#endif
if (DesignerProperties.GetIsInDesignMode(this))
{
// Create default design-mode-friendly settings
ActualRatio = 0.2;
SizeChanged += delegate(object sender, SizeChangedEventArgs e)
{
// Handle SizeChanged event to update Geometry dynamically
PieSeries.UpdatePieDataPointGeometry(this, e.NewSize.Width, e.NewSize.Height);
};
}
}
///
/// Builds the visual tree for the PieDataPoint when a new template is applied.
///
public override void OnApplyTemplate()
{
if (null != SliceElement)
{
SliceElement.MouseEnter -= new MouseEventHandler(SliceElement_MouseEnter);
SliceElement.MouseLeave -= new MouseEventHandler(SliceElement_MouseLeave);
}
base.OnApplyTemplate();
SliceElement = GetTemplateChild(SliceName) as UIElement;
if (null != SliceElement)
{
SliceElement.MouseEnter += new MouseEventHandler(SliceElement_MouseEnter);
SliceElement.MouseLeave += new MouseEventHandler(SliceElement_MouseLeave);
}
}
///
/// Provides handling for the MouseEnter event.
///
/// The event data.
protected override void OnMouseEnter(MouseEventArgs e)
{
// Do nothing because PieDataPoint handles SliceElement.MouseEnter instead
}
///
/// Provides handling for the MouseLeave event.
///
/// The event data.
protected override void OnMouseLeave(MouseEventArgs e)
{
// Do nothing because PieDataPoint handles SliceElement.MouseLeave instead
}
///
/// Provides handling for the MouseEnter event.
///
/// Event source.
/// The event data.
private void SliceElement_MouseEnter(object sender, MouseEventArgs e)
{
// Defer to Control's default MouseEnter handling
base.OnMouseEnter(e);
}
///
/// Provides handling for the MouseLeave event.
///
/// Event source.
/// The event data.
private void SliceElement_MouseLeave(object sender, MouseEventArgs e)
{
// Defer to Control's default MouseLeave handling
base.OnMouseLeave(e);
}
}
}