919 lines
24 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using DevComponents.DotNetBar.Charts.Style;
namespace DevComponents.DotNetBar.Charts
{
public class PieRing : ILegendItem, ILegendItemEx, INotifyPropertyChanged
{
#region Private variables
private int _RingLevel;
private int _RingWeight = -1;
private int _InnerRadius;
private int _OuterRadius;
private int _ExtentRadius;
private PieSeries _ChartSeries;
private List<PieSeriesPoint> _Psps;
private ChartLegendItem _LegendItem;
private string _LegendText;
private ChartLegendItemVisualStyles _ChartLegendItemVisualStyles;
private string _Name;
private States _States;
#endregion
public PieRing()
{
InitDefaultStates();
}
public PieRing(int ringLevel)
: this()
{
RingLevel = ringLevel;
}
#region InitDefaultStates
private void InitDefaultStates()
{
SetState(States.AllowDetach, true);
SetState(States.AllowSelect, true);
SetState(States.CheckedInLegend, true);
SetState(States.ShowInLegend, true);
SetState(States.ShowInParentLegend, true);
SetState(States.ShowMarkerInLegend, true);
SetState(States.Visible, true);
}
#endregion
#region Public properties
#region AllowDetach
/// <summary>
/// Gets or sets whether the element can be 'detached' from
/// the center of the pie by the user. Defaults to true.
/// </summary>
[DefaultValue(true), Category("Behavior")]
[Description("Indicates whether the element can be 'detached' from the center of the pie by the user. Defaults to true.")]
public bool AllowDetach
{
get { return (TestState(States.AllowDetach)); }
set
{
if (value != AllowDetach)
{
SetState(States.AllowDetach, value);
OnPropertyChanged("AllowDetach");
}
}
}
#endregion
#region AllowSelect
/// <summary>
/// Gets or sets whether the ring can be selected by clicking on it. Defaults to true.
/// </summary>
[DefaultValue(true), Category("Behavior")]
[Description("Indicates whether the ring can be selected by clicking on it. Defaults to true.")]
public bool AllowSelect
{
get { return (TestState(States.AllowSelect)); }
set
{
if (value != AllowSelect)
{
SetState(States.AllowSelect, value);
OnPropertyChanged("AllowSelect");
}
}
}
#endregion
#region IsDisplayed
///<summary>
/// Gets whether the ring is displayed (based upon Visibility and Legend state).
///</summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsDisplayed
{
get
{
if (ChartSeries != null)
{
PieChart pieChart = ChartSeries.Parent as PieChart;
if (pieChart != null)
{
ItemCheckAction ica = (pieChart != null)
? pieChart.Legend.ItemCheckAction : ItemCheckAction.ShowItem;
return ((Visible == true) &&
(ica == ItemCheckAction.None ||
(ShowCheckBoxInLegend == false || CheckedInLegend == true)));
}
}
return (false);
}
}
#endregion
#region Name
/// <summary>
/// Gets or sets the user assigned Name of the item.
/// </summary>
[DefaultValue("")]
[Description("Indicates the Name of the item.")]
public virtual string Name
{
get { return (_Name); }
set { _Name = value; }
}
#endregion
#region RingWeight
///<summary>
/// Gets or sets the 'relative' thickness of the series ring, as
/// compared to the relative thickness of other series
/// rings.
///</summary>
[DefaultValue(-1), Category("Display")]
[Description("Indicates the 'relative' thickness of the series ring, as compared to the relative thickness of other series rings.")]
public int RingWeight
{
get { return (_RingWeight); }
set
{
if (value != _RingWeight)
{
_RingWeight = value;
OnPropertyChangedEx("RingWeight", Style.VisualChangeType.Recalc);
}
}
}
#endregion
#region Visible
/// <summary>
/// Get or sets whether the item is visible
/// </summary>
[DefaultValue(true), Category("Appearance")]
[Description("Indicates whether the item is visible")]
public virtual bool Visible
{
get { return (TestState(States.Visible)); }
set
{
if (Visible != value)
{
SetState(States.Visible, value);
OnPropertyChangedEx("Visible", VisualChangeType.Layout);
}
}
}
#endregion
#endregion
#region Internal properties
#region ChartSeries
internal PieSeries ChartSeries
{
get { return (_ChartSeries); }
set { _ChartSeries = value; }
}
#endregion
#region ExtentRadius
internal int ExtentRadius
{
get { return (_ExtentRadius); }
set { _ExtentRadius = value; }
}
#endregion
#region InnerRadius
internal int InnerRadius
{
get { return (_InnerRadius); }
set { _InnerRadius = value; }
}
#endregion
#region IsEnabled
internal bool IsEnabled
{
get { return (IsRingEnabled()); }
}
#region IsRingEnabled
private bool IsRingEnabled()
{
if (ChartSeries != null)
{
int index = ChartSeries.PieRings.IndexOf(this);
if (index >= 0)
{
for (int i = index - 1; i >= 0; i--)
{
PieRing pieRing = ChartSeries.PieRings[i];
if (pieRing.IsDisplayed == false)
return (false);
}
}
return (ChartSeries.IsDisplayed);
}
return (false);
}
#endregion
#endregion
#region IsInitialRing
internal bool IsInnerRing
{
get { return (TestState(States.InnerRing)); }
set { SetState(States.InnerRing, value); }
}
#endregion
#region IsOuterRing
internal bool IsOuterRing
{
get { return (TestState(States.OuterRing)); }
set { SetState(States.OuterRing, value); }
}
#endregion
#region OuterRadius
internal int OuterRadius
{
get { return (_OuterRadius); }
set { _OuterRadius = value; }
}
#endregion
#region Psps
internal List<PieSeriesPoint> Psps
{
get
{
if (_Psps == null)
_Psps = new List<PieSeriesPoint>();
return (_Psps);
}
}
#endregion
#region RingLevel
internal int RingLevel
{
get { return (_RingLevel); }
set { _RingLevel = value; }
}
#endregion
#endregion
#region GetSelectionCount
/// <summary>
/// Gets a count of the visible selected pie series points.
/// </summary>
/// <returns></returns>
public int GetSelectionCount()
{
int count = 0;
foreach (PieSeriesPoint psp in Psps)
{
if (psp.Visible == true)
{
if (psp.IsSelected == true)
count++;
}
}
return (count);
}
#endregion
#region InvalidateRender
internal void InvalidateRender(PieChart pieChart)
{
foreach (PieSeriesPoint psp in Psps)
pieChart.InvalidateRender(psp.PathBounds);
}
#endregion
#region ILegendItem
#region ChartLegendItemVisualStyles
/// <summary>
/// Gets or sets the visual styles for the Legend item.
/// </summary>
[Category("Style")]
[Description("Indicates the visual styles for the Legend item.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ChartLegendItemVisualStyles ChartLegendItemVisualStyles
{
get
{
if (_ChartLegendItemVisualStyles == null)
{
_ChartLegendItemVisualStyles = new ChartLegendItemVisualStyles();
StyleChangeHandler(null, _ChartLegendItemVisualStyles);
}
return (_ChartLegendItemVisualStyles);
}
set
{
if (_ChartLegendItemVisualStyles != value)
{
ChartLegendItemVisualStyles oldValue = _ChartLegendItemVisualStyles;
_ChartLegendItemVisualStyles = value;
OnStyleChanged("ChartLegendItemVisualStyles", oldValue, value);
if (oldValue != null)
oldValue.Dispose();
}
}
}
#endregion
#region CheckedInLegend
/// <summary>
/// Gets or sets whether the item is checked in the Legend.
/// </summary>
[DefaultValue(true), Category("Legend")]
[Description("Indicates whether the item is checked in the Legend.")]
public bool CheckedInLegend
{
get { return (TestState(States.CheckedInLegend)); }
set
{
if (value != CheckedInLegend)
{
SetState(States.CheckedInLegend, value);
ChartSeries.InvalidateLayout();
if (LegendItem != null)
LegendItem.UpdateCheckState();
foreach (PieSeriesPoint psp in Psps)
{
if (psp.LegendItem != null)
psp.LegendItem.IsEnabled = psp.IsEnabled;
}
OnPropertyChangedEx("CheckedInLegend", VisualChangeType.Render);
}
}
}
#endregion
#region ShowCheckBoxInLegend
/// <summary>
/// Gets or sets whether a checkbox for the item is shown in the Legend.
/// </summary>
[DefaultValue(false), Category("Legend")]
[Description("Indicates whether a checkbox for the item is shown in the Legend.")]
public bool ShowCheckBoxInLegend
{
get { return (TestState(States.ShowCheckBoxInLegend)); }
set
{
if (value != ShowCheckBoxInLegend)
{
SetState(States.ShowCheckBoxInLegend, value);
OnPropertyChangedEx("ShowCheckBoxInLegend", VisualChangeType.Layout);
}
}
}
#endregion
#region ShowInLegend
/// <summary>
/// Gets or sets whether the item is shown in the Legend.
/// </summary>
[DefaultValue(true), Category("Legend")]
[Description("Indicates whether the item is shown in the Legend.")]
public bool ShowInLegend
{
get { return (TestState(States.ShowInLegend)); }
set
{
if (value != ShowInLegend)
{
SetState(States.ShowInLegend, value);
OnPropertyChangedEx("ShowInLegend", VisualChangeType.Layout);
}
}
}
#endregion
#region ShowInParentLegend
/// <summary>
/// Gets or sets whether the item Line is shown in parent Legend(s).
/// </summary>
[DefaultValue(true), Category("Legend")]
[Description("Indicates whether the item Line is shown in parent Legend(s).")]
public bool ShowInParentLegend
{
get { return (TestState(States.ShowInParentLegend)); }
set
{
if (value != ShowInParentLegend)
{
SetState(States.ShowInParentLegend, value);
OnPropertyChangedEx("ShowInParentLegend", VisualChangeType.Layout);
}
}
}
#endregion
#region ShowMarkerInLegend
/// <summary>
/// Gets or sets whether the item Marker is shown in the Legend.
/// </summary>
[DefaultValue(true), Category("Legend")]
[Description("Indicates whether the item Marker is shown in the Legend.")]
public bool ShowMarkerInLegend
{
get { return (TestState(States.ShowMarkerInLegend)); }
set
{
if (value != ShowMarkerInLegend)
{
SetState(States.ShowMarkerInLegend, value);
OnPropertyChangedEx("ShowMarkerInLegend", VisualChangeType.Layout);
}
}
}
#endregion
#region LegendItem
///<summary>
/// Gets the item's parent LegendItem.
///</summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Description("Indicates the item's parent LegendItem.")]
public ChartLegendItem LegendItem
{
get { return (_LegendItem); }
internal set { _LegendItem = value; }
}
#endregion
#region LegendText
///<summary>
/// Gets or sets the text to display in the legend.
///</summary>
[DefaultValue(null), Category("Legend")]
[Description("Indicates the text to display in the legend.")]
[NotifyParentProperty(true)]
public string LegendText
{
get { return (_LegendText); }
set
{
if (value != _LegendText)
{
_LegendText = value;
OnPropertyChangedEx("LegendText", Style.VisualChangeType.Layout);
}
}
}
#endregion
#region GetLegendItem
/// <summary>
/// Creates the LegendItem for the item.
/// </summary>
/// <returns></returns>
public ChartLegendItem GetLegendItem()
{
_LegendItem = null;
if (ShowInLegend == true)
{
_LegendItem = new ChartLegendItem();
if (CheckedInLegend == true)
_LegendItem.CheckState = CheckState.Checked;
_LegendItem.Name = Name;
_LegendItem.ItemText = LegendText;
if (string.IsNullOrEmpty(_LegendItem.Name) == true)
_LegendItem.Name = "(PieRing " + (RingLevel + 1) + ")";
_LegendItem.IsEnabled = IsEnabled;
_LegendItem.ChartItems.Add(this);
}
return (_LegendItem);
}
#endregion
#region GetLegendItems
/// <summary>
/// Creates a list of legend items associated with
/// the item.
/// </summary>
/// <returns></returns>
public List<ChartLegendItem> GetLegendItems()
{
List<ChartLegendItem> list = new List<ChartLegendItem>();
for (int i = 0; i < Psps.Count; i++)
{
PieSeriesPoint psp = Psps[i];
if (psp.IsDisplayable == true)
{
List<ChartLegendItem> items = psp.GetLegendItems();
if (items != null && items.Count > 0)
list.AddRange(items);
}
}
return (list);
}
#endregion
#region GetLegendItemColor
/// <summary>
/// Gets the default color associated with the legend item.
/// </summary>
/// <returns></returns>
public Color GetLegendItemColor()
{
if (Psps.Count > 0)
return (Psps[0].GetLegendItemColor());
return (ChartSeries.GetLegendItemColor());
}
#endregion
#region RenderLegendItemMarker
/// <summary>
/// Renders the Legend item Marker.
/// </summary>
/// <param name="g"></param>
/// <param name="litem"></param>
/// <param name="style"></param>
public void RenderLegendItemMarker(Graphics g,
ChartLegendItem litem, ChartLegendItemVisualStyle style)
{
Rectangle bounds = litem.MarkerBounds;
bounds.Inflate(-1, -1);
bounds.Width--;
bounds.Height--;
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.AntiAlias;
if (IsEnabled == true)
{
Color color = GetLegendItemColor();
using (Pen pen = new Pen(color, 2))
g.DrawEllipse(pen, bounds);
}
else
{
Color color = style.DisabledMarkerBackground.Color1;
if (color.IsEmpty == true)
color = style.DisabledTextColor;
if (color.IsEmpty == true)
color = Color.LightGray;
using (Pen pen = new Pen(color, 2))
g.DrawEllipse(pen, bounds);
}
g.SmoothingMode = sm;
}
#endregion
#endregion
#region States
[Flags]
private enum States : uint
{
AllowSelect = (1U << 0),
AllowDetach = (1U << 1),
InnerRing = (1U << 2),
OuterRing = (1U << 3),
CheckedInLegend = (1U << 4),
ShowInLegend = (1U << 5),
ShowInParentLegend = (1U << 6),
ShowCheckBoxInLegend = (1U << 7),
ShowMarkerInLegend = (1U << 8),
Visible = (1U << 9),
}
#region TestState
private bool TestState(States state)
{
return ((_States & state) == state);
}
#endregion
#region SetState
private void SetState(States state, bool value)
{
if (value == true)
_States |= state;
else
_States &= ~state;
}
#endregion
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when property value has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#region OnPropertyChanged
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="e">Event arguments</param>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
/// <summary>
/// Default PropertyChanged processing
/// </summary>
/// <param name="s"></param>
protected void OnPropertyChanged(string s)
{
if (PropertyChanged != null)
OnPropertyChanged(new PropertyChangedEventArgs(s));
}
/// <summary>
/// Default PropertyChanged processing
/// </summary>
/// <param name="s"></param>
/// <param name="changeType">invalidate</param>
protected void OnPropertyChangedEx(string s, VisualChangeType changeType)
{
if (PropertyChanged != null)
OnPropertyChanged(new VisualPropertyChangedEventArgs(s, changeType));
}
#endregion
#region NotifyVisualParent
internal void NotifyVisualParent(object sender, VisualChangeType vct)
{
object parent = sender;
PieSeries series = ChartSeries;
if (series != null)
{
if (vct == VisualChangeType.Layout)
series.InvalidateLayout();
else
series.InvalidateRender();
}
}
#endregion
#endregion
#region OnStyleChanged
protected virtual void OnStyleChanged(string property,
INotifyPropertyChanged oldValue, INotifyPropertyChanged newValue)
{
StyleChangeHandler(oldValue, newValue);
OnPropertyChanged(property);
}
#endregion
#region StyleChangeHandler
protected void StyleChangeHandler(
INotifyPropertyChanged oldValue, INotifyPropertyChanged newValue)
{
if (oldValue != null)
oldValue.PropertyChanged -= StyleChanged;
if (newValue != null)
newValue.PropertyChanged += StyleChanged;
}
#region StyleChanged
/// <summary>
/// Occurs when one of element visual styles has property changes.
/// Default implementation invalidates visual appearance of element.
/// </summary>
/// <param name="sender">VisualStyle that changed.</param>
/// <param name="e">Event arguments.</param>
protected void StyleChanged(object sender, PropertyChangedEventArgs e)
{
VisualChangeType changeType = ((VisualPropertyChangedEventArgs)e).ChangeType;
PieSeries series = ChartSeries;
if (series != null)
{
if (series.ChartControl != null)
series.ChartControl.GlobalUpdateCount++;
if (changeType == VisualChangeType.Layout)
series.InvalidateLayout();
else
series.InvalidateRender();
}
}
#endregion
#endregion
#region InvalidateRender
public void InvalidateRender()
{
PieSeries series = ChartSeries;
if (series != null)
series.InvalidateRender();
}
#endregion
#region TrackLegendItem
/// <summary>
/// Gets whether legend item tracking is enabled.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool TrackLegendItem
{
get
{
if (ChartSeries != null)
return (ChartSeries.TrackLegendItem);
return (false);
}
}
#endregion
#region FormatItemText
/// <summary>
/// Formats the provided item text.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string FormatItemText(string text)
{
return (text);
}
#endregion
}
}