private void DrawSlider(SKCanvas canvas, SKRect frame, SKRect chart) { if (!IsSliderVisible) { return; } if (!IsInitialized) { FindClosestItem(TouchedPoint.X); } float x = chart.GetInsideXValue(TouchedPoint.X); using (var paint = new SKPaint { Style = SKPaintStyle.Stroke, StrokeCap = UseItemWidthSlider ? SKStrokeCap.Butt : SKStrokeCap.Round, Color = this.SliderColor.ToSKColor().AsTransparency(), StrokeWidth = UseItemWidthSlider ? frame.GetItemWidth(MaxItems) : this.SliderWidth }) { // Straight slider line canvas.DrawLine(x, chart.Top, x, DisplayHorizontalValuesBySlider && !UseItemWidthSlider ? frame.Bottom + HorizontalTextSize : chart.Bottom, paint); DrawSliderHint(canvas, x); } // Get items on x axis var valueItems = ChartEntries.GetChartValueItemFromX(x, frame, frame.GetItemWidth(MaxItems)); // Send selected items with command SelectedValuesCommand?.Execute(new SelectedChartValueItemArgs { ChartValueItems = valueItems, TouchedPoint = new SKPoint(x, TouchedPoint.Y) }); }
protected void CalculateChartValuesXPoints(SKRect chart) { ChartValueItemsXPoints = new List <Tuple <object, float> >(); var chartEntries = ChartValuesDistinct; // Calculate the width of one item (distance between items) var itemWidth = chart.GetItemWidth(chartEntries.Count()); for (int i = 0; i < chartEntries.Count(); i++) { var entry = chartEntries.ElementAt(i); float x = chart.Left + (i * itemWidth); ChartValueItemsXPoints.Add(new Tuple <object, float>(entry.Tag, x)); } }
private void DrawBackground(SKCanvas canvas, SKRect frame) { if (!HasBackground) { return; } using (var paint = new SKPaint { IsAntialias = true, Color = ChartBackgroundColor.ToSKColor(), Style = SKPaintStyle.Fill }) { var items = ChartValueItemsXPoints; var width = frame.GetItemWidth(MaxItems); for (int i = 0; i < items.Count; i++) { if (i % 2 != 0) { continue; } var item = items[i]; var left = item.Item2; // Don't draw outside frame if ((left + (width * 2)).ToRounded() <= (frame.Right + FrameWidth).ToRounded()) { canvas.DrawRect(left + width, frame.Top, width, frame.Height - FrameWidth, paint); } } } }
protected SKPoint[] CalculatePoints(IEnumerable <ChartValueItem> valueItems, SKRect frame, SKRect chart) { var result = new List <SKPoint>(); // Calculate how many values one y position is var valueY = MaxValue / frame.Height; // Calculate the width of one item (distance between items) var itemWidth = chart.GetItemWidth(MaxItems); for (int i = 0; i < valueItems.Count(); i++) { var entry = valueItems.ElementAt(i); float x; if (ChartValueItemsXPoints.FirstOrDefault(p => p.Item1.ToString() == entry.Tag.ToString())?.Item2 > 0) { x = ChartValueItemsXPoints.FirstOrDefault(p => p.Item1.ToString() == entry.Tag.ToString()).Item2; } else { x = chart.Left + (i * itemWidth); } // Calculate items y position with frame height (bottom to upper) var y = frame.Top + (frame.Height - (entry.Value / valueY)); var point = new SKPoint(x, y); result.Add(point); entry.Point = point; } return(result.ToArray()); }
private void DrawBackground(SKCanvas canvas, SKRect frame) { if (!HasBackground) { return; } using (var paint = new SKPaint { IsAntialias = true, Color = ChartBackgroundColor.ToSKColor(), Style = LineBackground ? SKPaintStyle.Stroke : SKPaintStyle.Fill, IsStroke = LineBackground, }) { if (DashedFrame) { paint.PathEffect = SKPathEffect.CreateDash(new float[] { 12, 12 }, 0); } if (LineBackground) { paint.StrokeWidth = 2f; } var items = ChartValueItemsXPoints; var width = frame.GetItemWidth(MaxItems); for (int i = 0; i < items.Count; i++) { if (LineBackground) { var item = items[i]; var left = item.Item2; if (i != 0 && i != items.Count - 1) { canvas.DrawLine(left, frame.Bottom, left, frame.Top, paint); } } else { if (i % 2 != 0) { continue; } var item = items[i]; var left = item.Item2; // Don't draw outside frame if ((left + (width * 2)).ToRounded() <= (frame.Right + FrameWidth).ToRounded()) { canvas.DrawRect(left + width, frame.Top, width, frame.Height - FrameWidth, paint); } } } } }
private void DrawBars(SKCanvas canvas, SKRect frame, SKRect chart) { // Selected bar width var selectedValueItems = ChartEntries.GetChartValueItemFromX(chart.GetInsideXValue(TouchedPoint.X), chart, chart.GetItemWidth(MaxItems)); var selectedTags = selectedValueItems?.Select(x => x.ChartValueItem.Tag); // Invoke command with selected items SelectedValuesCommand?.Execute(new SelectedChartValueItemArgs { ChartValueItems = selectedValueItems, TouchedPoint = new SKPoint(chart.GetInsideXValue(TouchedPoint.X), TouchedPoint.Y) }); using (var paint = new SKPaint { IsAntialias = true, Style = SKPaintStyle.StrokeAndFill, StrokeCap = SKStrokeCap.Butt, }) { var groupedItems = ChartEntries.Where(x => x.IsVisible).SelectMany(x => x.Items).GroupBy(x => x.Tag.ToString()).OrderBy(x => x.Key); // Calc max items in one group var maxItemsInGroups = groupedItems.Max(x => x.Count()); // Calc how many groups there is var groupCount = groupedItems.Count(); // Calc how many bars there will be var totalBars = groupCount * maxItemsInGroups; var internalGroupMargin = maxItemsInGroups == 1 ? BarMargin : GroupMargin; var internalBarMargin = maxItemsInGroups == 1 ? 0 : BarMargin; var internalBarWidth = MinimumBarWidth; float itemWidth; if (AllowScroll) { // Calc total item width itemWidth = internalBarWidth * maxItemsInGroups + ((maxItemsInGroups - 1) * barMargin); } else { itemWidth = chart.Width / groupCount; itemWidth -= internalGroupMargin; internalBarWidth = (itemWidth / maxItemsInGroups) - ((maxItemsInGroups - 1) * barMargin); } int groupIndex = 0; float scrollLeftPadding = 0; float left = 0; float groupLeft = 0; float right = 0; groupCenters = new List <GroupChartItem>(); foreach (var groupedItem in groupedItems) { int itemIndex = 0; groupLeft = left; if (!AllowScroll && IsSliderVisible && selectedTags?.Contains(groupedItem.Key) == true) { var item = groupedItem.OrderByDescending(x => x.Value).First(); paint.Color = SliderColor.ToSKColor(); paint.PathEffect = SKPathEffect.CreateCorner(SliderCornerRadius); var bounds = new SKRect( item.Point.X - (itemWidth * .5f) - (groupMargin * .5f), item.Point.Y - (itemWidth * .5f), item.Point.X + (itemWidth * .5f) + (groupMargin * .5f), chart.Bottom + HorizontalTextSize); paint.StrokeWidth = 0; canvas.DrawRect(bounds, paint); } foreach (var item in groupedItem) { var parent = ChartEntries.FirstOrDefault(x => x.Items.Contains(item)); SKRect bounds = new SKRect(); if (AllowScroll) { if (left == 0) { left = itemWidth.FromDpiAdjusted(); groupLeft = left; scrollLeftPadding = left; } else if (itemIndex != 0) { left += internalBarMargin; } right = left + internalBarWidth; bounds = new SKRect( left, item.Point.Y, right, chart.Bottom); left = right; } else { left = (item.Point.X - (itemWidth * .5f)) + (internalBarWidth * itemIndex) + (internalBarMargin * itemIndex); right = left + internalBarWidth; bounds = new SKRect( left, item.Point.Y, right, chart.Bottom); } paint.StrokeWidth = 0; if (parent.UseDashedEffect) { paint.Color = parent.Color.ToSKColor().AsTransparency(); paint.PathEffect = SKPathEffect.CreateCorner(BarCornerRadius); canvas.DrawRect(bounds, paint); paint.Color = parent.Color.ToSKColor(); paint.PathEffect = SKPathEffect.CreateDash(new float[] { StrokeDashFirst, StrokeDashSecond }, StrokeDashPhase); paint.StrokeWidth = bounds.Width; canvas.DrawLine(bounds.MidX, bounds.Bottom, bounds.MidX, bounds.Top, paint); } else { paint.Color = parent.Color.ToSKColor(); paint.PathEffect = SKPathEffect.CreateCorner(BarCornerRadius); canvas.DrawRect(bounds, paint); } itemIndex++; } left += internalGroupMargin; groupIndex++; var groupCenterPosition = groupLeft + (itemWidth / 2); groupCenters.Add(new GroupChartItem { Label = groupedItem.FirstOrDefault().Label, Position = groupCenterPosition.FromDpiAdjusted(), Tag = groupedItem.Key }); } if (AllowScroll) { var requestedWidth = right.FromDpiAdjusted() + frame.Left.FromDpiAdjusted(); if (requestedWidth != this.WidthRequest) { this.WidthRequest = requestedWidth; } else { SetStartPosition(groupCenters); IsInitialized = true; } if (ScrollComponent != null) { var deviceWidth = Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Width; var halfDeviceWidth = ((float)(deviceWidth / 2)).FromDpiAdjusted(); var frameLeftVal = frame.Left.FromDpiAdjusted(); var rectRightMarginVal = ChartRectMargin.Right.FromDpiAdjusted(); var leftPadding = halfDeviceWidth - frameLeftVal - scrollLeftPadding.FromDpiAdjusted(); var rightPadding = halfDeviceWidth - frameLeftVal - rectRightMarginVal - itemWidth.FromDpiAdjusted(); ScrollComponent.Padding = new Thickness(leftPadding, 0, rightPadding, 0); ScrollComponent.Margin = new Thickness(frame.Left.FromDpiAdjusted(), 0, ChartRectMargin.Right.FromDpiAdjusted(), 0); if (Selector != null) { Selector.WidthRequest = itemWidth.FromDpiAdjusted(); Selector.Margin = new Thickness(halfDeviceWidth, 0, 0, this.Height - frame.Bottom.FromDpiAdjusted()); } if (!isScrollEventActive) { ScrollComponent.Scrolled += ScrollComponent_Scrolled; isScrollEventActive = true; } } if (SwipeNotificationLeft != null) { SwipeNotificationLeft.Margin = new Thickness(frame.Left.FromDpiAdjusted(), 0, 0, this.Height - frame.Bottom.FromDpiAdjusted()); SwipeNotificationLeft.WidthRequest = ScrollComponent.Padding.Left; } if (SwipeNotificationRight != null) { SwipeNotificationRight.Margin = new Thickness(0, 0, ChartRectMargin.Right.FromDpiAdjusted(), this.Height - frame.Bottom.FromDpiAdjusted()); SwipeNotificationRight.WidthRequest = ScrollComponent.Padding.Left; } } } DrawHorizontalLabel(selectedValueItems?.FirstOrDefault()?.ChartValueItem, canvas, frame, chart); }
public static IList <ChartValueItemParam> GetChartValueItemFromX(this IList <ChartItem> chartItems, float xPosition, SKRect frame, int maxItems, bool takeClosest = true) { var items = new List <ChartValueItemParam>(); if (chartItems?.Any(x => x.IsVisible) != true) { return(items); } foreach (var chartEntry in chartItems.Where(x => x.Items?.Any() == true && x.IsVisible)) { ChartValueItem item = null; if (!takeClosest) { // Create a bound and get item inside this bound foreach (var valueItem in chartEntry.Items) { SKRect rect = new SKRect((xPosition - (frame.GetItemWidth(maxItems) / 2)), 0, (xPosition + (frame.GetItemWidth(maxItems) / 2)), frame.Bottom + 2); if (rect.Contains(valueItem.Point)) { item = valueItem; } } } else { // Order list and takes the first that's lower then X value item = chartEntry.Items.OrderByDescending(c => c.Point.X).FirstOrDefault(c => c.Point.X <= xPosition); } // No item found so check if we touched left of the frame if (item == null) { item = chartEntry.Items.FirstOrDefault(); if ((xPosition <= frame.Left && item.Point.X == frame.Left)) { items.Add(new ChartValueItemParam(item, null, chartEntry)); } continue; } // Get the index of the found item int index = chartEntry.Items.IndexOf(item); // It's the last item in the list so add just the this entry if (index + 1 >= chartEntry.Items.Count) { if ((xPosition >= frame.Right && item.Point.X == frame.Right) || item.Point.X >= (xPosition - (frame.GetItemWidth(maxItems) / 2))) { items.Add(new ChartValueItemParam(item, null, chartEntry)); } continue; } // Takes the next item in the list var nextItem = chartEntry.Items[index + 1]; if (item != null) { // Add current item, next item and parents line color items.Add(new ChartValueItemParam(item, nextItem, chartEntry)); } } return(items); }
private void DrawBars(SKCanvas canvas, SKRect frame, SKRect chart) { var itemWidth = (MaxItems >= 12 ? chart.GetItemWidth(MaxItems) : chart.GetItemWidth(12)) / 2; var count = ChartEntries.Count(x => x.IsVisible); // Regular bar width var barWidth = (itemWidth / count) - 10; // Selected bar width var selectedValueItems = ChartEntries.GetChartValueItemFromX(chart.GetInsideXValue(TouchedPoint.X), chart, MaxItems, false); var selectedTags = selectedValueItems?.Select(x => x.ChartValueItem.Tag); int index = 0; foreach (var item in ChartEntries.Where(x => x.IsVisible)) { foreach (var valueItem in item.Items.Where(x => selectedTags?.Contains(x.Tag) != true)) { // Draw not selected bars // canvas.DrawBar( valueItem.Point.X, valueItem.Point.Y, chart.Bottom, barWidth, BarMargin, count, index, IsSliderVisible ? item.Color.ToSKColor().AsTransparency() : item.Color.ToSKColor(), item.UseDashedEffect); } index++; } if (!IsSliderVisible) { return; } count = selectedValueItems?.Count() ?? 1; barWidth = (itemWidth / selectedValueItems?.Count() ?? 1) + 10; index = 0; foreach (var item in ChartEntries.Where(x => x.IsVisible)) { var valueItems = item.Items.Where(x => selectedTags?.Contains(x.Tag) == true); if (valueItems?.Any() != true) { continue; } foreach (var valueItem in valueItems) { canvas.DrawBar( valueItem.Point.X, valueItem.Point.Y, chart.Bottom, barWidth, BarMargin, count, index, item.Color.ToSKColor(), item.UseDashedEffect); string text = Math.Round(double.Parse(valueItem.Value.ToString()), 0, MidpointRounding.AwayFromZero).ToString() + " " + this.VerticalUnit; canvas.DrawSliderValue( text, frame.GetInsideXValue(valueItem.Point.X), frame.Top, SliderDetailTextSize, SKColors.White, item.Color.ToSKColor(), SliderDetailPadding, SliderDetailMargin, MaxValue + " " + this.VerticalUnit, selectedValueItems.Count, index, SliderDetailOrientation, frame, item.UseDashedEffect); } index++; } float hintX = selectedValueItems?.FirstOrDefault()?.ChartValueItem?.Point.X ?? 0; float hintY = selectedValueItems?.OrderByDescending(x => x.ChartValueItem.Point.Y)?.FirstOrDefault()?.ChartValueItem?.Point.Y ?? 0; if (hintX != 0 && hintY != 0) { DrawDragHintGraphic( canvas, hintX, hintY + ((frame.Bottom - hintY) / 2), (barWidth * count), frame); } }