/// <summary> /// Raises the <see cref="ChartElement.Updated"/> event. /// </summary> /// <remarks> /// <strong>Notes to Inheritors:</strong> When overriding <see cref="OnUpdate"/> in a /// derived class, be sure to call the base class's <see cref="OnUpdate"/> method so that /// the base class <see cref="Chart"/> can update the data source if required. /// </remarks> protected override void OnUpdate() { base.OnUpdate(); // Updates the data source, if required. // Cleanup _lineRenderer.Clear(); _areaRenderer.Clear(); Debug.Assert(Canvas.Children.Count == 0, "Canvas should be cleared in base class."); Canvas.Children.Add(_areaPath); Canvas.Children.Add(_linePath); if (Data != null && Data.Count != 0) { // Clip filled area and lines to the chart area. Rect chartArea = ChartPanel.GetChartAreaBounds(XAxis, YAxis); // Allow line to draw on chart axes. Rect lineClipRect = new Rect(chartArea.Left - 2, chartArea.Top - 2, chartArea.Width + 4, chartArea.Height + 4); LineClipGeometry = new RectangleGeometry { Rect = lineClipRect }; // Keep area inside the chart area. Rect areaClipRect = chartArea; AreaClipGeometry = new RectangleGeometry { Rect = areaClipRect }; FindVisibleDataPoints(); CachePositions(); OnUpdateLines(_startIndex, _endIndexExclusive, _xPositions, _basePositions, _yPositions); UpdateMarkers(); #else switch (RenderMode) { case ChartRenderMode.Quality: // Update lines and markers immediately. OnUpdateLines(_startIndex, _endIndexExclusive, _xPositions, _basePositions, _yPositions); UpdateMarkers(); break; case ChartRenderMode.Performance: // Immediately update lines. OnUpdateLines(_startIndex, _endIndexExclusive, _xPositions, _basePositions, _yPositions); // Temporarily disable bitmap cache and anti-aliasing. // Update markers when application is idle. if (!_updatePending) { _updatePending = true; ClearCacheMode(); ClearEdgeMode(); ChartHelper.Defer(Dispatcher, () => { if (_updatePending) { _updatePending = false; UpdateMarkers(); RestoreCacheMode(); RestoreEdgeMode(); } }); } break; case ChartRenderMode.DoNotRender: // Do nothing. break; } } }
/// <inheritdoc/> protected override void OnUpdate() { base.OnUpdate(); Debug.Assert(Canvas.Children.Count == 0, "Canvas should be cleared in base class."); if (Data == null || Data.Count == 0 || DataPointTemplate == null) { // A relevant property is not set. return; } // Fetch dependency properties. (Accessing dependency properties is expensive.) Axis xAxis = XAxis; Axis yAxis = YAxis; AxisScale xScale = xAxis.Scale; AxisScale yScale = yAxis.Scale; double centerX = CenterX; double centerY = CenterY; double hole = Hole; double outerRadius = Radius; Thickness padding = Padding; if (hole < 0 || hole > 1) throw new ChartException("Pie chart has invalid hole size. The hole size is relative to the outer radius and must be in the range [0, 1]."); if (outerRadius < 0) throw new ChartException("Pie chart has invalid radius. The radius must be a equal to or greater than 0."); // Determine available space. double left = xAxis.GetPosition(xScale.Min); double right = xAxis.GetPosition(xScale.Max); if (left > right) { // Scale is reversed. ChartHelper.Swap(ref left, ref right); } double top = yAxis.GetPosition(yScale.Max); double bottom = yAxis.GetPosition(yScale.Min); if (top > bottom) { // Scale is reversed. ChartHelper.Swap(ref top, ref bottom); } // Apply padding. left += padding.Left; right -= padding.Right; top += padding.Top; bottom -= padding.Bottom; if (Numeric.IsNaN(centerX)) { // Center pie chart horizontally. centerX = (left + right) / 2; } else { centerX = xAxis.OriginX + centerX * xAxis.Length; } if (Numeric.IsNaN(centerY)) { // Center pie chart vertically. centerY = (top + bottom) / 2; } else { centerY = yAxis.OriginY - (1.0 - centerY) * yAxis.Length; } if (Numeric.IsNaN(outerRadius)) { // Fit pie chart into available space. double radiusLeft = centerX - left; double radiusRight = right - centerX; double radiusTop = centerY - top; double radiusBottom = bottom - centerY; outerRadius = radiusLeft; if (outerRadius > radiusRight) outerRadius = radiusRight; if (outerRadius > radiusTop) outerRadius = radiusTop; if (outerRadius > radiusBottom) outerRadius = radiusBottom; } if (Numeric.IsNaN(hole)) hole = 0; double innerRadius = hole * outerRadius; ActualHoleRadius = innerRadius; // Draw pie chart inside chart panel. DrawPieChart(Canvas, centerX, centerY, innerRadius, outerRadius, BrushSelector, false); // Update legend symbols inside legends. // Use a fixed innerRadius innerRadius = (Hole > 0) ? 2 : 0; foreach (var legendSymbol in _legendSymbols) DrawPieChart(legendSymbol, 8, 8, innerRadius, 8, BrushSelector, true); }
private void GetBrushes(double paletteIndex, out Brush strokeBrush, out Brush fillBrush) { Axis xAxis = XAxis; Axis yAxis = YAxis; strokeBrush = null; fillBrush = null; if (FillMode == BarFillMode.Solid) { // Solid: // Get a solid color brush for fill and stroke. if (FillPalette != null && FillPalette.Count > 0) fillBrush = new SolidColorBrush(FillPalette.GetColor(paletteIndex)); if (StrokePalette != null && StrokePalette.Count > 0) strokeBrush = new SolidColorBrush(StrokePalette.GetColor(paletteIndex)); } else { // Gradient: // We draw from 0 upwards or from a negative value upwards to 0. double lower = Math.Min(0, paletteIndex); double higher = Math.Max(0, paletteIndex); // Define end point for gradient direction. Point startPoint; Point endPoint; if (PaletteIndex == PaletteIndex.XValue) { // Horizontal startPoint = new Point(0, 0); endPoint = new Point(1, 0); if (xAxis != null && xAxis.Scale != null && xAxis.Scale.Reversed) ChartHelper.Swap(ref startPoint, ref endPoint); } else { // Vertical startPoint = new Point(0, 1); endPoint = new Point(0, 0); if (yAxis != null && yAxis.Scale != null && yAxis.Scale.Reversed) ChartHelper.Swap(ref startPoint, ref endPoint); } // Get brush for fill and stroke. if (FillPalette != null && FillPalette.Count > 0) { fillBrush = new LinearGradientBrush { GradientStops = FillPalette.GetGradient(lower, higher), StartPoint = startPoint, EndPoint = endPoint, ColorInterpolationMode = FillPalette.ColorInterpolationMode }; } if (StrokePalette != null && StrokePalette.Count > 0) { strokeBrush = new LinearGradientBrush { GradientStops = StrokePalette.GetGradient(lower, higher), StartPoint = startPoint, EndPoint = endPoint, ColorInterpolationMode = StrokePalette.ColorInterpolationMode }; } } }
protected override Size ArrangeOverride(Size finalSize) { // Ensure that all axes are up to date. foreach (Axis axis in _monitoredAxes) axis.Update(); UIElementCollection children = Children; for (int i = 0; i < children.Count; ++i) { UIElement child = children[i]; if (child == null) continue; // Get the child or one of its descendants that needs to be positioned. DependencyObject elementThatRequiresArrange = GetElementThatRequiresArrange(child); if (elementThatRequiresArrange == null) { // This child ignores the attached properties (X1, Y1, X2, Y2). Rect childBounds = new Rect(new Point(), finalSize); // Arrange element, child.Arrange(childBounds); // Clip to chart area. if (GetClipToChartArea(child)) ClipElementToChartArea(child, childBounds); } else { // Determine alignment. HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left; VerticalAlignment verticalAlignment = VerticalAlignment.Top; if (elementThatRequiresArrange is FrameworkElement) { FrameworkElement frameworkElement = (FrameworkElement)elementThatRequiresArrange; horizontalAlignment = frameworkElement.HorizontalAlignment; verticalAlignment = frameworkElement.VerticalAlignment; } // Get associated axes. Axis xAxis = GetXAxis(elementThatRequiresArrange); Axis yAxis = GetYAxis(elementThatRequiresArrange); // Check whether we have to position the element relative to a point (X1, Y1) // or inside a region (X1, Y1) - (X2, Y2). // Convert positions given in data values to pixel positions. double x1 = xAxis.GetPosition(GetX1(elementThatRequiresArrange)); double y1 = yAxis.GetPosition(GetY1(elementThatRequiresArrange)); double x2 = xAxis.GetPosition(GetX2(elementThatRequiresArrange)); double y2 = yAxis.GetPosition(GetY2(elementThatRequiresArrange)); bool isX1Valid = Numeric.IsFinite(x1); bool isY1Valid = Numeric.IsFinite(y1); bool isX2Valid = Numeric.IsFinite(x2); bool isY2Valid = Numeric.IsFinite(y2); double availableWidth = Double.PositiveInfinity; double availableHeight = Double.PositiveInfinity; if (!isX1Valid && !isX2Valid) { // No coordinates set. Use 0 and 'left' alignment. x1 = 0; x2 = Double.NaN; horizontalAlignment = HorizontalAlignment.Left; } else if (!isX1Valid) { // Only X2 set. Use X2 as position. x1 = x2; x2 = Double.NaN; } else if (isX2Valid) { // X1 and X2 are set. int result = Numeric.Compare(x1, x2); if (result < 0) { // X1 < X2: Horizontal region. availableWidth = x2 - x1; } else if (result == 0) { // X1 == X2: Use only X1. x2 = Double.NaN; } else { // X2 > X1: Horizontal region, but swapped. ChartHelper.Swap(ref x1, ref x2); availableWidth = x2 - x1; } } if (!isY1Valid && !isY2Valid) { // No coordinates set. Use 0 and 'top' alignment. y1 = 0; y2 = Double.NaN; verticalAlignment = VerticalAlignment.Top; } else if (!isY1Valid) { // Only Y2 set. Use Y2 as position. y1 = y2; y2 = Double.NaN; } else if (isY2Valid) { // Y1 and Y2 are set. int result = Numeric.Compare(y1, y2); if (result < 0) { // Y1 < Y2: Horizontal region. availableHeight = y2 - y1; } else if (result == 0) { // Y1 == Y2: Use only Y1. y2 = Double.NaN; } else { // Y2 > Y1: Horizontal region, but swapped. ChartHelper.Swap(ref y1, ref y2); availableHeight = y2 - y1; } } // Get size of child. double elementWidth = child.DesiredSize.Width; double elementHeight = child.DesiredSize.Height; if (elementWidth == 0.0 && elementHeight == 0.0 && child is FrameworkElement) { // Fix for Silverlight. FrameworkElement frameworkElement = (FrameworkElement)child; elementWidth = frameworkElement.ActualWidth; elementHeight = frameworkElement.ActualHeight; } // Compute bounds of the child element. Rect childBounds = new Rect(); // Position child horizontally. if (Numeric.IsNaN(x2)) { // Position child relative to point. switch (horizontalAlignment) { case HorizontalAlignment.Left: case HorizontalAlignment.Stretch: childBounds.X = x1; childBounds.Width = elementWidth; break; case HorizontalAlignment.Center: childBounds.X = x1 - elementWidth / 2.0; childBounds.Width = elementWidth; break; case HorizontalAlignment.Right: childBounds.X = x1 - elementWidth; childBounds.Width = elementWidth; break; } } else { // Position child inside horizontal region. switch (horizontalAlignment) { case HorizontalAlignment.Left: childBounds.X = x1; childBounds.Width = elementWidth; break; case HorizontalAlignment.Center: childBounds.X = x1 + availableWidth / 2.0 - elementWidth / 2.0; childBounds.Width = elementWidth; break; case HorizontalAlignment.Right: childBounds.X = x2 - elementWidth; childBounds.Width = elementWidth; break; case HorizontalAlignment.Stretch: childBounds.X = x1; childBounds.Width = availableWidth; break; } } // Position child vertically. if (Numeric.IsNaN(y2)) { // Position child relative to point. switch (verticalAlignment) { case VerticalAlignment.Top: case VerticalAlignment.Stretch: childBounds.Y = y1; childBounds.Height = elementHeight; break; case VerticalAlignment.Center: childBounds.Y = y1 - elementHeight / 2.0; childBounds.Height = elementHeight; break; case VerticalAlignment.Bottom: childBounds.Y = y1 - elementHeight; childBounds.Height = elementHeight; break; } } else { // Position child inside vertical region. switch (verticalAlignment) { case VerticalAlignment.Top: childBounds.Y = y1; childBounds.Height = elementHeight; break; case VerticalAlignment.Center: childBounds.Y = y1 + availableHeight / 2.0 - elementHeight / 2.0; childBounds.Height = elementHeight; break; case VerticalAlignment.Bottom: childBounds.Y = y2 - elementHeight; childBounds.Height = elementHeight; break; case VerticalAlignment.Stretch: childBounds.Y = y1; childBounds.Height = availableHeight; break; } } // Arrange element. child.Arrange(childBounds); // Clip to chart area. if (elementThatRequiresArrange is UIElement && GetClipToChartArea(elementThatRequiresArrange)) { UIElement element = (UIElement)elementThatRequiresArrange; ClipElementToChartArea(element, childBounds); } } } return finalSize; }
protected virtual void OnGetStrokeAndFillForLegendSymbol(double paletteIndex, out Brush stroke, out Brush fill) { Axis xAxis = XAxis; Axis yAxis = YAxis; // Check coloring parameters. if (PaletteIndex == PaletteIndex.Index && FillMode == AreaFillMode.GradientVertical || PaletteIndex == PaletteIndex.XValue && FillMode == AreaFillMode.GradientVertical) throw new NotSupportedException("Vertical gradients are only supported if the palette is indexed by y values."); // Set default values for stroke and fill brush. stroke = null; fill = null; // Try to find colors in palette. // We draw from 0 upwards or from a negative value upwards to 0. double lower = Math.Min(0, paletteIndex); double higher = Math.Max(0, paletteIndex); //if (FillMode == AreaFillMode.Solid) //{ // // Fill legend symbol with single color. // lower = higher; //} // Define gradient direction. Point startPoint; Point endPoint; if (PaletteIndex == PaletteIndex.Index || PaletteIndex == PaletteIndex.XValue || FillMode == AreaFillMode.GradientHorizontal || FillMode == AreaFillMode.Solid) { startPoint = new Point(0, 0); endPoint = new Point(1, 0); if (xAxis != null && xAxis.Scale != null && xAxis.Scale.Reversed) ChartHelper.Swap(ref startPoint, ref endPoint); } else { startPoint = new Point(0, 1); endPoint = new Point(0, 0); if (yAxis != null && yAxis.Scale != null && yAxis.Scale.Reversed) ChartHelper.Swap(ref startPoint, ref endPoint); } // Get brush for fill and stroke. if (FillPalette != null && FillPalette.Count > 0) { fill = new LinearGradientBrush { GradientStops = FillPalette.GetGradient(lower, higher), StartPoint = startPoint, EndPoint = endPoint, ColorInterpolationMode = FillPalette.ColorInterpolationMode }; } if (StrokePalette != null && StrokePalette.Count > 0) { stroke = new LinearGradientBrush { GradientStops = StrokePalette.GetGradient(lower, higher), StartPoint = startPoint, EndPoint = endPoint, ColorInterpolationMode = StrokePalette.ColorInterpolationMode }; } }
protected virtual void OnGetStrokeAndFillForSegment(int index, out Brush stroke, out Brush fill) { Axis xAxis = XAxis; Axis yAxis = YAxis; // Check coloring parameters. if (PaletteIndex == PaletteIndex.Index && FillMode == AreaFillMode.GradientVertical || PaletteIndex == PaletteIndex.XValue && FillMode == AreaFillMode.GradientVertical) { throw new NotSupportedException("Vertical gradients are only supported if the palette is indexed by y values."); } // Find palette index. double paletteIndex = 0; switch (PaletteIndex) { case PaletteIndex.Index: paletteIndex = index; break; case PaletteIndex.XValue: paletteIndex = Data[index].X; break; case PaletteIndex.YValue: paletteIndex = Data[index].Y; break; default: throw new NotSupportedException("PaletteIndex type is not supported."); } // Set default values for stroke and fill brush. stroke = null; fill = null; // Try to find colors in palette. if (FillMode == AreaFillMode.Solid) { // Solid: // Get a solid color fill for fill and stroke. if (FillPalette != null && FillPalette.Count > 0) fill = new SolidColorBrush(FillPalette.GetColor(paletteIndex)); if (StrokePalette != null && StrokePalette.Count > 0) stroke = new SolidColorBrush(StrokePalette.GetColor(paletteIndex)); } else { // Get data values. DataPoint data = Data[index]; DataPoint dataBase = new DataPoint(data.X, 0, null); // The base is currently always y = 0. DataPoint previousData = (index > 0) ? Data[index - 1] : data; DataPoint previousDataBase = new DataPoint(previousData.X, 0, null); // The base is currently always y = 0. DataPoint nextData = (index < Data.Count - 1) ? Data[index + 1] : data; DataPoint nextDataBase = new DataPoint(nextData.X, 0, null); // The base is currently always y = 0. // Gradient: double fillStart; double fillEnd; double strokeStart; double strokeEnd; // Define palette index values. ChartInterpolation interpolation = EffectiveInterpolation; if (PaletteIndex == PaletteIndex.Index) { // Index-based if (interpolation == ChartInterpolation.CenteredSteps) { fillStart = strokeStart = Math.Max(index - 0.5, 0); fillEnd = strokeEnd = Math.Min(index + 0.5, Data.Count - 1); } else if (interpolation == ChartInterpolation.LeftSteps) { fillStart = strokeStart = Math.Max(index - 1, 0); fillEnd = strokeEnd = index; } else { fillStart = strokeStart = index; fillEnd = strokeEnd = index + 1; } } else if (PaletteIndex == PaletteIndex.XValue) { // x value based if (interpolation == ChartInterpolation.CenteredSteps) { fillStart = strokeStart = (previousData.X + data.X) / 2; fillEnd = strokeEnd = (data.X + nextData.X) / 2; } else if (interpolation == ChartInterpolation.LeftSteps) { fillStart = strokeStart = previousData.X; fillEnd = strokeEnd = data.X; } else { fillStart = strokeStart = data.X; fillEnd = strokeEnd = nextData.X; } } else { // y value based if (FillMode == AreaFillMode.GradientHorizontal) { if (interpolation == ChartInterpolation.CenteredSteps) { fillStart = strokeStart = (previousData.Y + data.Y) / 2; fillEnd = strokeEnd = (data.Y + nextData.Y) / 2; } else if (interpolation == ChartInterpolation.LeftSteps) { fillStart = strokeStart = previousData.Y; fillEnd = strokeEnd = data.Y; } else { fillStart = strokeStart = data.Y; fillEnd = strokeEnd = nextData.Y; } } else { fillStart = Math.Min(0, Math.Min(data.Y, dataBase.Y)); fillEnd = Math.Max(0, Math.Max(data.Y, dataBase.Y)); if (interpolation == ChartInterpolation.Linear) { fillStart = Math.Min(fillStart, Math.Min(nextData.Y, nextDataBase.Y)); fillEnd = Math.Max(fillEnd, Math.Max(nextData.Y, nextDataBase.Y)); } // This is the only case where the stroke has a different start and end value than the fill. // (Because the fill starts at y=0, but the line starts at the data point.) strokeStart = data.Y; strokeEnd = nextData.Y; if (strokeStart > strokeEnd) ChartHelper.Swap(ref strokeStart, ref strokeEnd); } } // Define gradient direction. Point startPoint; Point endPoint; if (PaletteIndex == PaletteIndex.Index || PaletteIndex == PaletteIndex.XValue || FillMode == AreaFillMode.GradientHorizontal) { startPoint = new Point(0, 0); endPoint = new Point(1, 0); if (xAxis != null && xAxis.Scale != null && xAxis.Scale.Reversed) ChartHelper.Swap(ref startPoint, ref endPoint); } else { startPoint = new Point(0, 1); endPoint = new Point(0, 0); if (yAxis != null && yAxis.Scale != null && yAxis.Scale.Reversed) ChartHelper.Swap(ref startPoint, ref endPoint); } // Get brush for fill and stroke. if (FillPalette != null && FillPalette.Count > 0) { fill = new LinearGradientBrush { GradientStops = FillPalette.GetGradient(fillStart, fillEnd), StartPoint = startPoint, EndPoint = endPoint, ColorInterpolationMode = FillPalette.ColorInterpolationMode }; } if (StrokePalette != null && StrokePalette.Count > 0) { stroke = new LinearGradientBrush { GradientStops = StrokePalette.GetGradient(strokeStart, strokeEnd), StartPoint = startPoint, EndPoint = endPoint, ColorInterpolationMode = StrokePalette.ColorInterpolationMode }; } } }