896 lines
25 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using DevComponents.DotNetBar.Charts.Style;
namespace DevComponents.DotNetBar.Charts
{
/// <summary>
/// Represents a Crosshair element.
/// </summary>
[TypeConverter(typeof(BlankExpandableObjectConverter))]
public class ChartCrosshair : ChartVisualElement
{
#region Private variables
private States _States;
private PointIntersectMode _PointIntersectMode = PointIntersectMode.Edge;
private int _PointIntersectMargin = 2;
private AxisOrientation _AxisOrientation = AxisOrientation.X;
private CrosshairLabelMode _CrosshairLabelMode = CrosshairLabelMode.Common;
private CrosshairVisualStyle _CrosshairVisualStyle;
private EffectiveStyle<CrosshairVisualStyle> _EffectiveCrosshairStyle;
private CrosshairValueVisualStyle _CrosshairLabelVisualStyle;
private EffectiveStyle<CrosshairValueVisualStyle> _EffectiveCrosshairLabelStyle;
#endregion
public ChartCrosshair()
{
InitDefaultStates();
_EffectiveCrosshairStyle = new EffectiveStyle<CrosshairVisualStyle>(this);
_EffectiveCrosshairLabelStyle = new EffectiveStyle<CrosshairValueVisualStyle>(this);
}
#region InitDefaultStates
private void InitDefaultStates()
{
SetState(States.ShowCrosshairLabelMarkers, true);
SetState(States.ShowGroupHeader, true);
}
#endregion
#region Public properties
#region AxisOrientation
/// <summary>
/// Gets or sets the axis orientation driving the crosshair display.
/// </summary>
[DefaultValue(AxisOrientation.X), Category("Appearance")]
[Description("Indicates the axis orientation driving the crosshair display.")]
public AxisOrientation AxisOrientation
{
get { return (_AxisOrientation); }
set
{
if (value != _AxisOrientation)
{
_AxisOrientation = value;
OnPropertyChanged("AxisOrientation");
}
}
}
#endregion
#region CrosshairLabelMode
/// <summary>
/// Gets or sets the mode used to display the Crosshair label.
/// </summary>
[DefaultValue(CrosshairLabelMode.Common), Category("Behavior")]
[Description("Indicates the mode used to display the Crosshair label.")]
public CrosshairLabelMode CrosshairLabelMode
{
get { return (_CrosshairLabelMode); }
set
{
if (value != _CrosshairLabelMode)
{
_CrosshairLabelMode = value;
OnPropertyChangedEx("CrosshairLabelMode", VisualChangeType.Render);
}
}
}
#endregion
#region PointIntersectMode
/// <summary>
/// Gets or sets the the Crosshair point intersection mode.
/// </summary>
[DefaultValue(PointIntersectMode.Edge), Category("Behavior")]
[Description("Indicates the Crosshair point intersection mode.")]
public PointIntersectMode PointIntersectMode
{
get { return (_PointIntersectMode); }
set
{
if (value != _PointIntersectMode)
{
_PointIntersectMode = value;
OnPropertyChangedEx("PointIntersectMode", VisualChangeType.Render);
}
}
}
#endregion
#region PointIntersectMargin
/// <summary>
/// Gets or sets the the Crosshair point intersection margin.
/// </summary>
[DefaultValue(2), Category("Layout")]
[Description("Indicates the Crosshair point intersection margin.")]
public int PointIntersectMargin
{
get { return (_PointIntersectMargin); }
set
{
if (value != _PointIntersectMargin)
{
_PointIntersectMargin = value;
OnPropertyChangedEx("PointIntersectMargin", VisualChangeType.Render);
}
}
}
#endregion
#region CrosshairLabelVisualStyle
/// <summary>
/// Gets or sets the default visual style to be used for Crosshair
/// values rendered on the X and Y axes.
/// </summary>
[Category("Style")]
[Description("Indicates visual style to be used for Crosshair values rendered on the X and Y axes")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public CrosshairValueVisualStyle CrosshairLabelVisualStyle
{
get
{
if (_CrosshairLabelVisualStyle == null)
{
_CrosshairLabelVisualStyle = new CrosshairValueVisualStyle();
StyleVisualChangeHandler(null, _CrosshairLabelVisualStyle);
}
return (_CrosshairLabelVisualStyle);
}
set
{
if (_CrosshairLabelVisualStyle != value)
{
CrosshairValueVisualStyle oldValue = _CrosshairLabelVisualStyle;
_CrosshairLabelVisualStyle = value;
OnVisualStyleChanged("CrosshairLabelVisualStyle", oldValue, value);
}
}
}
#endregion
#region CrosshairVisualStyle
/// <summary>
/// Gets or sets the visual style for the Crosshair.
/// </summary>
[Category("Style")]
[Description("Indicates the visual style for the Crosshair.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public CrosshairVisualStyle CrosshairVisualStyle
{
get
{
if (_CrosshairVisualStyle == null)
{
_CrosshairVisualStyle = new CrosshairVisualStyle();
StyleVisualChangeHandler(null, _CrosshairVisualStyle);
}
return (_CrosshairVisualStyle);
}
set
{
if (_CrosshairVisualStyle != value)
{
CrosshairVisualStyle oldValue = _CrosshairVisualStyle;
_CrosshairVisualStyle = value;
OnVisualStyleChanged("CrosshairVisualStyle", oldValue, value);
if (oldValue != null)
oldValue.Dispose();
}
}
}
#endregion
#region EffectiveCrosshairLabelStyle
/// <summary>
/// Gets a reference to the Crosshair label's Effective (cached, composite) style.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public CrosshairValueVisualStyle EffectiveCrosshairLabelStyle
{
get { return (_EffectiveCrosshairLabelStyle.Style); }
}
#endregion
#region EffectiveCrosshairStyle
/// <summary>
/// Gets a reference to the Crosshair's Effective (cached, composite) style.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public CrosshairVisualStyle EffectiveCrosshairStyle
{
get { return (_EffectiveCrosshairStyle.Style); }
}
#endregion
#region HighlightPoints
/// <summary>
/// Gets or sets whether series points are highlighted
/// when the Crosshair cursor intersects with them.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether series points are highlighted when the Crosshair cursor intersects with them.")]
public bool HighlightPoints
{
get { return (TestState(States.HighlightPoints)); }
set
{
if (value != HighlightPoints)
{
SetState(States.HighlightPoints, value);
OnPropertyChangedEx("HighlightPoints", VisualChangeType.Render);
}
}
}
#endregion
#region HighlightSinglePoint
/// <summary>
/// Gets or sets whether only single series points are highlighted
/// when the Crosshair cursor intersects with them.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether only single series points are highlighted when the Crosshair cursor intersects with them.")]
public bool HighlightSinglePoint
{
get { return (TestState(States.HighlightSinglePoint)); }
set
{
if (value != HighlightSinglePoint)
{
SetState(States.HighlightSinglePoint, value);
OnPropertyChangedEx("HighlightSinglePoint", VisualChangeType.Render);
}
}
}
#endregion
#region ShowValueXLabels
/// <summary>
/// Gets or sets whether ValueX Labels are shown.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether ValueX Labels are shown.")]
public bool ShowValueXLabels
{
get { return (TestState(States.ShowValueXLabels)); }
set
{
if (value != ShowValueXLabels)
{
SetState(States.ShowValueXLabels, value);
OnPropertyChangedEx("ShowValueXLabels", VisualChangeType.Render);
}
}
}
#endregion
#region ShowValueXLine
/// <summary>
/// Gets or sets whether a ValueX line is shown.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether a ValueX line is shown.")]
public bool ShowValueXLine
{
get { return (TestState(States.ShowValueXLine)); }
set
{
if (value != ShowValueXLine)
{
SetState(States.ShowValueXLine, value);
OnPropertyChangedEx("ShowValueXLine", VisualChangeType.Render);
}
}
}
#endregion
#region ShowCrosshairLabelMarkers
/// <summary>
/// Gets or sets whether Crosshair label markers are shown.
/// </summary>
[DefaultValue(true), Category("Appearance")]
[Description("Indicates whether Crosshair label markers are shown.")]
public bool ShowCrosshairLabelMarkers
{
get { return (TestState(States.ShowCrosshairLabelMarkers)); }
set
{
if (value != ShowCrosshairLabelMarkers)
{
SetState(States.ShowCrosshairLabelMarkers, value);
OnPropertyChangedEx("ShowCrosshairLabelMarkers", VisualChangeType.Render);
}
}
}
#endregion
#region ShowCrosshairLabels
/// <summary>
/// Gets or sets whether Crosshair labels are shown.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether Crosshair labels are shown.")]
public bool ShowCrosshairLabels
{
get { return (TestState(States.ShowCrosshairLabels)); }
set
{
if (value != ShowCrosshairLabels)
{
SetState(States.ShowCrosshairLabels, value);
OnPropertyChangedEx("ShowCrosshairLabels", VisualChangeType.Render);
}
}
}
#endregion
#region ShowGroupHeaders
/// <summary>
/// Gets or sets whether Group Headers are shown for each series.
/// </summary>
[DefaultValue(true), Category("Appearance")]
[Description("Indicates whether Group Headers are shown for each series.")]
public bool ShowGroupHeaders
{
get { return (TestState(States.ShowGroupHeader)); }
set
{
if (value != ShowGroupHeaders)
{
SetState(States.ShowGroupHeader, value);
OnPropertyChangedEx("ShowGroupHeaders", VisualChangeType.Layout);
}
}
}
#endregion
#region ShowValueYLabels
/// <summary>
/// Gets or sets whether a ValueY label is shown.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether a ValueY label is shown.")]
public bool ShowValueYLabels
{
get { return (TestState(States.ShowValueYLabels)); }
set
{
if (value != ShowValueYLabels)
{
SetState(States.ShowValueYLabels, value);
OnPropertyChangedEx("ShowValueYLabels", VisualChangeType.Render);
}
}
}
#endregion
#region ShowValueYLine
/// <summary>
/// Gets or sets whether a ValueY line is shown.
/// </summary>
[DefaultValue(false), Category("Appearance")]
[Description("Indicates whether a ValueY line is shown.")]
public bool ShowValueYLine
{
get { return (TestState(States.ShowValueYLine)); }
set
{
if (value != ShowValueYLine)
{
SetState(States.ShowValueYLine, value);
OnPropertyChangedEx("ShowValueYLine", VisualChangeType.Render);
}
}
}
#endregion
#endregion
#region MeasureOverride
protected override void MeasureOverride(ChartLayoutInfo layoutInfo)
{
}
#endregion
#region ArrangeOverride
protected override void ArrangeOverride(ChartLayoutInfo layoutInfo)
{
}
#endregion
#region RenderOverride
protected override void RenderOverride(ChartRenderInfo renderInfo)
{
}
#endregion
#region Style support
#region ApplyStyles
public override void ApplyStyles(BaseVisualStyle style)
{
base.ApplyStyles(style);
CrosshairVisualStyle cstyle = style as CrosshairVisualStyle;
if (cstyle != null)
{
ApplyParentStyles(cstyle, Parent as ChartContainer);
cstyle.ApplyStyle(CrosshairVisualStyle);
ApplyDefaultLineStyles(cstyle.ValueXLineStyle);
ApplyDefaultLineStyles(cstyle.ValueYLineStyle);
if (cstyle.GroupHeaderFont == null)
cstyle.GroupHeaderFont = SystemFonts.CaptionFont;
if (cstyle.GroupHeaderTextColor.IsEmpty)
cstyle.GroupHeaderTextColor = Color.Black;
if (cstyle.Padding.IsEmpty == true)
cstyle.Padding = new Style.Padding(4);
if (cstyle.BorderThickness <= 0)
cstyle.BorderThickness = 1;
if (cstyle.BorderPattern == LinePattern.NotSet)
cstyle.BorderPattern = LinePattern.Solid;
if (cstyle.BorderColor.IsEmpty == true)
cstyle.BorderColor = Color.Black;
if (cstyle.Background.IsEmpty == true)
cstyle.Background = new Background(Color.White);
if (cstyle.Font == null)
cstyle.Font = SystemFonts.DefaultFont;
}
}
#region ApplyParentStyles
private void ApplyParentStyles(
CrosshairVisualStyle pstyle, ChartContainer item)
{
if (item != null)
{
ApplyParentStyles(pstyle, item.Parent as ChartContainer);
if (item is ChartPanel)
pstyle.ApplyStyle(((ChartPanel)item).DefaultVisualStyles.CrosshairVisualStyle);
}
else
{
pstyle.ApplyStyle(ChartControl.BaseVisualStyles.CrosshairVisualStyle);
pstyle.ApplyStyle(ChartControl.DefaultVisualStyles.CrosshairVisualStyle);
}
}
#endregion
#region ApplyDefaultLineStyles
private void ApplyDefaultLineStyles(ChartLineVisualStyle style)
{
if (style.LineColor.IsEmpty == true)
style.LineColor = Color.Fuchsia;
if (style.LinePattern == LinePattern.NotSet)
style.LinePattern = LinePattern.Solid;
if (style.LineWidth < 0)
style.LineWidth = 1;
}
#endregion
#region InvalidateStyle
///<summary>
///Invalidate the cached Styles
///</summary>
public void InvalidateStyle()
{
ClearEffectiveStyles();
}
#endregion
#region ClearEffectiveStyles
protected override void ClearEffectiveStyles()
{
_EffectiveCrosshairStyle.InvalidateStyle();
}
#endregion
#endregion
#endregion
#region Crosshair States
[Flags]
private enum States : uint
{
HighlightPoints = (1U << 0),
HighlightSinglePoint = (1U << 1),
ShowValueXLabels = (1U << 2),
ShowValueXLine = (1U << 3),
ShowCrosshairLabels = (1U << 4),
ShowGroupHeader = (1U << 5),
ShowCrosshairLabelMarkers = (1U << 6),
ShowValueYLabels = (1U << 7),
ShowValueYLine = (1U << 8),
}
#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 Copy/CopyTo
public override ChartVisualElement Copy()
{
ChartCrosshair copy = new ChartCrosshair();
CopyTo(copy);
return (copy);
}
public override void CopyTo(ChartVisualElement copy)
{
ChartCrosshair c = copy as ChartCrosshair;
if (c != null)
{
base.CopyTo(c);
c.AxisOrientation = AxisOrientation;
c.CrosshairLabelMode = CrosshairLabelMode;
c.PointIntersectMode = PointIntersectMode;
c.PointIntersectMargin = PointIntersectMargin;
c.CrosshairLabelVisualStyle = (_CrosshairLabelVisualStyle != null) ? CrosshairLabelVisualStyle.Copy() : null;
c.CrosshairVisualStyle = (_CrosshairVisualStyle != null) ? CrosshairVisualStyle.Copy() : null;
c.HighlightPoints = HighlightPoints;
c.HighlightSinglePoint = HighlightSinglePoint;
c.ShowValueXLabels = ShowValueXLabels;
c.ShowValueXLine = ShowValueXLine;
c.ShowCrosshairLabels = ShowCrosshairLabels;
c.ShowGroupHeaders = ShowGroupHeaders;
c.ShowValueYLabels = ShowValueYLabels;
c.ShowValueYLine = ShowValueYLine;
}
}
#endregion
#region GetSerialData
internal override SerialElementCollection GetSerialData(string serialName)
{
SerialElementCollection sec = new SerialElementCollection();
if (serialName != null)
{
if (serialName.Equals("") == true)
serialName = "ChartCrosshair";
sec.AddStartElement(serialName);
}
sec.AddValue("AxisOrientation", AxisOrientation, AxisOrientation.X);
sec.AddValue("CrosshairLabelMode", CrosshairLabelMode, CrosshairLabelMode.Common);
sec.AddValue("PointIntersectMode", PointIntersectMode, PointIntersectMode.Edge);
sec.AddValue("PointIntersectMargin", PointIntersectMargin, 2);
if (_CrosshairLabelVisualStyle != null && _CrosshairLabelVisualStyle.IsEmpty == false)
sec.AddElement(_CrosshairLabelVisualStyle.GetSerialData("CrosshairLabelVisualStyle"));
if (_CrosshairVisualStyle != null && _CrosshairVisualStyle.IsEmpty == false)
sec.AddElement(_CrosshairVisualStyle.GetSerialData("CrosshairVisualStyle"));
sec.AddValue("HighlightPoints", HighlightPoints, false);
sec.AddValue("HighlightSinglePoint", HighlightSinglePoint, false);
sec.AddValue("ShowValueXLabels", ShowValueXLabels, false);
sec.AddValue("ShowValueXLine", ShowValueXLine, false);
sec.AddValue("ShowCrosshairLabels", ShowCrosshairLabels, false);
sec.AddValue("ShowGroupHeaders", ShowGroupHeaders, true);
sec.AddValue("ShowValueYLabels", ShowValueYLabels, false);
sec.AddValue("ShowValueYLine", ShowValueYLine, false);
sec.AddElement(base.GetSerialData(null));
if (serialName != null)
sec.AddEndElement(serialName);
return (sec);
}
#endregion
#region PutSerialData
#region ProcessValue
internal override void ProcessValue(SerialElement se)
{
switch (se.Name)
{
case "AxisOrientation":
AxisOrientation = (AxisOrientation)se.GetValueEnum(typeof(AxisOrientation));
break;
case "CrosshairLabelMode":
CrosshairLabelMode = (CrosshairLabelMode)se.GetValueEnum(typeof(CrosshairLabelMode));
break;
case "PointIntersectMode":
PointIntersectMode = (PointIntersectMode)se.GetValueEnum(typeof(PointIntersectMode));
break;
case "PointIntersectMargin":
PointIntersectMargin = int.Parse(se.StringValue);
break;
case "HighlightPoints":
HighlightPoints = bool.Parse(se.StringValue);
break;
case "HighlightSinglePoint":
HighlightSinglePoint = bool.Parse(se.StringValue);
break;
case "ShowValueXLabels":
ShowValueXLabels = bool.Parse(se.StringValue);
break;
case "ShowValueXLine":
ShowValueXLine = bool.Parse(se.StringValue);
break;
case "ShowCrosshairLabels":
ShowCrosshairLabels = bool.Parse(se.StringValue);
break;
case "ShowGroupHeaders":
ShowGroupHeaders = bool.Parse(se.StringValue);
break;
case "ShowValueYLabels":
ShowValueYLabels = bool.Parse(se.StringValue);
break;
case "ShowValueYLine":
ShowValueYLine = bool.Parse(se.StringValue);
break;
default:
base.ProcessValue(se);
break;
}
}
#endregion
#region ProcessCollection
internal override void ProcessCollection(SerialElement se)
{
SerialElementCollection sec = se.Sec;
switch (se.Name)
{
case "CrosshairLabelVisualStyle":
sec.PutSerialData(CrosshairLabelVisualStyle);
break;
case "CrosshairVisualStyle":
sec.PutSerialData(CrosshairVisualStyle);
break;
default:
base.ProcessCollection(se);
break;
}
}
#endregion
#endregion
#region IDisposable
public override void Dispose()
{
CrosshairLabelVisualStyle = null;
CrosshairVisualStyle = null;
base.Dispose();
}
#endregion
}
#region enums
#region CrosshairLabelMode
public enum CrosshairLabelMode
{
/// <summary>
/// A common Crosshair label is shown for all series
/// </summary>
Common,
/// <summary>
/// A crosshair label is shown for the nearest series
/// </summary>
NearestSeries,
/// <summary>
/// A crosshair label is shown for each series
/// </summary>
EachSeries,
}
#endregion
#region CrosshairLabelPosition
public enum CrosshairLabelPosition
{
/// <summary>
/// Crosshair labels positioned relative to the Chart
/// </summary>
ChartRelative,
/// <summary>
/// Crosshair labels positioned relative to the Mouse
/// </summary>
MouseRelative,
}
#endregion
#region PointIntersectMode
public enum PointIntersectMode
{
/// <summary>
/// From center.
/// </summary>
Center,
/// <summary>
/// From edge.
/// </summary>
Edge,
}
#endregion
#endregion
}