// (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.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using EF = System.Windows.Controls.DataVisualization.EnumerableFunctions;
namespace System.Windows.Controls.DataVisualization.Charting
{
///
/// An axis that displays numeric values.
///
[StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))]
[StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))]
[StyleTypedProperty(Property = "MinorTickMarkStyle", StyleTargetType = typeof(Line))]
[StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(DateTimeAxisLabel))]
[StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))]
[TemplatePart(Name = AxisGridName, Type = typeof(Grid))]
[TemplatePart(Name = AxisTitleName, Type = typeof(Title))]
public class DateTimeAxis : RangeAxis
{
#region public DateTime? ActualMaximum
///
/// Gets the actual maximum value plotted on the chart.
///
public DateTime? ActualMaximum
{
get { return (DateTime?)GetValue(ActualMaximumProperty); }
private set { SetValue(ActualMaximumProperty, value); }
}
///
/// Identifies the ActualMaximum dependency property.
///
public static readonly DependencyProperty ActualMaximumProperty =
DependencyProperty.Register(
"ActualMaximum",
typeof(DateTime?),
typeof(DateTimeAxis),
null);
#endregion public DateTime? ActualMaximum
#region public DateTime? ActualMinimum
///
/// Gets the actual maximum value plotted on the chart.
///
public DateTime? ActualMinimum
{
get { return (DateTime?)GetValue(ActualMinimumProperty); }
private set { SetValue(ActualMinimumProperty, value); }
}
///
/// Identifies the ActualMinimum dependency property.
///
public static readonly DependencyProperty ActualMinimumProperty =
DependencyProperty.Register(
"ActualMinimum",
typeof(DateTime?),
typeof(DateTimeAxis),
null);
#endregion public DateTime? ActualMinimum
#region public DateTime? Maximum
///
/// Gets or sets the maximum value plotted on the axis.
///
[TypeConverter(typeof(NullableConverter))]
public DateTime? Maximum
{
get { return (DateTime?)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
///
/// Identifies the Maximum dependency property.
///
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register(
"Maximum",
typeof(DateTime?),
typeof(DateTimeAxis),
new PropertyMetadata(null, OnMaximumPropertyChanged));
///
/// MaximumProperty property changed handler.
///
/// DateTimeAxis2 that changed its Maximum.
/// Event arguments.
private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
DateTime? newValue = (DateTime?)e.NewValue;
source.OnMaximumPropertyChanged(newValue);
}
///
/// MaximumProperty property changed handler.
///
/// New value.
private void OnMaximumPropertyChanged(DateTime? newValue)
{
this.ProtectedMaximum = newValue;
}
#endregion public DateTime? Maximum
#region public DateTime? Minimum
///
/// Gets or sets the minimum value to plot on the axis.
///
[TypeConverter(typeof(NullableConverter))]
public DateTime? Minimum
{
get { return (DateTime?)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
///
/// Identifies the Minimum dependency property.
///
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(
"Minimum",
typeof(DateTime?),
typeof(DateTimeAxis),
new PropertyMetadata(null, OnMinimumPropertyChanged));
///
/// MinimumProperty property changed handler.
///
/// DateTimeAxis2 that changed its Minimum.
/// Event arguments.
private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
DateTime? newValue = (DateTime?)e.NewValue;
source.OnMinimumPropertyChanged(newValue);
}
///
/// MinimumProperty property changed handler.
///
/// New value.
private void OnMinimumPropertyChanged(DateTime? newValue)
{
this.ProtectedMinimum = newValue;
}
#endregion public DateTime? Minimum
#region public double? Interval
///
/// Gets or sets the axis interval.
///
[TypeConverter(typeof(NullableConverter))]
public double? Interval
{
get { return (double?)GetValue(IntervalProperty); }
set { SetValue(IntervalProperty, value); }
}
///
/// Identifies the Interval dependency property.
///
public static readonly DependencyProperty IntervalProperty =
DependencyProperty.Register(
"Interval",
typeof(double?),
typeof(DateTimeAxis),
new PropertyMetadata(null, OnIntervalPropertyChanged));
///
/// IntervalProperty property changed handler.
///
/// DateTimeAxis2 that changed its Interval.
/// Event arguments.
private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
source.OnIntervalPropertyChanged();
}
///
/// IntervalProperty property changed handler.
///
private void OnIntervalPropertyChanged()
{
Invalidate();
}
#endregion public double? Interval
#region public double ActualInterval
///
/// Gets the actual interval.
///
public double ActualInterval
{
get { return (double)GetValue(ActualIntervalProperty); }
private set { SetValue(ActualIntervalProperty, value); }
}
///
/// Identifies the ActualInterval dependency property.
///
public static readonly DependencyProperty ActualIntervalProperty =
DependencyProperty.Register(
"ActualInterval",
typeof(double),
typeof(DateTimeAxis),
new PropertyMetadata(double.NaN));
#endregion public double ActualInterval
#region public DateTimeIntervalType IntervalType
///
/// Gets or sets the interval to use for the axis.
///
public DateTimeIntervalType IntervalType
{
get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); }
set { SetValue(IntervalTypeProperty, value); }
}
///
/// Identifies the InternalIntervalType dependency property.
///
public static readonly DependencyProperty IntervalTypeProperty =
DependencyProperty.Register(
"IntervalType",
typeof(DateTimeIntervalType),
typeof(DateTimeAxis),
new PropertyMetadata(DateTimeIntervalType.Auto, OnIntervalTypePropertyChanged));
///
/// IntervalTypeProperty property changed handler.
///
/// DateTimeAxis that changed its InternalIntervalType.
/// Event arguments.
private static void OnIntervalTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
DateTimeIntervalType newValue = (DateTimeIntervalType)e.NewValue;
source.OnIntervalTypePropertyChanged(newValue);
}
///
/// IntervalTypeProperty property changed handler.
///
/// New value.
private void OnIntervalTypePropertyChanged(DateTimeIntervalType newValue)
{
this.ActualIntervalType = newValue;
Invalidate();
}
#endregion public DateTimeIntervalType IntervalType
#region public DateTimeIntervalType ActualIntervalType
///
/// Gets or sets the actual interval type.
///
private DateTimeIntervalType ActualIntervalType
{
get { return (DateTimeIntervalType)GetValue(ActualIntervalTypeProperty); }
set { SetValue(ActualIntervalTypeProperty, value); }
}
///
/// Identifies the ActualIntervalType dependency property.
///
private static readonly DependencyProperty ActualIntervalTypeProperty =
DependencyProperty.Register(
"ActualIntervalType",
typeof(DateTimeIntervalType),
typeof(DateTimeAxis),
new PropertyMetadata(DateTimeIntervalType.Auto));
#endregion public DateTimeIntervalType ActualIntervalType
///
/// Gets the origin value on the axis.
///
protected override IComparable Origin
{
get { return null; }
}
///
/// Instantiates a new instance of the DateTimeAxis2 class.
///
public DateTimeAxis()
{
int year = DateTime.Now.Year;
this.ActualRange = new Range(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
}
///
/// Creates a new instance of the DateTimeAxisLabel class.
///
/// Returns a new instance of the DateTimeAxisLabel class.
///
protected override Control CreateAxisLabel()
{
return new DateTimeAxisLabel();
}
///
/// Prepares an instance of the DateTimeAxisLabel class by setting its
/// IntervalType property.
///
/// An instance of the DateTimeAxisLabel class.
///
/// The data context to assign to the label.
///
protected override void PrepareAxisLabel(Control label, object dataContext)
{
DateTimeAxisLabel dateTimeAxisLabel = label as DateTimeAxisLabel;
if (dateTimeAxisLabel != null)
{
dateTimeAxisLabel.IntervalType = ActualIntervalType;
}
base.PrepareAxisLabel(label, dataContext);
}
///
/// Gets the actual range of DateTime values.
///
protected Range ActualDateTimeRange { get; private set; }
///
/// Updates the typed actual maximum and minimum properties when the
/// actual range changes.
///
/// The actual range.
protected override void OnActualRangeChanged(Range range)
{
ActualDateTimeRange = range.ToDateTimeRange();
if (range.HasData)
{
this.ActualMaximum = (DateTime)range.Maximum;
this.ActualMinimum = (DateTime)range.Minimum;
}
else
{
this.ActualMaximum = null;
this.ActualMinimum = null;
}
base.OnActualRangeChanged(range);
}
///
/// Returns a value indicating whether a value can plot.
///
/// The value to plot.
/// A value indicating whether a value can plot.
public override bool CanPlot(object value)
{
DateTime val;
return ValueHelper.TryConvert(value, out val);
}
///
/// Returns the plot area coordinate of a value.
///
/// The value to plot.
/// The length of the axis.
/// The plot area coordinate of a value.
protected override UnitValue GetPlotAreaCoordinate(object value, double length)
{
return GetPlotAreaCoordinate(value, ActualDateTimeRange, length);
}
///
/// Returns the plot area coordinate of a value.
///
/// The value to plot.
/// The range to use determine the coordinate.
/// The length of the axis.
/// The plot area coordinate of a value.
protected override UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length)
{
return GetPlotAreaCoordinate(value, currentRange.ToDateTimeRange(), length);
}
///
/// Returns the plot area coordinate of a value.
///
/// The value to plot.
/// The range to use determine the coordinate.
/// The length of the axis.
/// The plot area coordinate of a value.
private static UnitValue GetPlotAreaCoordinate(object value, Range currentRange, double length)
{
if (currentRange.HasData)
{
DateTime dateTimeValue = ValueHelper.ToDateTime(value);
double rangelength = currentRange.Maximum.ToOADate() - currentRange.Minimum.ToOADate();
double pixelLength = Math.Max(length - 1, 0);
return new UnitValue((dateTimeValue.ToOADate() - currentRange.Minimum.ToOADate()) * (pixelLength / rangelength), Unit.Pixels);
}
return UnitValue.NaN();
}
///
/// Returns the actual interval to use to determine which values are
/// displayed in the axis.
///
/// The available size.
/// The actual interval to use to determine which values are
/// displayed in the axis.
///
private double CalculateActualInterval(Size availableSize)
{
if (Interval != null)
{
return Interval.Value;
}
DateTimeIntervalType intervalType;
double interval = CalculateDateTimeInterval(ActualDateTimeRange.Minimum, ActualDateTimeRange.Maximum, out intervalType, availableSize);
ActualIntervalType = intervalType;
return interval;
}
///
/// Returns a sequence of major values.
///
/// The available size.
/// A sequence of major values.
protected virtual IEnumerable GetMajorAxisValues(Size availableSize)
{
if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0)
{
yield break;
}
this.ActualInterval = CalculateActualInterval(availableSize);
DateTime date = ActualDateTimeRange.Minimum;
DateTime start = AlignIntervalStart(date, this.ActualInterval, ActualIntervalType);
while (start < date)
{
start = IncrementDateTime(start, this.ActualInterval);
}
IEnumerable intermediateDates =
EnumerableFunctions
.Iterate(start, next => IncrementDateTime(next, this.ActualInterval))
.TakeWhile(current => ActualDateTimeRange.Contains(current));
foreach (DateTime current in intermediateDates)
{
yield return current;
}
}
///
/// Returns a sequence of values to create major tick marks for.
///
/// The available size.
/// A sequence of values to create major tick marks for.
///
protected override IEnumerable GetMajorTickMarkValues(Size availableSize)
{
return GetMajorAxisValues(availableSize).CastWrapper();
}
///
/// Returns a sequence of values to plot on the axis.
///
/// The available size.
/// A sequence of values to plot on the axis.
protected override IEnumerable GetLabelValues(Size availableSize)
{
return GetMajorAxisValues(availableSize).CastWrapper();
}
///
/// This method accepts a date time and increments it.
///
/// A date time.
/// The interval used to increment the date time.
///
/// The new date time.
private DateTime IncrementDateTime(DateTime date, double interval)
{
DateTimeIntervalType intervalType = this.ActualIntervalType;
TimeSpan span = new TimeSpan(0);
DateTime result;
if (intervalType == DateTimeIntervalType.Days)
{
span = TimeSpan.FromDays(interval);
}
else if (intervalType == DateTimeIntervalType.Hours)
{
span = TimeSpan.FromHours(interval);
}
else if (intervalType == DateTimeIntervalType.Milliseconds)
{
span = TimeSpan.FromMilliseconds(interval);
}
else if (intervalType == DateTimeIntervalType.Seconds)
{
span = TimeSpan.FromSeconds(interval);
}
else if (intervalType == DateTimeIntervalType.Minutes)
{
span = TimeSpan.FromMinutes(interval);
}
else if (intervalType == DateTimeIntervalType.Weeks)
{
span = TimeSpan.FromDays(7.0 * interval);
}
else if (intervalType == DateTimeIntervalType.Months)
{
// Special case handling when current date point
// to the last day of the month
bool lastMonthDay = false;
if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))
{
lastMonthDay = true;
}
// Add specified amount of months
date = date.AddMonths((int)Math.Floor(interval));
span = TimeSpan.FromDays(30.0 * (interval - Math.Floor(interval)));
// Check if last month of the day was used
if (lastMonthDay && span.Ticks == 0)
{
// Make sure the last day of the month is selected
int daysInMobth = DateTime.DaysInMonth(date.Year, date.Month);
date = date.AddDays(daysInMobth - date.Day);
}
}
else if (intervalType == DateTimeIntervalType.Years)
{
date = date.AddYears((int)Math.Floor(interval));
span = TimeSpan.FromDays(365.0 * (interval - Math.Floor(interval)));
}
result = date.Add(span);
return result;
}
///
/// Adjusts the beginning of the first interval depending on the type and size.
///
/// Original start point.
/// Interval size.
/// Type of the interval (Month, Year, ...).
///
/// Adjusted interval start position.
///
private static DateTime AlignIntervalStart(DateTime start, double intervalSize, DateTimeIntervalType type)
{
// Do not adjust start position for these interval type
if (type == DateTimeIntervalType.Auto)
{
return start;
}
// Get the beginning of the interval depending on type
DateTime newStartDate = start;
// Adjust the months interval depending on size
if (intervalSize > 0.0 && intervalSize != 1.0)
{
if (type == DateTimeIntervalType.Months && intervalSize <= 12.0 && intervalSize > 1)
{
// Make sure that the beginning is aligned correctly for cases
// like quarters and half years
DateTime resultDate = newStartDate;
DateTime sizeAdjustedDate = new DateTime(newStartDate.Year, 1, 1, 0, 0, 0);
while (sizeAdjustedDate < newStartDate)
{
resultDate = sizeAdjustedDate;
sizeAdjustedDate = sizeAdjustedDate.AddMonths((int)intervalSize);
}
newStartDate = resultDate;
return newStartDate;
}
}
// Check interval type
switch (type)
{
case DateTimeIntervalType.Years:
int year = (int)((int)(newStartDate.Year / intervalSize) * intervalSize);
if (year <= 0)
{
year = 1;
}
newStartDate = new DateTime(year, 1, 1, 0, 0, 0);
break;
case DateTimeIntervalType.Months:
int month = (int)((int)(newStartDate.Month / intervalSize) * intervalSize);
if (month <= 0)
{
month = 1;
}
newStartDate = new DateTime(newStartDate.Year, month, 1, 0, 0, 0);
break;
case DateTimeIntervalType.Days:
int day = (int)((int)(newStartDate.Day / intervalSize) * intervalSize);
if (day <= 0)
{
day = 1;
}
newStartDate = new DateTime(newStartDate.Year, newStartDate.Month, day, 0, 0, 0);
break;
case DateTimeIntervalType.Hours:
int hour = (int)((int)(newStartDate.Hour / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
hour,
0,
0);
break;
case DateTimeIntervalType.Minutes:
int minute = (int)((int)(newStartDate.Minute / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
minute,
0);
break;
case DateTimeIntervalType.Seconds:
int second = (int)((int)(newStartDate.Second / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
newStartDate.Minute,
second,
0);
break;
case DateTimeIntervalType.Milliseconds:
int milliseconds = (int)((int)(newStartDate.Millisecond / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
newStartDate.Minute,
newStartDate.Second,
milliseconds);
break;
case DateTimeIntervalType.Weeks:
// Elements that have interval set to weeks should be aligned to the
// nearest start of week no matter how many weeks is the interval.
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
0,
0,
0);
newStartDate = newStartDate.AddDays(-((int)newStartDate.DayOfWeek));
break;
}
return newStartDate;
}
///
/// Returns the value range given a plot area coordinate.
///
/// The position.
/// A range of values at that plot area coordinate.
protected override IComparable GetValueAtPosition(UnitValue value)
{
if (ActualRange.HasData && ActualLength != 0.0)
{
double coordinate = value.Value;
if (value.Unit == Unit.Pixels)
{
double minimumAsDouble = ActualDateTimeRange.Minimum.ToOADate();
double rangelength = ActualDateTimeRange.Maximum.ToOADate() - minimumAsDouble;
DateTime output = DateTime.FromOADate((coordinate * (rangelength / ActualLength)) + minimumAsDouble);
return output;
}
else
{
throw new NotImplementedException();
}
}
return null;
}
///
/// Recalculates a DateTime interval obtained from maximum and minimum.
///
/// The minimum.
/// The maximum.
/// Date time interval type.
/// The available size.
/// Auto Interval.
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "The method should inspect all variations of time span (millisec to year) and contains long case. Otherwise is simple and readable.")]
private double CalculateDateTimeInterval(DateTime minimum, DateTime maximum, out DateTimeIntervalType type, Size availableSize)
{
DateTime dateTimeMin = minimum;
DateTime dateTimeMax = maximum;
TimeSpan timeSpan = dateTimeMax.Subtract(dateTimeMin);
// this algorithm is designed to return close to 10 intervals.
// we need to align the time span for PrefferedNumberOfIntervals
double maxIntervals = Orientation == AxisOrientation.X ? MaximumAxisIntervalsPer200Pixels * 0.8 : MaximumAxisIntervalsPer200Pixels;
double rangeMultiplicator = GetLength(availableSize) / (200 * 10 / maxIntervals);
timeSpan = new TimeSpan((long)((double)timeSpan.Ticks / rangeMultiplicator));
// Minutes
double inter = timeSpan.TotalMinutes;
// For Range less than 60 seconds interval is 5 sec
if (inter <= 1.0)
{
// Milli Seconds
double milliSeconds = timeSpan.TotalMilliseconds;
if (milliSeconds <= 10)
{
type = DateTimeIntervalType.Milliseconds;
return 1;
}
if (milliSeconds <= 50)
{
type = DateTimeIntervalType.Milliseconds;
return 4;
}
if (milliSeconds <= 200)
{
type = DateTimeIntervalType.Milliseconds;
return 20;
}
if (milliSeconds <= 500)
{
type = DateTimeIntervalType.Milliseconds;
return 50;
}
// Seconds
double seconds = timeSpan.TotalSeconds;
if (seconds <= 7)
{
type = DateTimeIntervalType.Seconds;
return 1;
}
else if (seconds <= 15)
{
type = DateTimeIntervalType.Seconds;
return 2;
}
else if (seconds <= 30)
{
type = DateTimeIntervalType.Seconds;
return 5;
}
else if (seconds <= 60)
{
type = DateTimeIntervalType.Seconds;
return 10;
}
}
else if (inter <= 2.0)
{
// For Range less than 120 seconds interval is 10 sec
type = DateTimeIntervalType.Seconds;
return 20;
}
else if (inter <= 3.0)
{
// For Range less than 180 seconds interval is 30 sec
type = DateTimeIntervalType.Seconds;
return 30;
}
else if (inter <= 10)
{
// For Range less than 10 minutes interval is 1 min
type = DateTimeIntervalType.Minutes;
return 1;
}
else if (inter <= 20)
{
// For Range less than 20 minutes interval is 1 min
type = DateTimeIntervalType.Minutes;
return 2;
}
else if (inter <= 60)
{
// For Range less than 60 minutes interval is 5 min
type = DateTimeIntervalType.Minutes;
return 5;
}
else if (inter <= 120)
{
// For Range less than 120 minutes interval is 10 min
type = DateTimeIntervalType.Minutes;
return 10;
}
else if (inter <= 180)
{
// For Range less than 180 minutes interval is 30 min
type = DateTimeIntervalType.Minutes;
return 30;
}
else if (inter <= 60 * 12)
{
// For Range less than 12 hours interval is 1 hour
type = DateTimeIntervalType.Hours;
return 1;
}
else if (inter <= 60 * 24)
{
// For Range less than 24 hours interval is 4 hour
type = DateTimeIntervalType.Hours;
return 4;
}
else if (inter <= 60 * 24 * 2)
{
// For Range less than 2 days interval is 6 hour
type = DateTimeIntervalType.Hours;
return 6;
}
else if (inter <= 60 * 24 * 3)
{
// For Range less than 3 days interval is 12 hour
type = DateTimeIntervalType.Hours;
return 12;
}
else if (inter <= 60 * 24 * 10)
{
// For Range less than 10 days interval is 1 day
type = DateTimeIntervalType.Days;
return 1;
}
else if (inter <= 60 * 24 * 20)
{
// For Range less than 20 days interval is 2 day
type = DateTimeIntervalType.Days;
return 2;
}
else if (inter <= 60 * 24 * 30)
{
// For Range less than 30 days interval is 3 day
type = DateTimeIntervalType.Days;
return 3;
}
else if (inter <= 60 * 24 * 30.5 * 2)
{
// For Range less than 2 months interval is 1 week
type = DateTimeIntervalType.Weeks;
return 1;
}
else if (inter <= 60 * 24 * 30.5 * 5)
{
// For Range less than 5 months interval is 2weeks
type = DateTimeIntervalType.Weeks;
return 2;
}
else if (inter <= 60 * 24 * 30.5 * 12)
{
// For Range less than 12 months interval is 1 month
type = DateTimeIntervalType.Months;
return 1;
}
else if (inter <= 60 * 24 * 30.5 * 24)
{
// For Range less than 24 months interval is 3 month
type = DateTimeIntervalType.Months;
return 3;
}
else if (inter <= 60 * 24 * 30.5 * 48)
{
// For Range less than 48 months interval is 6 months
type = DateTimeIntervalType.Months;
return 6;
}
// For Range more than 48 months interval is year
type = DateTimeIntervalType.Years;
double years = inter / 60 / 24 / 365;
if (years < 5)
{
return 1;
}
else if (years < 10)
{
return 2;
}
// Make a correction of the interval
return Math.Floor(years / 5);
}
///
/// Overrides the actual range to ensure that it is never set to an
/// empty range.
///
/// The range to override.
/// The overridden range.
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This method is very difficult to break up cleanly.")]
protected override Range OverrideDataRange(Range range)
{
Range overriddenActualRange = range;
if (!overriddenActualRange.HasData)
{
int year = DateTime.Now.Year;
return new Range(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
}
else if (ValueHelper.Compare(overriddenActualRange.Minimum, overriddenActualRange.Maximum) == 0)
{
DateTime minimum = ValueHelper.ToDateTime(overriddenActualRange.Minimum);
DateTime midpoint = ((DateTime.MinValue == minimum) ? DateTime.Now : minimum).Date;
return new Range(midpoint.AddMonths(-6), midpoint.AddMonths(6));
}
// ActualLength of 1.0 or less maps all points to the same coordinate
if (range.HasData && this.ActualLength > 1.0)
{
IList valueMargins = new List();
foreach (ValueMargin valueMargin in
this.RegisteredListeners
.OfType()
.SelectMany(provider => provider.GetValueMargins(this)))
{
valueMargins.Add(
new ValueMarginCoordinateAndOverlap
{
ValueMargin = valueMargin,
});
}
if (valueMargins.Count > 0)
{
double maximumPixelMarginLength =
valueMargins
.Select(valueMargin => valueMargin.ValueMargin.LowMargin + valueMargin.ValueMargin.HighMargin)
.MaxOrNullable().Value;
// Requested margin is larger than the axis so give up
// trying to find a range that will fit it.
if (maximumPixelMarginLength > this.ActualLength)
{
return range;
}
Range currentRange = range.ToDateTimeRange();
// Ensure range is not empty.
if (currentRange.Minimum == currentRange.Maximum)
{
int year = DateTime.Now.Year;
currentRange = new Range(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
}
// priming the loop
double actualLength = this.ActualLength;
ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin;
ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin;
UpdateValueMargins(valueMargins, currentRange.ToComparableRange());
GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin);
while (maxLeftOverlapValueMargin.LeftOverlap > 0 || maxRightOverlapValueMargin.RightOverlap > 0)
{
long unitOverPixels = currentRange.GetLength().Value.Ticks / ((long) actualLength);
DateTime newMinimum = new DateTime(currentRange.Minimum.Ticks - (long)((maxLeftOverlapValueMargin.LeftOverlap + 0.5) * unitOverPixels));
DateTime newMaximum = new DateTime(currentRange.Maximum.Ticks + (long)((maxRightOverlapValueMargin.RightOverlap + 0.5) * unitOverPixels));
currentRange = new Range(newMinimum, newMaximum);
UpdateValueMargins(valueMargins, currentRange.ToComparableRange());
GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin);
}
return currentRange.ToComparableRange();
}
}
return range;
}
}
}