/// <summary> /// Get or create a linear numeric axis in the correct dimension. /// </summary> /// <param name="orientation">Dimension of the axis to create.</param> /// <param name="oldAxis"> /// Old value of the axis in this dimension. /// </param> /// <returns>New value of the axis in this dimension.</returns> private IRangeAxis GetAxis(AxisOrientation orientation, IRangeAxis oldAxis) { // Check the existing axes for a potential axis IRangeAxis axis = (from IRangeAxis a in SeriesHost.Axes.OfType<IRangeAxis>() where a.Orientation == orientation select a) .FirstOrDefault(); if (axis == null) { // Create a new axis if not found axis = new LinearAxis { Orientation = orientation, }; } if (oldAxis != axis) { // Unregister any existing axis if (oldAxis != null) { oldAxis.RegisteredListeners.Remove(this); } // Register the new axis if (!axis.RegisteredListeners.Contains(this)) { axis.RegisteredListeners.Add(this); } } return axis; }
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; }
/// <summary> /// Get a point in screen coordinates. /// </summary> /// <param name="x">Independent value.</param> /// <param name="function">The function.</param> /// <param name="independent">The independent axis.</param> /// <param name="dependent">The dependent axis.</param> /// <returns>The point in screen coordinates.</returns> private Point GetPoint(double x, Func<double, double> function, IRangeAxis independent, IRangeAxis dependent) { // Get the dependent value double y = double.NaN; try { y = function(x); } catch (DivideByZeroException) { } // Map the actual values into coordinate values return new Point( independent.GetPlotAreaCoordinate(x).Value, Math.Min( Math.Max( ActualHeight - dependent.GetPlotAreaCoordinate(y).Value, -1), ActualHeight + 1)); }
/// <summary> /// Returns the range of values for the axis to include. /// </summary> /// <param name="rangeAxis">The axis corresponding to the range.</param> /// <returns>The range for the axis to include.</returns> Range <IComparable> IRangeAxisInformationProvider.GetDesiredRange(IRangeAxis rangeAxis) { return(OverrideRequestedAxisRange(rangeAxis, (this as IRangeAxisInformationProvider).GetActualRange(rangeAxis))); }
/// <summary> /// DependentRangeAxisProperty property changed handler. /// </summary> /// <param name="newValue">New value.</param> private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; }
/// <summary> /// Get a point in screen coordinates. /// </summary> /// <param name="x">Independent value.</param> /// <param name="function">The function.</param> /// <param name="independent">The independent axis.</param> /// <param name="dependent">The dependent axis.</param> /// <returns>The point in screen coordinates.</returns> private Point GetPoint(double x, Func <double, double> function, IRangeAxis independent, IRangeAxis dependent) { // Get the dependent value double y = double.NaN; try { y = function(x); } catch (DivideByZeroException) { } // Map the actual values into coordinate values return(new Point( independent.GetPlotAreaCoordinate(x).Value, Math.Min( Math.Max( ActualHeight - dependent.GetPlotAreaCoordinate(y).Value, -1), ActualHeight + 1))); }
/// <summary> /// Acquires a range axis. /// </summary> /// <param name="assignedAxis">The axis assigned to the exposed /// axis property.</param> /// <param name="firstDataPoint">A data point used to determine /// where a date or numeric axis is required.</param> /// <param name="orientation">The desired orientation of the axis. /// </param> /// <param name="axisInitializationAction">A function that initializes /// a newly created axis.</param> /// <param name="axisPropertyAccessor">A function that returns the /// current value of the property used to store the axis.</param> /// <param name="axisPropertySetter">A function that accepts an axis /// value and assigns it to the property intended to store a reference /// to it.</param> /// <param name="dataPointAxisValueGetter">A function that accepts a /// Control and returns the value that will be plot on the axis. /// </param> protected void GetRangeAxis( IAxis assignedAxis, DataPoint firstDataPoint, AxisOrientation orientation, Func <IRangeAxis> axisInitializationAction, Func <IRangeAxis> axisPropertyAccessor, Action <IRangeAxis> axisPropertySetter, Func <DataPoint, object> dataPointAxisValueGetter) { if (assignedAxis != null) { if (assignedAxis.Orientation == orientation) { IRangeAxis assignedRangeAxis = assignedAxis as IRangeAxis; if (assignedRangeAxis != null) { object value = dataPointAxisValueGetter(firstDataPoint); if (assignedRangeAxis.CanPlot(value)) { axisPropertySetter(assignedRangeAxis); if (!assignedAxis.IsObjectRegistered(this)) { assignedRangeAxis.Invalidated += OnAxisInvalidated; this.SeriesHost.RegisterWithAxis(this, (IAxis)assignedRangeAxis); } return; } else { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.DataPointSeriesWithAxes_AxisCannotPlotValue, value)); } } else { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_ExpectedAxesOfTypeICategoryAxis); } } else { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.DataPointSeriesWithAxes_AxisIsIncorrectOrientation, orientation)); } } // If current axis is not suitable... if (axisPropertyAccessor() == null || !axisPropertyAccessor().CanPlot(dataPointAxisValueGetter(firstDataPoint))) { // Attempt to find suitable axis IRangeAxis axis = SeriesHost.Axes .Cast <IAxis>() .Where(currentAxis => currentAxis.Orientation == orientation && currentAxis.CanPlot(dataPointAxisValueGetter(firstDataPoint)) && currentAxis.CanRegister(this)) .OfType <IRangeAxis>() .FirstOrDefault(); if (axis == null) { axis = axisInitializationAction(); axis.Orientation = orientation; } IAxis baseAxis = (IAxis)axis; // Unregister with current axis if it has one. if (axisPropertyAccessor() != null) { axisPropertyAccessor().Invalidated -= OnAxisInvalidated; SeriesHost.UnregisterWithAxis(this, baseAxis); } axisPropertySetter(axis); if (!axis.IsObjectRegistered(this)) { axis.Invalidated += OnAxisInvalidated; SeriesHost.RegisterWithAxis(this, baseAxis); } } }
/// <summary> /// DependentRangeAxisProperty property changed handler. /// </summary> /// <param name="newValue">New value.</param> private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { InternalDependentAxis = newValue; }
/// <summary> /// Refreshes data from data source and renders the series. /// </summary> private void Refresh() { if (SeriesHost == null || ActualWidth == 0) { return; } // Ensure we have a function to plot Func <double, double> function = Function; if (function == null) { return; } // Ensure we have axes IRangeAxis independent = GetAxis(AxisOrientation.X, IndependentAxis); IndependentAxis = independent; IRangeAxis dependent = GetAxis(AxisOrientation.Y, DependentAxis); DependentAxis = dependent; if (!independent.Range.HasData) { return; } // Create a geometry that matches the function to plot PathGeometry path = new PathGeometry(); PathFigure figure = new PathFigure(); // Get the range over which we will double start = (double)independent.Range.Minimum; double end = (double)independent.Range.Maximum; // Adjust the line at each pixel double delta = (end - start) / ActualWidth; // We'll only add a new line segment when the slope is changing // between points Point last = GetPoint(start, function, independent, dependent); figure.StartPoint = last; double slope = double.NaN; for (double x = start + delta; x <= end; x += delta) { Point next = GetPoint(x, function, independent, dependent); double newSlope = (next.Y - last.Y) / (next.X - last.X); if (slope != newSlope) { figure.Segments.Add(new LineSegment { Point = last }); } slope = newSlope; last = next; } figure.Segments.Add(new LineSegment { Point = last }); path.Figures.Add(figure); Geometry = path; }
/// <summary> /// Get or create a linear numeric axis in the correct dimension. /// </summary> /// <param name="orientation">Dimension of the axis to create.</param> /// <param name="oldAxis"> /// Old value of the axis in this dimension. /// </param> /// <returns>New value of the axis in this dimension.</returns> private IRangeAxis GetAxis(AxisOrientation orientation, IRangeAxis oldAxis) { // Check the existing axes for a potential axis IRangeAxis axis = (from IRangeAxis a in SeriesHost.Axes.OfType<IRangeAxis>() where a.Orientation == orientation select a) .FirstOrDefault(); if (axis == null) { // Create a new axis if not found axis = new LinearAxis { Orientation = orientation, }; } if (oldAxis != axis) { // Unregister any existing axis if (oldAxis != null) { SeriesHost.UnregisterWithAxis(this, oldAxis); oldAxis.Invalidated -= OnAxisInvalidated; } // Register the new axis SeriesHost.RegisterWithAxis(this, axis); axis.Invalidated += OnAxisInvalidated; } return axis; }
/// <summary> /// If data is found returns the minimum and maximum dependent numeric /// values. /// </summary> /// <param name="rangeAxis">IRangeAxis that needs the data.</param> /// <returns> /// The range of values or empty if no data is present. /// </returns> public Range<IComparable> GetDesiredRange(IRangeAxis rangeAxis) { // Use an empty range so we only plot over the area used by other // axes. return new Range<IComparable>(); }
/// <summary> /// Ensures that if the desired range is below or above zero and the /// data does not cross zero then the minimum of the desired range is /// adjusted to zero. /// </summary> /// <param name="rangeAxis">The axis to request the range for.</param> /// <param name="range">The range of the data.</param> /// <returns>The desired data range.</returns> protected override Range<IComparable> OverrideRequestedAxisRange(IRangeAxis rangeAxis, Range<IComparable> range) { Range<IComparable> desiredRange = base.OverrideRequestedAxisRange(rangeAxis, range); if (range.HasData && desiredRange.HasData) { Range<double> doubleRange = range.ToDoubleRange(); Range<double> doubleDesiredRange = desiredRange.ToDoubleRange(); if (rangeAxis == InternalActualDependentAxis) { double minimum = doubleDesiredRange.Minimum; double maximum = doubleDesiredRange.Maximum; if (doubleRange.Minimum >= 0.0 && minimum < 0.0) { minimum = 0.0; } if (doubleRange.Maximum <= 0.0 && maximum >= 0.0) { maximum = 0.0; } return new Range<IComparable>(minimum, maximum); } } return desiredRange; }
public void UpdateBody(IRangeAxis rangeAxis) { if (Body == null) return; double highPointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(High)).Value; double lowPointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(Low)).Value; double openPointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(Open)).Value; double closePointY = rangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(Close)).Value; Thickness margin; if (openPointY > closePointY) { margin = new Thickness(0, highPointY - openPointY, 0, closePointY - lowPointY); } else { margin = new Thickness(0, highPointY - closePointY, 0, openPointY - lowPointY); } Body.Margin = margin; }
/// <summary> /// Overrides the requested axis range to include the width and height /// necessary for the bubbles. /// </summary> /// <param name="rangeAxis">The range axis.</param> /// <param name="range">The data range.</param> /// <returns>The requested axis range.</returns> protected override Range<IComparable> OverrideRequestedAxisRange(IRangeAxis rangeAxis, Range<IComparable> range) { if (ActiveDataPoints.Any()) { if (rangeAxis == ActualIndependentRangeAxis) { double smallestXCoordinate = ActiveDataPoints .Select(dataPoint => ActualIndependentRangeAxis.GetPlotAreaCoordinate((IComparable)dataPoint.IndependentValue) - dataPoint.ActualWidth) .Min(); double largestXCoordinate = ActiveDataPoints .Select(dataPoint => ActualIndependentRangeAxis.GetPlotAreaCoordinate((IComparable)dataPoint.IndependentValue) + dataPoint.ActualWidth) .Max(); return new Range<IComparable>( ActualIndependentRangeAxis.GetPlotAreaCoordinateValueRange(smallestXCoordinate).Minimum, ActualIndependentRangeAxis.GetPlotAreaCoordinateValueRange(largestXCoordinate).Maximum); } else if (rangeAxis == ActualDependentRangeAxis) { double smallestYCoordinate = ActiveDataPoints .Select(dataPoint => ActualDependentRangeAxis.GetPlotAreaCoordinate((IComparable)dataPoint.DependentValue) - dataPoint.ActualHeight) .Min(); double largestYCoordinate = ActiveDataPoints .Select(dataPoint => ActualDependentRangeAxis.GetPlotAreaCoordinate((IComparable)dataPoint.DependentValue) + dataPoint.ActualHeight) .Max(); return new Range<IComparable>( ActualDependentRangeAxis.GetPlotAreaCoordinateValueRange(smallestYCoordinate).Minimum, ActualDependentRangeAxis.GetPlotAreaCoordinateValueRange(largestYCoordinate).Maximum); } } return new Range<IComparable>(); }