All the controls missing in WPF. Over 1 million downloads.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1014 lines
40 KiB

// (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
{
/// <summary>
/// An axis that displays numeric values.
/// </summary>
[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
/// <summary>
/// Gets the actual maximum value plotted on the chart.
/// </summary>
public DateTime? ActualMaximum
{
get { return (DateTime?)GetValue(ActualMaximumProperty); }
private set { SetValue(ActualMaximumProperty, value); }
}
/// <summary>
/// Identifies the ActualMaximum dependency property.
/// </summary>
public static readonly DependencyProperty ActualMaximumProperty =
DependencyProperty.Register(
"ActualMaximum",
typeof(DateTime?),
typeof(DateTimeAxis),
null);
#endregion public DateTime? ActualMaximum
#region public DateTime? ActualMinimum
/// <summary>
/// Gets the actual maximum value plotted on the chart.
/// </summary>
public DateTime? ActualMinimum
{
get { return (DateTime?)GetValue(ActualMinimumProperty); }
private set { SetValue(ActualMinimumProperty, value); }
}
/// <summary>
/// Identifies the ActualMinimum dependency property.
/// </summary>
public static readonly DependencyProperty ActualMinimumProperty =
DependencyProperty.Register(
"ActualMinimum",
typeof(DateTime?),
typeof(DateTimeAxis),
null);
#endregion public DateTime? ActualMinimum
#region public DateTime? Maximum
/// <summary>
/// Gets or sets the maximum value plotted on the axis.
/// </summary>
[TypeConverter(typeof(NullableConverter<DateTime>))]
public DateTime? Maximum
{
get { return (DateTime?)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
/// <summary>
/// Identifies the Maximum dependency property.
/// </summary>
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register(
"Maximum",
typeof(DateTime?),
typeof(DateTimeAxis),
new PropertyMetadata(null, OnMaximumPropertyChanged));
/// <summary>
/// MaximumProperty property changed handler.
/// </summary>
/// <param name="d">DateTimeAxis2 that changed its Maximum.</param>
/// <param name="e">Event arguments.</param>
private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
DateTime? newValue = (DateTime?)e.NewValue;
source.OnMaximumPropertyChanged(newValue);
}
/// <summary>
/// MaximumProperty property changed handler.
/// </summary>
/// <param name="newValue">New value.</param>
private void OnMaximumPropertyChanged(DateTime? newValue)
{
this.ProtectedMaximum = newValue;
}
#endregion public DateTime? Maximum
#region public DateTime? Minimum
/// <summary>
/// Gets or sets the minimum value to plot on the axis.
/// </summary>
[TypeConverter(typeof(NullableConverter<DateTime>))]
public DateTime? Minimum
{
get { return (DateTime?)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
/// <summary>
/// Identifies the Minimum dependency property.
/// </summary>
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(
"Minimum",
typeof(DateTime?),
typeof(DateTimeAxis),
new PropertyMetadata(null, OnMinimumPropertyChanged));
/// <summary>
/// MinimumProperty property changed handler.
/// </summary>
/// <param name="d">DateTimeAxis2 that changed its Minimum.</param>
/// <param name="e">Event arguments.</param>
private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
DateTime? newValue = (DateTime?)e.NewValue;
source.OnMinimumPropertyChanged(newValue);
}
/// <summary>
/// MinimumProperty property changed handler.
/// </summary>
/// <param name="newValue">New value.</param>
private void OnMinimumPropertyChanged(DateTime? newValue)
{
this.ProtectedMinimum = newValue;
}
#endregion public DateTime? Minimum
#region public double? Interval
/// <summary>
/// Gets or sets the axis interval.
/// </summary>
[TypeConverter(typeof(NullableConverter<double>))]
public double? Interval
{
get { return (double?)GetValue(IntervalProperty); }
set { SetValue(IntervalProperty, value); }
}
/// <summary>
/// Identifies the Interval dependency property.
/// </summary>
public static readonly DependencyProperty IntervalProperty =
DependencyProperty.Register(
"Interval",
typeof(double?),
typeof(DateTimeAxis),
new PropertyMetadata(null, OnIntervalPropertyChanged));
/// <summary>
/// IntervalProperty property changed handler.
/// </summary>
/// <param name="d">DateTimeAxis2 that changed its Interval.</param>
/// <param name="e">Event arguments.</param>
private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
source.OnIntervalPropertyChanged();
}
/// <summary>
/// IntervalProperty property changed handler.
/// </summary>
private void OnIntervalPropertyChanged()
{
Invalidate();
}
#endregion public double? Interval
#region public double ActualInterval
/// <summary>
/// Gets the actual interval.
/// </summary>
public double ActualInterval
{
get { return (double)GetValue(ActualIntervalProperty); }
private set { SetValue(ActualIntervalProperty, value); }
}
/// <summary>
/// Identifies the ActualInterval dependency property.
/// </summary>
public static readonly DependencyProperty ActualIntervalProperty =
DependencyProperty.Register(
"ActualInterval",
typeof(double),
typeof(DateTimeAxis),
new PropertyMetadata(double.NaN));
#endregion public double ActualInterval
#region public DateTimeIntervalType IntervalType
/// <summary>
/// Gets or sets the interval to use for the axis.
/// </summary>
public DateTimeIntervalType IntervalType
{
get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); }
set { SetValue(IntervalTypeProperty, value); }
}
/// <summary>
/// Identifies the InternalIntervalType dependency property.
/// </summary>
public static readonly DependencyProperty IntervalTypeProperty =
DependencyProperty.Register(
"IntervalType",
typeof(DateTimeIntervalType),
typeof(DateTimeAxis),
new PropertyMetadata(DateTimeIntervalType.Auto, OnIntervalTypePropertyChanged));
/// <summary>
/// IntervalTypeProperty property changed handler.
/// </summary>
/// <param name="d">DateTimeAxis that changed its InternalIntervalType.</param>
/// <param name="e">Event arguments.</param>
private static void OnIntervalTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimeAxis source = (DateTimeAxis)d;
DateTimeIntervalType newValue = (DateTimeIntervalType)e.NewValue;
source.OnIntervalTypePropertyChanged(newValue);
}
/// <summary>
/// IntervalTypeProperty property changed handler.
/// </summary>
/// <param name="newValue">New value.</param>
private void OnIntervalTypePropertyChanged(DateTimeIntervalType newValue)
{
this.ActualIntervalType = newValue;
Invalidate();
}
#endregion public DateTimeIntervalType IntervalType
#region public DateTimeIntervalType ActualIntervalType
/// <summary>
/// Gets or sets the actual interval type.
/// </summary>
private DateTimeIntervalType ActualIntervalType
{
get { return (DateTimeIntervalType)GetValue(ActualIntervalTypeProperty); }
set { SetValue(ActualIntervalTypeProperty, value); }
}
/// <summary>
/// Identifies the ActualIntervalType dependency property.
/// </summary>
private static readonly DependencyProperty ActualIntervalTypeProperty =
DependencyProperty.Register(
"ActualIntervalType",
typeof(DateTimeIntervalType),
typeof(DateTimeAxis),
new PropertyMetadata(DateTimeIntervalType.Auto));
#endregion public DateTimeIntervalType ActualIntervalType
/// <summary>
/// Gets the origin value on the axis.
/// </summary>
protected override IComparable Origin
{
get { return null; }
}
/// <summary>
/// Instantiates a new instance of the DateTimeAxis2 class.
/// </summary>
public DateTimeAxis()
{
int year = DateTime.Now.Year;
this.ActualRange = new Range<IComparable>(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
}
/// <summary>
/// Creates a new instance of the DateTimeAxisLabel class.
/// </summary>
/// <returns>Returns a new instance of the DateTimeAxisLabel class.
/// </returns>
protected override Control CreateAxisLabel()
{
return new DateTimeAxisLabel();
}
/// <summary>
/// Prepares an instance of the DateTimeAxisLabel class by setting its
/// IntervalType property.
/// </summary>
/// <param name="label">An instance of the DateTimeAxisLabel class.
/// </param>
/// <param name="dataContext">The data context to assign to the label.
/// </param>
protected override void PrepareAxisLabel(Control label, object dataContext)
{
DateTimeAxisLabel dateTimeAxisLabel = label as DateTimeAxisLabel;
if (dateTimeAxisLabel != null)
{
dateTimeAxisLabel.IntervalType = ActualIntervalType;
}
base.PrepareAxisLabel(label, dataContext);
}
/// <summary>
/// Gets the actual range of DateTime values.
/// </summary>
protected Range<DateTime> ActualDateTimeRange { get; private set; }
/// <summary>
/// Updates the typed actual maximum and minimum properties when the
/// actual range changes.
/// </summary>
/// <param name="range">The actual range.</param>
protected override void OnActualRangeChanged(Range<IComparable> 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);
}
/// <summary>
/// Returns a value indicating whether a value can plot.
/// </summary>
/// <param name="value">The value to plot.</param>
/// <returns>A value indicating whether a value can plot.</returns>
public override bool CanPlot(object value)
{
DateTime val;
return ValueHelper.TryConvert(value, out val);
}
/// <summary>
/// Returns the plot area coordinate of a value.
/// </summary>
/// <param name="value">The value to plot.</param>
/// <param name="length">The length of the axis.</param>
/// <returns>The plot area coordinate of a value.</returns>
protected override UnitValue GetPlotAreaCoordinate(object value, double length)
{
return GetPlotAreaCoordinate(value, ActualDateTimeRange, length);
}
/// <summary>
/// Returns the plot area coordinate of a value.
/// </summary>
/// <param name="value">The value to plot.</param>
/// <param name="currentRange">The range to use determine the coordinate.</param>
/// <param name="length">The length of the axis.</param>
/// <returns>The plot area coordinate of a value.</returns>
protected override UnitValue GetPlotAreaCoordinate(object value, Range<IComparable> currentRange, double length)
{
return GetPlotAreaCoordinate(value, currentRange.ToDateTimeRange(), length);
}
/// <summary>
/// Returns the plot area coordinate of a value.
/// </summary>
/// <param name="value">The value to plot.</param>
/// <param name="currentRange">The range to use determine the coordinate.</param>
/// <param name="length">The length of the axis.</param>
/// <returns>The plot area coordinate of a value.</returns>
private static UnitValue GetPlotAreaCoordinate(object value, Range<DateTime> 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();
}
/// <summary>
/// Returns the actual interval to use to determine which values are
/// displayed in the axis.
/// </summary>
/// <param name="availableSize">The available size.</param>
/// <returns>The actual interval to use to determine which values are
/// displayed in the axis.
/// </returns>
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;
}
/// <summary>
/// Returns a sequence of major values.
/// </summary>
/// <param name="availableSize">The available size.</param>
/// <returns>A sequence of major values.</returns>
protected virtual IEnumerable<DateTime> 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<DateTime> intermediateDates =
EnumerableFunctions
.Iterate(start, next => IncrementDateTime(next, this.ActualInterval))
.TakeWhile(current => ActualDateTimeRange.Contains(current));
foreach (DateTime current in intermediateDates)
{
yield return current;
}
}
/// <summary>
/// Returns a sequence of values to create major tick marks for.
/// </summary>
/// <param name="availableSize">The available size.</param>
/// <returns>A sequence of values to create major tick marks for.
/// </returns>
protected override IEnumerable<IComparable> GetMajorTickMarkValues(Size availableSize)
{
return GetMajorAxisValues(availableSize).CastWrapper<IComparable>();
}
/// <summary>
/// Returns a sequence of values to plot on the axis.
/// </summary>
/// <param name="availableSize">The available size.</param>
/// <returns>A sequence of values to plot on the axis.</returns>
protected override IEnumerable<IComparable> GetLabelValues(Size availableSize)
{
return GetMajorAxisValues(availableSize).CastWrapper<IComparable>();
}
/// <summary>
/// This method accepts a date time and increments it.
/// </summary>
/// <param name="date">A date time.</param>
/// <param name="interval">The interval used to increment the date time.
/// </param>
/// <returns>The new date time.</returns>
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;
}
/// <summary>
/// Adjusts the beginning of the first interval depending on the type and size.
/// </summary>
/// <param name="start">Original start point.</param>
/// <param name="intervalSize">Interval size.</param>
/// <param name="type">Type of the interval (Month, Year, ...).</param>
/// <returns>
/// Adjusted interval start position.
/// </returns>
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;
}
/// <summary>
/// Returns the value range given a plot area coordinate.
/// </summary>
/// <param name="value">The position.</param>
/// <returns>A range of values at that plot area coordinate.</returns>
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;
}
/// <summary>
/// Recalculates a DateTime interval obtained from maximum and minimum.
/// </summary>
/// <param name="minimum">The minimum.</param>
/// <param name="maximum">The maximum.</param>
/// <param name="type">Date time interval type.</param>
/// <param name="availableSize">The available size.</param>
/// <returns>Auto Interval.</returns>
[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);
}
/// <summary>
/// Overrides the actual range to ensure that it is never set to an
/// empty range.
/// </summary>
/// <param name="range">The range to override.</param>
/// <returns>The overridden range.</returns>
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This method is very difficult to break up cleanly.")]
protected override Range<IComparable> OverrideDataRange(Range<IComparable> range)
{
Range<IComparable> overriddenActualRange = range;
if (!overriddenActualRange.HasData)
{
int year = DateTime.Now.Year;
return new Range<IComparable>(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<IComparable>(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<ValueMarginCoordinateAndOverlap> valueMargins = new List<ValueMarginCoordinateAndOverlap>();
foreach (ValueMargin valueMargin in
this.RegisteredListeners
.OfType<IValueMarginProvider>()
.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<DateTime> currentRange = range.ToDateTimeRange();
// Ensure range is not empty.
if (currentRange.Minimum == currentRange.Maximum)
{
int year = DateTime.Now.Year;
currentRange = new Range<DateTime>(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<DateTime>(newMinimum, newMaximum);
UpdateValueMargins(valueMargins, currentRange.ToComparableRange());
GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin);
}
return currentRange.ToComparableRange();
}
}
return range;
}
}
}