//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @owner=alexgor, deliant //================================================================= // File: AxisScrollZoom.cs // // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting // // Classes: AxisScaleView, ViewEventArgs, DoubleNanValueConverter // // Purpose: AxisScaleView class represents a data scaleView, and is // exposed using the Axis.ScaleView property. A data scaleView is // a "scaleView" of data that has a start position (represented // by the Position property) and a size (represented by // the Size property). // // Axis data scaleView is used in zooming and scrolling when // only part of the data must be visible. Views always // belong to an axis, and a scaleView can result from either // user interaction or by calling the Zoom or Scroll // methods. User interaction, accomplished using range // selection along an axis using the mouse, is possible // if the IsUserSelectionEnabled property of the chart area' // s cursor property is set to true. The end-user selects // a range by left-clicking the mouse and dragging the // mouse, and when the mouse button is released the // selected range is then displayed as a scaleView. // // Reviewed: AG - Microsoft 16, 2007 // //=================================================================== #region Used namespace using System; using System.Drawing; using System.Drawing.Drawing2D; using System.ComponentModel; using System.Collections; using System.Globalization; #if Microsoft_CONTROL using System.Windows.Forms.DataVisualization.Charting; using System.Windows.Forms.DataVisualization.Charting.Data; using System.Windows.Forms.DataVisualization.Charting.ChartTypes; using System.Windows.Forms.DataVisualization.Charting.Utilities; using System.Windows.Forms.DataVisualization.Charting.Borders3D; using System.Diagnostics.CodeAnalysis; #else using System.Web.UI.DataVisualization.Charting; using System.Web.UI.DataVisualization.Charting.Utilities; #endif #endregion #if Microsoft_CONTROL namespace System.Windows.Forms.DataVisualization.Charting #else namespace System.Web.UI.DataVisualization.Charting #endif { #region Scrolling enumerations #if Microsoft_CONTROL /// /// Scrolling type enumeration. /// public enum ScrollType { /// /// Scrolls by substracting one small size. /// SmallDecrement, /// /// Scrolls by adding one small size. /// SmallIncrement, /// /// Scrolls by substracting one scaleView size. /// LargeDecrement, /// /// Scrolls by adding one scaleView size. /// LargeIncrement, /// /// Scrolls to the first scaleView. /// First, /// /// Scrolls to the last scaleView. /// Last } #endif // Microsoft_CONTROL #endregion /// /// AxisScaleView class represents a scale view which allows to display /// only part of the available data. /// [ SRDescription("DescriptionAttributeAxisDataView_AxisDataView"), DefaultProperty("Position"), ] #if ASPPERM_35 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] #endif public class AxisScaleView { #region Fields // Reference to the axis object internal Axis axis = null; // Axis data scaleView position private double _position = double.NaN; // Axis data scaleView size private double _size = double.NaN; // Axis data scaleView size units type private DateTimeIntervalType _sizeType = DateTimeIntervalType.Auto; #if Microsoft_CONTROL // Axis data scaleView minimum scaleView/scrolling size private double _minSize = double.NaN; // Axis data scaleView minimum scaleView/scrolling size units type private DateTimeIntervalType _minSizeType = DateTimeIntervalType.Auto; // Axis data scaleView zooming UI interface enabled flag private bool _zoomable = true; // Axis data scaleView scroll line size private double _smallScrollSize = double.NaN; // Axis data scaleView scroll line size units type private DateTimeIntervalType _smallScrollSizeType = DateTimeIntervalType.Auto; // Axis data scaleView scroll line minimum size private double _smallScrollMinSize = 1.0; // Axis data scaleView scroll line minimum size units type private DateTimeIntervalType _smallScrollMinSizeType = DateTimeIntervalType.Auto; // Axis data scaleView scroll line minimum size private double _currentSmallScrollSize = double.NaN; // Axis data scaleView scroll line minimum size units type private DateTimeIntervalType _currentSmallScrollSizeType = DateTimeIntervalType.Auto; // Storage for the saved data scaleView states (position/size/sizetype) internal ArrayList dataViewStates = null; #endif // Ignore validation flag private bool _ignoreValidation = false; #endregion #region Constructor /// /// Default constructor /// public AxisScaleView() { this.axis = null; } /// /// Internal constructor. /// /// Data scaleView axis. internal AxisScaleView(Axis axis) { this.axis = axis; } #endregion #region Axis data scaleView properties /// /// Gets or sets the position of the AxisScaleView. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(Double.NaN), SRDescription("DescriptionAttributeAxisDataView_Position"), TypeConverter(typeof(DoubleDateNanValueConverter)), ParenthesizePropertyNameAttribute(true) ] public double Position { get { // Axis scaleView is not supported in circular chrt areas if(this.axis != null && this.axis.ChartArea != null && this.axis.ChartArea.chartAreaIsCurcular) { return Double.NaN; } return _position; } set { // Axis scaleView is not supported in circular chrt areas if(this.axis != null && this.axis.ChartArea != null && this.axis.ChartArea.chartAreaIsCurcular) { return; } if(_position != value) { // Set new position _position = value; // Align scaleView in connected areas if(this.axis != null && this.axis.ChartArea != null && this.axis.Common != null && this.axis.Common.ChartPicture != null) { if(!this.axis.ChartArea.alignmentInProcess) { AreaAlignmentOrientations orientation = (this.axis.axisType == AxisName.X || this.axis.axisType== AxisName.X2) ? AreaAlignmentOrientations.Vertical : AreaAlignmentOrientations.Horizontal; this.axis.Common.ChartPicture.AlignChartAreasAxesView(this.axis.ChartArea, orientation); } } // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } /// /// Gets or sets the size of the AxisScaleView /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(Double.NaN), SRDescription("DescriptionAttributeAxisDataView_Size"), TypeConverter(typeof(DoubleNanValueConverter)), ParenthesizePropertyNameAttribute(true) ] public double Size { get { // Axis scaleView is not supported in circular chrt areas if(this.axis != null && this.axis.ChartArea != null && this.axis.ChartArea.chartAreaIsCurcular) { return Double.NaN; } return _size; } set { // Axis scaleView is not supported in circular chrt areas if(this.axis != null && this.axis.ChartArea != null && this.axis.ChartArea.chartAreaIsCurcular) { return; } if(_size != value) { // Set size value _size = value; // Align scaleView in connected areas if(this.axis != null && this.axis.ChartArea != null && this.axis.Common != null && this.axis.Common.ChartPicture != null) { if(!this.axis.ChartArea.alignmentInProcess) { AreaAlignmentOrientations orientation = (this.axis.axisType == AxisName.X || this.axis.axisType== AxisName.X2) ? AreaAlignmentOrientations.Vertical : AreaAlignmentOrientations.Horizontal; this.axis.Common.ChartPicture.AlignChartAreasAxesView(this.axis.ChartArea, orientation); } } #if Microsoft_CONTROL // Reset current scrolling line size this._currentSmallScrollSize = double.NaN; #endif //Microsoft_CONTROL // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } /// /// Gets or sets the unit of measurement of the Size property. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(DateTimeIntervalType.Auto), SRDescription("DescriptionAttributeAxisDataView_SizeType"), ParenthesizePropertyNameAttribute(true) ] public DateTimeIntervalType SizeType { get { return _sizeType; } set { if(_sizeType != value) { // Set size type _sizeType = (value != DateTimeIntervalType.NotSet) ? value : DateTimeIntervalType.Auto; // Align scaleView in connected areas if(this.axis != null && this.axis.ChartArea != null && this.axis.Common != null && this.axis.Common.ChartPicture != null) { if(!this.axis.ChartArea.alignmentInProcess) { AreaAlignmentOrientations orientation = (this.axis.axisType == AxisName.X || this.axis.axisType== AxisName.X2) ? AreaAlignmentOrientations.Vertical : AreaAlignmentOrientations.Horizontal; this.axis.Common.ChartPicture.AlignChartAreasAxesView(this.axis.ChartArea, orientation); } } // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } /// /// Indicates if axis is zoomed-in. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(false), Browsable(false), SRDescription("DescriptionAttributeAxisDataView_IsZoomed"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SerializationVisibility(SerializationVisibility.Hidden), ] public bool IsZoomed { get { return ( !double.IsNaN(this.Size) && this.Size != 0.0 && !double.IsNaN(this.Position)); } } #if Microsoft_CONTROL /// /// Gets or sets the minimum size of the AxisScaleView. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(Double.NaN), SRDescription("DescriptionAttributeAxisDataView_MinSize"), TypeConverter(typeof(DoubleNanValueConverter)) ] public double MinSize { get { return _minSize; } set { _minSize = value; } } /// /// Gets or sets the unit of measurement of the MinSize property. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(DateTimeIntervalType.Auto), SRDescription("DescriptionAttributeAxisDataView_MinSizeType"), ] public DateTimeIntervalType MinSizeType { get { return _minSizeType; } set { _minSizeType = (value != DateTimeIntervalType.NotSet) ? value : DateTimeIntervalType.Auto; } } /// /// Gets or sets a flag which indicates whether the zooming user interface is enabled. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(true), SRDescription("DescriptionAttributeAxisDataView_Zoomable"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification="'Zoomable' is a commonly used term and generally well understood"), ] public bool Zoomable { get { return _zoomable; } set { _zoomable = value; } } /// /// Gets or sets the small scrolling size. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(Double.NaN), SRDescription("DescriptionAttributeAxisDataView_SmallScrollSize"), TypeConverter(typeof(AxisMinMaxAutoValueConverter)) ] public double SmallScrollSize { get { return _smallScrollSize; } set { if(_smallScrollSize != value) { // Set size value _smallScrollSize = value; // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } /// /// Gets or sets the unit of measurement for the SmallScrollMinSize property /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(DateTimeIntervalType.Auto), SRDescription("DescriptionAttributeAxisDataView_SmallScrollSizeType"), ] public DateTimeIntervalType SmallScrollSizeType { get { return _smallScrollSizeType; } set { if(_smallScrollSizeType != value) { // Set size type _smallScrollSizeType = (value != DateTimeIntervalType.NotSet) ? value : DateTimeIntervalType.Auto; // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } /// /// Gets or sets the minimum small scrolling size. /// Only used if the small scrolling size is not set. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(1.0), SRDescription("DescriptionAttributeAxisDataView_SmallScrollMinSize") ] public double SmallScrollMinSize { get { return _smallScrollMinSize; } set { if(_smallScrollMinSize != value) { // Set size value _smallScrollMinSize = value; _currentSmallScrollSize = double.NaN; // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } /// /// Gets or sets the unit of measurement for the SmallScrollMinSize property. /// [ SRCategory("CategoryAttributeAxisView"), Bindable(true), DefaultValue(DateTimeIntervalType.Auto), SRDescription("DescriptionAttributeAxisDataView_SmallScrollMinSizeType"), ] public DateTimeIntervalType SmallScrollMinSizeType { get { return _smallScrollMinSizeType; } set { if(_smallScrollMinSizeType != value) { // Set size type _smallScrollMinSizeType = (value != DateTimeIntervalType.NotSet) ? value : DateTimeIntervalType.Auto; _currentSmallScrollSize = double.NaN; // Validate chart if(!_ignoreValidation && axis != null) { axis.Invalidate(); } } } } #endif // Microsoft_CONTROL #endregion #region ScaleView position internal methods /// /// Call this method to get the minimum axis value of a data view. /// /// The minimum axis value for the data view. [Browsable(false)] [Utilities.SerializationVisibility(Utilities.SerializationVisibility.Hidden)] public double ViewMinimum { get { // If zooming is enabled if (!Double.IsNaN(this.Size)) { // If size set only use axis minimum for scaleView position if (Double.IsNaN(this.Position)) { this.Position = this.axis.Minimum; } // Check if scaleView position and size are set else { // Calculate and add axis side margin if (this.Position <= axis.minimum) { return this.Position; } else // Add a margin only if scaleView is inside data point scaleView { return this.Position - axis.marginView; } } } // Return axis scale minimum value if scaleView position is not set return axis.minimum; } } /// /// Maximum axis value of a data view. /// /// The maximum axis value for the data view. [Browsable(false)] [Utilities.SerializationVisibility(Utilities.SerializationVisibility.Hidden)] public double ViewMaximum { get { // If zooming is enabled if (!Double.IsNaN(this.Size)) { // If size set only use axis minimum for scaleView position if (Double.IsNaN(this.Position)) { this.Position = this.axis.Minimum; } // Check if scaleView position and size are set else { // Get axis interval double viewSize = ChartHelper.GetIntervalSize(this.Position, this.Size, this.SizeType); // Calculate and add axis side margin if (this.Position + viewSize >= axis.maximum) { return this.Position + viewSize; } else // Add a margin only if scaleView is inside data point scaleView { return this.Position + viewSize + axis.marginView; } } } // Return axis scale maximum value if scaleView position is not set return axis.maximum; } } #endregion #region Scrolling methods #if Microsoft_CONTROL /// /// Call this method to scroll to a specified position along an axis. /// /// Direction and size to scroll. public void Scroll(ScrollType scrollType) { this.Scroll(scrollType, false); } /// /// Scrolls axis data scaleView from current position. /// /// Direction and size to scroll. /// Fire scaleView position events from this method. internal void Scroll(ScrollType scrollType, bool fireChangeEvents) { // Adjust current position depending on the scroll type double newPosition = this._position; switch(scrollType) { case(ScrollType.SmallIncrement): newPosition += ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(this._position, this.GetScrollingLineSize(), this.GetScrollingLineSizeType()); break; case(ScrollType.SmallDecrement): newPosition -= ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(this._position, this.GetScrollingLineSize(), this.GetScrollingLineSizeType()); break; case(ScrollType.LargeIncrement): newPosition += ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(this._position, this.Size, this.SizeType); break; case(ScrollType.LargeDecrement): newPosition -= ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(this._position, this.Size, this.SizeType); break; case(ScrollType.First): if(!axis.IsReversed) { newPosition = (axis.minimum + axis.marginView); } else { newPosition = (axis.maximum - axis.marginView); } break; case(ScrollType.Last): { double viewSize = ChartHelper.GetIntervalSize(newPosition, this.Size, this.SizeType); if(!axis.IsReversed) { newPosition = (axis.maximum - axis.marginView - viewSize); } else { newPosition = (axis.minimum + axis.marginView + viewSize); } break; } } // Scroll to the new position this.Scroll(newPosition, fireChangeEvents); } /// /// Call this method to scroll to a specified position along an axis. /// /// New position. public void Scroll(double newPosition) { this.Scroll(newPosition, false); } /// /// Call this method to scroll to a specified position along an axis. /// /// New position. public void Scroll(DateTime newPosition) { this.Scroll(newPosition.ToOADate(), false); } /// /// Internal helper method for scrolling into specified position. /// /// New data scaleView position. /// Fire scaleView position events from this method. internal void Scroll(double newPosition, bool fireChangeEvents) { // Get current scaleView size double viewSize = ChartHelper.GetIntervalSize(newPosition, this.Size, this.SizeType); // Validate new scaleView position if(newPosition < (axis.minimum + axis.marginView)) { newPosition = (axis.minimum + axis.marginView); } else if(newPosition > (axis.maximum - axis.marginView - viewSize)) { newPosition = (axis.maximum - axis.marginView - viewSize); } // Fire scaleView position changing events ViewEventArgs arguments = new ViewEventArgs(this.axis, newPosition, this.Size, this.SizeType); if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanging(arguments); newPosition = arguments.NewPosition; } // Check if data scaleView position and size is different from current if(newPosition == this.Position) { return; } // Change scaleView position this.Position = newPosition; // Fire scaleView position changed events if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanged(arguments); } } #endif #endregion #region Zooming and ZoomResetting methods #if Microsoft_CONTROL /// /// Sets a new axis data view/position based on the start and end dates specified. /// /// New start position for the axis scale view. /// New size for the axis scale view. /// New unit of measurement of the size. /// Indicates whether the current size/position needs to be saved. public void Zoom(double viewPosition, double viewSize, DateTimeIntervalType viewSizeType, bool saveState) { this.Zoom(viewPosition, viewSize, viewSizeType, false, saveState); } /// /// Sets a new axis data view/position based on the specified start and end values. /// /// New start position for the axis scale view. /// New end position for the axis scale view. public void Zoom(double viewStart, double viewEnd) { this.Zoom(viewStart, viewEnd - viewStart, DateTimeIntervalType.Number, false, false); } /// /// Sets a new axis data view/position based on the start and end dates specified. /// /// New start position for the axis scale view. /// New size for the axis scale view. /// New unit of measurement of the size. public void Zoom(double viewPosition, double viewSize, DateTimeIntervalType viewSizeType) { this.Zoom(viewPosition, viewSize, viewSizeType, false, false); } /// /// Reset the specified number of zooming operations by restoring axis data view. /// /// Number of zoom operations to reset. Zero for all. public void ZoomReset(int numberOfViews) { this.LoadDataViewState(numberOfViews, false); } /// /// Reset one zooming operation by restoring axis data view. /// public void ZoomReset() { this.LoadDataViewState(1, false); } /// /// Reset several zooming operation by restoring data scaleView size/position. /// /// How many scaleView zoom operations to reset. Zero for all. /// Fire scaleView position events from this method. internal void ZoomReset(int numberOfViews, bool fireChangeEvents) { this.LoadDataViewState(numberOfViews, fireChangeEvents); } /// /// Internal helper zooming method. /// /// New data scaleView start posiion. /// New data scaleView size. /// New data scaleView size units type. /// Fire scaleView position events from this method. /// Indicates that current scaleView size/position must be save, so it can be restored later. /// True if zoom operation was made. internal bool Zoom( double viewPosition, double viewSize, DateTimeIntervalType viewSizeType, bool fireChangeEvents, bool saveState) { // Validate new scaleView position and size ValidateViewPositionSize(ref viewPosition, ref viewSize, ref viewSizeType); // Fire scaleView position/size changing events ViewEventArgs arguments = new ViewEventArgs(this.axis, viewPosition, viewSize, viewSizeType); if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanging(arguments); viewPosition = arguments.NewPosition; viewSize = arguments.NewSize; viewSizeType = arguments.NewSizeType; } // Check if data scaleView position and size is different from current if(viewPosition == this.Position && viewSize == this.Size && viewSizeType == this.SizeType) { return false; } // Save current data scaleView state, so it can be restored if(saveState) { SaveDataViewState(); } // Change scaleView position/size this._ignoreValidation = true; this.Position = viewPosition; this.Size = viewSize; this.SizeType = viewSizeType; this._ignoreValidation = false; // Reset current scrolling line size this._currentSmallScrollSize = double.NaN; // Invalidate chart axis.Invalidate(); // Fire scaleView position/size changed events if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanged(arguments); } return true; } #endif #endregion #region Data scaleView state saving/restoring methods #if Microsoft_CONTROL /// /// Saves current data scaleView position/size/sizetype, so /// it can be restored later. /// /// Number of time to reset zoom. Zero for all. /// Fire scaleView position events from this method. private void LoadDataViewState(int numberOfViews, bool fireChangeEvents) { // Check parameters if(numberOfViews < 0) { throw (new ArgumentOutOfRangeException("numberOfViews", SR.ExceptionScrollBarZoomResetsNumberInvalid)); } // Check if storage was created if(dataViewStates != null && dataViewStates.Count >= 3) { // Find starting index of restoring state int dataStartIndex = 0; if(numberOfViews > 0) { dataStartIndex = dataViewStates.Count - numberOfViews * 3; if(dataStartIndex < 0) { dataStartIndex = 0; } } // Fire scaleView position/size changing events ViewEventArgs arguments = new ViewEventArgs( this.axis, (double)dataViewStates[dataStartIndex], (double)dataViewStates[dataStartIndex + 1], (DateTimeIntervalType)dataViewStates[dataStartIndex + 2]); if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanging(arguments); } // Restore data this.Position = arguments.NewPosition; this.Size = arguments.NewSize; this.SizeType = arguments.NewSizeType; // Fire scaleView position/size changed events if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanged(arguments); } // Clear data int itemsToRemove = numberOfViews * 3; if (itemsToRemove > (dataViewStates.Count - dataStartIndex)) { itemsToRemove = dataViewStates.Count - dataStartIndex; } dataViewStates.RemoveRange(dataStartIndex, itemsToRemove); // clean up the history state when the numberOfViews == 0 (reset all by docs) if ( numberOfViews == 0 ) { dataViewStates.Clear(); } if (Double.IsNaN(this.Position) || Double.IsNaN(this.Size)) { this.Position = Double.NaN; this.Size = Double.NaN; } } // Nothing to restore - just disable the data scaleView else { // Fire scaleView position/size changing events ViewEventArgs arguments = new ViewEventArgs( this.axis, double.NaN, double.NaN, DateTimeIntervalType.Auto); if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanging(arguments); } // Restore data this.Position = arguments.NewPosition; this.Size = arguments.NewSize; this.SizeType = arguments.NewSizeType; // Fire scaleView position/size changed events if(fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanged(arguments); } } // clear cached chart areas and bitmap buffers GetChartObject().Refresh(); } /// /// Saves current data scaleView position/size/sizetype, so /// it can be restored later. /// private void SaveDataViewState() { // Create storage array if(dataViewStates == null) { dataViewStates = new ArrayList(); } // Save data scaleView state dataViewStates.Add(this.Position); dataViewStates.Add(this.Size); dataViewStates.Add(this.SizeType); } #endif #endregion #region Helper methods #if Microsoft_CONTROL /// /// Initialize internal scrolling line size variables for later use. /// This size is used in to scroll chart one line up or down. /// private void GetCurrentViewSmallScrollSize() { //************************************************************************** //** Check if current scrolling line size was not already calculated //************************************************************************** if(double.IsNaN(_currentSmallScrollSize)) { //************************************************************************** //** Calculate line size depending on the current scaleView size //************************************************************************** if(this.SizeType == DateTimeIntervalType.Auto || this.SizeType == DateTimeIntervalType.Number) { // Set line size type _currentSmallScrollSizeType = DateTimeIntervalType.Number; // Devide scaleView by 20 to find the scrolling line size double newSize = this.Size / 20.0; // Make sure that current line size is even with minimum value if(!double.IsNaN(this.SmallScrollMinSize) && this.SmallScrollMinSize != 0.0) { double rounder = (Math.Round(newSize / this.SmallScrollMinSize)); if(rounder < 0) { rounder = 1; } newSize = rounder * this.SmallScrollMinSize; } // Set new current line size this._currentSmallScrollSize = newSize; } else { // Calculate line size for date/time double viewEndPosition = this.Position + ChartHelper.GetIntervalSize(this.Position, this.Size, this.SizeType); _currentSmallScrollSize = axis.CalcInterval( this.Position, viewEndPosition, true, out _currentSmallScrollSizeType, ChartValueType.Auto); } //************************************************************************** //** Make sure calculated scroll line size is not smaller than the minimum //************************************************************************** if(!double.IsNaN(this.SmallScrollMinSize) && this.SmallScrollMinSize != 0.0) { double newLineSize = ChartHelper.GetIntervalSize(this.Position, _currentSmallScrollSize, _currentSmallScrollSizeType); double minLineSize = ChartHelper.GetIntervalSize(this.Position, this.SmallScrollMinSize, this.SmallScrollMinSizeType); if(newLineSize < minLineSize) { _currentSmallScrollSize = SmallScrollMinSize; _currentSmallScrollSizeType = SmallScrollMinSizeType; } } } } /// /// Returns the scroll line size. /// /// Scroll line size. internal double GetScrollingLineSize() { // Scroll line size/type is specificly set by user if(!double.IsNaN(this.SmallScrollSize)) { return this.SmallScrollSize; } // Calcualte scroll line size depending on the current scaleView size GetCurrentViewSmallScrollSize(); // Return line size return _currentSmallScrollSize; } /// /// Returns the scroll line size units type. /// /// Scroll line size units type. internal DateTimeIntervalType GetScrollingLineSizeType() { // Scroll line size/type is specificly set by user if(!double.IsNaN(this.SmallScrollSize)) { return this.SmallScrollSizeType; } // Calcualte scroll line size depending on the current scaleView size GetCurrentViewSmallScrollSize(); // Return line size units type return _currentSmallScrollSizeType; } /// /// Helper method, which validates the axis data scaleView position and size. /// Returns adjusted scaleView position and size. /// /// ScaleView position. /// ScaleView size. /// ScaleView size units type. private void ValidateViewPositionSize(ref double viewPosition, ref double viewSize, ref DateTimeIntervalType viewSizeType) { //**************************************************************** //** Check if new scaleView position is inside axis scale //** minimum/maximum without margin. //**************************************************************** if(viewPosition < (axis.minimum + axis.marginView)) { if(viewSizeType == DateTimeIntervalType.Auto || viewSizeType == DateTimeIntervalType.Number) { viewSize -= (axis.minimum + axis.marginView) - viewPosition; } viewPosition = (axis.minimum + axis.marginView); } else if(viewPosition > (axis.maximum - axis.marginView)) { if(viewSizeType == DateTimeIntervalType.Auto || viewSizeType == DateTimeIntervalType.Number) { viewSize -= viewPosition - (axis.maximum - axis.marginView); } viewPosition = (axis.maximum - axis.marginView); } //**************************************************************** //** Check if new scaleView size is not smaller than minimum size //** set by the user //**************************************************************** double newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); double minViewSize = ChartHelper.GetIntervalSize(viewPosition, 1, this.MinSizeType); if(!double.IsNaN(this.MinSize)) { minViewSize = ChartHelper.GetIntervalSize(viewPosition, this.MinSize, this.MinSizeType); if(newViewSize < minViewSize) { viewSize = (double.IsNaN(this.MinSize)) ? 1 : this.MinSize; viewSizeType = this.MinSizeType; newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); } } //**************************************************************** //** Check if new scaleView size is smaller than (0.000000001) //**************************************************************** if(newViewSize < 0.000000001) { viewSize = 0.000000001; viewSizeType = DateTimeIntervalType.Number; newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); } //**************************************************************** //** Check if new scaleView end position (position + size) is inside //** axis scale minimum/maximum without margin. //**************************************************************** while( (viewPosition + newViewSize) > (axis.maximum - axis.marginView) ) { double currentSize = viewSize; DateTimeIntervalType currentSizeType = viewSizeType; // Try to reduce the scaleView size if(newViewSize > minViewSize) { // Try to adjust the scaleView size if(viewSize > 1) { --viewSize; } else if(viewSizeType == DateTimeIntervalType.Years) { viewSize = 11; viewSizeType = DateTimeIntervalType.Months; } else if(viewSizeType == DateTimeIntervalType.Months) { viewSize = 4; viewSizeType = DateTimeIntervalType.Weeks; } else if(viewSizeType == DateTimeIntervalType.Weeks) { viewSize = 6; viewSizeType = DateTimeIntervalType.Days; } else if(viewSizeType == DateTimeIntervalType.Days) { viewSize = 23; viewSizeType = DateTimeIntervalType.Hours; } else if(viewSizeType == DateTimeIntervalType.Hours) { viewSize = 59; viewSizeType = DateTimeIntervalType.Minutes; } else if(viewSizeType == DateTimeIntervalType.Minutes) { viewSize = 59; viewSizeType = DateTimeIntervalType.Seconds; } else if(viewSizeType == DateTimeIntervalType.Seconds) { viewSize = 999; viewSizeType = DateTimeIntervalType.Milliseconds; } else { viewPosition = (axis.maximum - axis.marginView) - minViewSize; break; } // Double check that scaleView size is not smaller than min size newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); if(newViewSize < minViewSize) { // Can't adjust size no more (restore prev. value) viewSize = currentSize; viewSizeType = currentSizeType; // Adjust the start position viewPosition = (axis.maximum - axis.marginView) - minViewSize; break; } } else { // Adjust the start position viewPosition = (axis.maximum - axis.marginView) - newViewSize; break; } } } /// /// Helper function which returns a reference to the chart object. /// /// Chart object reference. internal Chart GetChartObject() { if(this.axis != null && this.axis.Common!=null) { return this.axis.Common.Chart; } return null; } #endif //Microsoft_CONTROL #endregion } #if Microsoft_CONTROL /// /// This class is used as a parameter object in the AxisViewChanged and AxisViewChanging events of the root Chart object. /// public class ViewEventArgs : EventArgs { #region Private fields // Private fields for properties values storage private Axis _axis = null; private double _newPosition = double.NaN; private double _newSize = double.NaN; private DateTimeIntervalType _newSizeType = DateTimeIntervalType.Auto; #endregion #region Constructors /// /// ViewEventArgs constructor. /// /// Axis of the scale view. /// New scale view start position. public ViewEventArgs(Axis axis, double newPosition) { this._axis = axis; this._newPosition = newPosition; } /// /// ViewEventArgs constructor. /// /// Axis of the scale view. /// New scale view start position. /// New scale view size. /// New unit of measurement of the size. public ViewEventArgs(Axis axis, double newPosition, double newSize, DateTimeIntervalType newSizeType) { this._axis = axis; this._newPosition = newPosition; this._newSize = newSize; this._newSizeType = newSizeType; } #endregion #region Properties /// /// Axis of the event. /// [ SRDescription("DescriptionAttributeAxis"), ] public Axis Axis { get { return _axis; } } /// /// ChartArea of the event. /// [ SRDescription("DescriptionAttributeChartArea"), ] public ChartArea ChartArea { get { return _axis.ChartArea; } } /// /// New scale view start position. /// [ SRDescription("DescriptionAttributeViewEventArgs_NewPosition"), ] public double NewPosition { get { return _newPosition; } set { _newPosition = value; } } /// /// New scale view size. /// [ SRDescription("DescriptionAttributeViewEventArgs_NewSize"), ] public double NewSize { get { return _newSize; } set { _newSize = value; } } /// /// New unit of measurement of the scale view. /// [ SRDescription("DescriptionAttributeViewEventArgs_NewSizeType"), ] public DateTimeIntervalType NewSizeType { get { return _newSizeType; } set { _newSizeType = (value != DateTimeIntervalType.NotSet) ? value : DateTimeIntervalType.Auto; } } #endregion } #endif // #if Microsoft_CONTROL } #if Microsoft_CONTROL namespace System.Windows.Forms.DataVisualization.Charting #else namespace System.Web.UI.DataVisualization.Charting #endif { /// /// Designer converter class /// Converts Double.NaN values to/from "Not set". /// internal class DoubleNanValueConverter : DoubleConverter { #region Converter methods /// /// Standard values supported. This method always return true. /// /// Descriptor context. /// True. public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } /// /// Standard values are not exclusive. This method always return false. /// /// Descriptor context. /// False. public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { return false; } /// /// Get in the collection of standard values. /// /// Descriptor context. public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { ArrayList values = new ArrayList(); values.Add(Double.NaN); return new StandardValuesCollection(values); } /// /// Convert double.NaN to string "Not set" /// /// Descriptor context. /// Culture information. /// Value to convert. /// Conversion destination type. /// Converted object. public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { double doubleValue = (double)value; if (destinationType == typeof(string)) { if(Double.IsNaN(doubleValue)) { return Constants.NotSetValue; } } // Call base class return base.ConvertTo(context, culture, value, destinationType); } /// /// Convert minimum or maximum values from string. /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // If converting from string value string crossingValue = value as string; if (crossingValue != null) { if (String.Compare(crossingValue, Constants.NotSetValue, StringComparison.OrdinalIgnoreCase) == 0) { return Double.NaN; } } // Call base converter return base.ConvertFrom(context, culture, value); } #endregion } /// /// Designer converter class /// Converts Double.NaN values to/from "Not set". /// Converts value to/from date strings. /// internal class DoubleDateNanValueConverter : DoubleConverter { #region Converter methods /// /// Standard values supported - return true /// /// Descriptor context. /// Standard values supported. public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } /// /// Standard values are not exclusive - return false /// /// Descriptor context. /// Non exclusive standard values. public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { return false; } /// /// Fill in the list of predefined values. /// /// Descriptor context. public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { ArrayList values = new ArrayList(); values.Add(Double.NaN); return new StandardValuesCollection(values); } /// /// Convert values to string if step type is set to one of the DateTime type /// /// Descriptor context. /// Culture information. /// Value to convert. /// Conversion destination type. /// Converted object. public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { // Check for NaN if (destinationType == typeof(string)) { if(Double.IsNaN((double)value)) { return Constants.NotSetValue; } } if (context != null && context.Instance != null) { // Get access to the Axis object Axis axis = null; if(context.Instance is AxisScaleView) { axis = ((AxisScaleView)context.Instance).axis; } #if Microsoft_CONTROL else if(context.Instance is Cursor) { axis = ((Cursor)context.Instance).GetAxis(); } #endif // Microsoft_CONTROL if (axis != null && destinationType == typeof(string)) { string strValue = ConvertDateTimeToString( (double)value, axis.GetAxisValuesType(), axis.InternalIntervalType); if (strValue != null) return strValue; } } return base.ConvertTo(context, culture, value, destinationType); } public static string ConvertDateTimeToString( double dtValue, ChartValueType axisValuesType, DateTimeIntervalType dtIntervalType) { string strValue = null; // Use axis values types if interval is automatic if (dtIntervalType == DateTimeIntervalType.Auto) { if (axisValuesType == ChartValueType.DateTime || axisValuesType == ChartValueType.Time || axisValuesType == ChartValueType.Date || axisValuesType == ChartValueType.DateTimeOffset) { strValue = DateTime.FromOADate(dtValue).ToString("g", System.Globalization.CultureInfo.CurrentCulture); } } else { if (dtIntervalType != DateTimeIntervalType.Number) { // Covert value to date/time if (dtIntervalType < DateTimeIntervalType.Hours) { strValue = DateTime.FromOADate(dtValue).ToShortDateString(); } else { strValue = DateTime.FromOADate(dtValue).ToString("g", System.Globalization.CultureInfo.CurrentCulture); } } } if (axisValuesType == ChartValueType.DateTimeOffset && strValue != null) strValue += " +0"; return strValue; } /// /// Convert Min and Max values from string if step type is set to one of the DateTime type /// /// Descriptor context. /// Culture information. /// Value to convert from. /// Converted object. public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { object result = null; bool convertFromDate = false; // If converting from string value string crossingValue = value as string; if (crossingValue != null) { if (String.Compare(crossingValue, Constants.NotSetValue, StringComparison.OrdinalIgnoreCase) == 0) { return Double.NaN; } } // If context interface provided check if we are dealing with DateTime values if (context != null && context.Instance != null && context.Instance is Axis) { // Get axis object Axis axis = null; if(context.Instance is AxisScaleView) { axis = ((AxisScaleView)context.Instance).axis; } #if Microsoft_CONTROL else if(context.Instance is Cursor) { axis = ((Cursor)context.Instance).GetAxis(); } #endif // Microsoft_CONTROL if (axis != null && crossingValue != null) { if(axis.InternalIntervalType == DateTimeIntervalType.Auto) { if(axis.GetAxisValuesType() == ChartValueType.DateTime || axis.GetAxisValuesType() == ChartValueType.Date || axis.GetAxisValuesType() == ChartValueType.Time || axis.GetAxisValuesType() == ChartValueType.DateTimeOffset) { convertFromDate = true; } } else { if(axis.InternalIntervalType != DateTimeIntervalType.Number) { convertFromDate = true; } } } } // Try to convert from double string try { result = base.ConvertFrom(context, culture, value); } catch (ArgumentException) { result = null; } catch (NotSupportedException) { result = null; } // Try to convert from date/time string if (crossingValue != null && (convertFromDate || result == null)) { DateTime valueAsDate; bool parseSucceed = DateTime.TryParse(crossingValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out valueAsDate); if (parseSucceed) { return valueAsDate.ToOADate(); } } // Call base converter return base.ConvertFrom(context, culture, value); } #endregion } }