// (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); } } }