Related
I have a amChart4 XYChart loaded from a CSV external file.
How to mark the max values on a serie, max values are known when writing the file. So just need to mark the data points with a bullet.
var maxNm = 404.24;
var maxHP = 327.7;
Se code with working chart loaded from CSV.
https://codepen.io/lasse-kofoed/pen/WNbNXxe
// Themes begin
am4core.useTheme(am4themes_material);
am4core.useTheme(am4themes_animated);
// Themes end
// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);
chart.responsive.enabled = true;
// Set up data source
chart.dataSource.url = "https://master.tus.io/files/a6bfda6a8051313c0c0b1d7129a75786+DB7aQwWVBca.GAK4H6FLamUT58549Asv6vLoR9kEJySMEVOsFlCSi9eqzgMYLhqXdMJDZoTE0C90HuVUUKD7KoKdFjlM0f1IRkkQ0L5X6iykr8kSsyNWTtPkcmzIFwDp";
chart.dataSource.parser = new am4core.CSVParser();
chart.dataSource.parser.options.useColumnNames = true;
// Increase contrast by taking evey second color
chart.colors.step = 2;
chart.dataSource.events.on("error", function (ev) {
console.log("Oopsy! Something went wrong");
});
// Create value axis
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.tooltip.disabled = true;
valueAxis.title.text = "Power & Torque";
var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "Rpm";
// Create serie Nm
var nm = chart.series.push(new am4charts.LineSeries());
var maxNm = 404.24;
nm.dataFields.valueY = "Nm";
nm.dataFields.categoryX = "Rpm";
nm.yAxis = valueAxis;
nm.name = "Nm";
nm.strokeWidth = 1;
//nm.tensionX = 0.7;
nm.tooltipText = "{valueY.value} Nm";
var hp = chart.series.push(new am4charts.LineSeries());
var maxHP = 327.7;
hp.dataFields.valueY = "Hp";
hp.dataFields.categoryX = "Rpm";
hp.yAxis = valueAxis;
hp.name = "Hp";
hp.strokeWidth = 1;
//hp.tensionX = 0.7;
hp.tooltipText = "{valueY.value} Hp";
// Add legend
chart.legend = new am4charts.Legend();
// Add cursor
chart.cursor = new am4charts.XYCursor();
Updated a CodePen with the result.
https://codepen.io/lasse-kofoed/pen/eYmprPR
Found that using the event beforedatavalidated you can alter the data and set a field to false.
And then set propertyFields.disabled = true on a bullet object ( hides all bullets )
and .propertyFields.disabled to the field name that was added to the data list item ( show on bullets on values that have the field set to false )
chart.series.values[0].dataItem.values.valueY
Also found that the max and min values can be found here, but is not using that information in the example
// Add max Torque bullet
var maxTorque = nm.bullets.push(new am4charts.CircleBullet());
maxTorque.disabled = true;
maxTorque.propertyFields.disabled = "maxTorque";
maxTorque.radius = 8;
maxTorque.strokeWidth = 2;
maxTorque.stroke = am4core.color("#fff");
var maxTorqueLabel = nm.bullets.push(new am4charts.LabelBullet());
maxTorqueLabel.label.text = "Max torque\n[bold]{valueY} {name}#{categoryX}[/]";
maxTorqueLabel.disabled = true;
maxTorqueLabel.propertyFields.disabled = "maxTorque";
maxTorqueLabel.label.dy = -30;
// Add max Power bullet
var maxPower = hp.bullets.push(new am4charts.CircleBullet());
maxPower.disabled = true;
maxPower.propertyFields.disabled = "maxPower";
var maxPowerLabel = hp.bullets.push(new am4charts.LabelBullet());
maxPowerLabel.label.text = "Max power\n[bold]{valueY} {name}#{categoryX}[/]";
maxPowerLabel.disabled = true;
maxPowerLabel.propertyFields.disabled = "maxPower";
maxPowerLabel.label.dy = -30;
chart.events.on("beforedatavalidated", function(ev) {
var maxT = chart.data.find(x => x.Rpm === "5221");
var maxP = chart.data.find(x=> x.Rpm === "5888");
if(typeof maxT !== "undefined")
{
maxT.maxTorque = false;
}
if(typeof maxP !== "undefined")
{
maxP.maxPower = false;
}
});
How do i get row id of grid row selection? I want to add row unique id and i want it on tapped or on clicked. please suggest me if anyone have any idea.
I want to do some thing like this, if i click on the row and i get id. with this id i will fetch another records and send user on another page. for this i need unique id of the row.
i have grid like bellow:
Below is the code i use:
public BusList(Common busdata)
{
InitializeComponent();
this.BindingContext = this;
List<BusDetail> Bus = new List<BusDetail>
{
...
new BusDetail("Nita Travel","Mumbai", "Navsari",new DateTime(2017, 3, 9), "10:15 AM", "09:15 AM")
};
Label header = new Label
{
Text = "GridView Demo",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center
};
var controlGrid = new Grid
{
RowSpacing = 2,
ColumnSpacing = 2,
Margin = new Thickness(5, Device.OnPlatform(5, 0, 0), 5, 5)
};
controlGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(30) });
for (int i = 0; i < Bus.Count(); i++)
{
controlGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(30) });
}
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
Bus = Bus.Where(x => x.FromDest.ToLower().Contains(busdata.FromDest.ToLower()) && x.FromDate == busdata.FromDate).ToList();
controlGrid.Children.Add(new Label { Text = "ID", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 0, 0);
controlGrid.Children.Add(new Label { Text = "Travel Name", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 1, 0);
controlGrid.Children.Add(new Label { Text = "Arr Time", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 2, 0);
controlGrid.Children.Add(new Label { Text = "Dep Time", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 3, 0);
for (int i = 0; i < Bus.Count(); i++)
{
var clickableRow = new ContentView();
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandProperty, "RowTappedCommand");
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandParameterProperty, i.ToString());
clickableRow.GestureRecognizers.Add(tapGestureRecognizer);
clickableRow.BindingContext = i.ToString();
controlGrid.Children.Add(clickableRow, 0, i);
Grid.SetColumnSpan(clickableRow, 3);
controlGrid.Children.Add(new Label { Text = Bus[i].TravelName, BindingContext = Bus[i].TravelName, BackgroundColor = Color.LightGray, VerticalTextAlignment = TextAlignment.Center }, 1, i + 1);
controlGrid.Children.Add(new Label { Text = Bus[i].ArrivalTime, BindingContext = Bus[i].ArrivalTime, BackgroundColor = Color.LightGray, VerticalTextAlignment = TextAlignment.Center }, 2, i + 1);
controlGrid.Children.Add(new Label { Text = Bus[i].DepartureTime, BindingContext= Bus[i].DepartureTime, BackgroundColor = Color.LightGray, VerticalTextAlignment = TextAlignment.Center }, 3, i + 1);
}
this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
this.Content = new StackLayout
{
Children =
{
header,
controlGrid
}
};
}
public void RowTappedCommand(string index)
{
var tappedRow = int.Parse(index);
}
You could add a ContentView to each row on the first column and span it across all columns by using the ColumnSpan property. Then you'd handle the taps with a TouchGestureListener. When creating the Command, you'd also include a CommandParameter which contains the index of the row in question.
Outside the constructor, define this:
public ICommand RowTappedCommand { get; private set; }
Then add the following code somewhere after InitializeComponent:
RowTappedCommand = new Command<string>(RowTapped);
Then create the clickable ContentView controls for each row:
var clickableRow = new ContentView {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
}
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandProperty, "RowTappedCommand");
var binding = new Binding();
binding.Source = i.ToString();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandParameterProperty, binding); // This is the index of the row
clickableRow.GestureRecognizers.Add(tapGestureRecognizer);
controlGrid.Children.Add(clickableRow, 0, 0);
Grid.SetColumnSpan(clickableRow,3);
You also need to define the method that matches the name defined in the CommandProperty. If you have a view model defined for the view you need to do it there. Otherwise, you'll need to add the method to your view's code behind file (xaml.cs).
void RowTapped(string index)
{
var tappedRow = int.Parse(index);
// Do something with the value
}
Remember to set the BindingContext correctly so that your command gets called. Here's more information about Commands in Xamarin.Forms:
Update: There were a few mistakes that needed to be fixed.
Command wasn't defined correctly.
The binding for the command property needs to be pointing to a static value
Simplifying Events with Commanding
Recently, I started development using Xamarin.Forms in Visual Studio.
I need to draw something like this:
Is it possible using xamarin forms or is there any sample code for reference?
Thanks
I would look at the Syncfusion Circular Guage
See images below:
I am not 100% sure on the licencing, but I think they provide a Community Licence
Sorry for my English.
I have made something like that, see the picture below.
I copied some codes from some forums and made some ajusts of my own. But the principle is there, maybe adjust a few things and you can manage to achieve what you want. The adjusts that I made don't respect the proportional size in xaml (widht, height), I used the Request = 200. I hope that it can help you.
using System;
using System.Collections.Generic;
using System.Text;
using Microcharts.Forms;
using Microcharts;
using SkiaSharp;
using System.Linq;
namespace MES_App1.Chart
{
class SpeedometerChart : RadialGaugeChart
{
public SpeedometerChart()
{
BackgroundColor = SKColors.White;
}
private const float startAngle = 150;
private const float backgroundSweepAngle = 240;
public override void DrawContent(SKCanvas canvas, int width, int height)
{
var diferenceX = width > height ? (width - height) / 2 : 0;
var diferenceY = height > width ? (height - width) / 2 : 0;
var strokeWidth = (4 * (width - diferenceX)) / 100;
var rect = new SKRect(
5 + strokeWidth + diferenceX,
5 + strokeWidth + diferenceY + 50,
width - (5 + strokeWidth + diferenceX),
height - (5 + strokeWidth + diferenceY)+50);
var paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColor.Parse("#008000"),
StrokeWidth = strokeWidth*4,
IsAntialias = true,
IsStroke = true
};
float[] angulos = { 1, 0.85f, 0.7f, 0.55f, 0.4f, .25f };
string[] angulosStr = { "100%", "85%", "70%", "55%", "40%", "25%" };
string[] cores = { "#008000", "#32CD32", "#5F9EA0", "#FFA500", "#FFD700", "#FF0000" };
for (int i = 0; i < angulos.Length; i++)
{
using (SKPath backgroundPath = new SKPath())
{
paint.Color = SKColor.Parse(cores[i]);
backgroundPath.AddArc(rect, startAngle, backgroundSweepAngle * angulos[i]);
canvas.DrawPath(backgroundPath, paint);
}
using (SKPath labelPath = new SKPath())
{
var rectLabels = new SKRect
{
Left=rect.Left-strokeWidth*2-20,
Top=rect.Top-strokeWidth*2-20,
Right=rect.Right+strokeWidth*2+20,
Bottom=rect.Bottom+strokeWidth*2+20
};
var labelPaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
BlendMode = SKBlendMode.Clear,
Color = SKColors.Black,
StrokeWidth = 0,
IsAntialias = true,
IsStroke = true
};
labelPath.AddArc(rectLabels, startAngle, backgroundSweepAngle * angulos[i]);
canvas.DrawPath(labelPath, labelPaint);
canvas.DrawCaptionLabels(string.Empty, SKColor.Empty, angulosStr[i], SKColors.Black, 20,labelPath.LastPoint, SKTextAlign.Center);
}
}
float[] angulosLabel = { 1f, 0.85f, 0.7f, 0.55f, .25f };
float[] offsetLabels = { 20, 25, 20, 30, 30};
string[] labelsStr = { "Ideal", "Alta", "Tipico", "Precisa Melhoria", "Inaceitavel" };
for (int i = angulosLabel.Length-1; i >= 0; i--)
{
float anguloInicial;
if (i == angulosLabel.Length-1)
{
anguloInicial = startAngle;
}
else
{
anguloInicial = startAngle+backgroundSweepAngle * angulosLabel[i + 1];
}
using (SKPath labelPath = new SKPath())
{
var labelPaint = new SKPaint
{
TextSize=18
};
labelPath.AddArc(rect, anguloInicial, backgroundSweepAngle * angulosLabel[i]);
canvas.DrawTextOnPath(labelsStr[i], labelPath, offsetLabels[i], -10, labelPaint);
if (labelsStr[i] == "Alta")
{
labelPaint.TextSize = 16;
canvas.DrawTextOnPath("Performance", labelPath, 0, 10, labelPaint);
}
}
}
using (SKPath circlePath = new SKPath())
{
var circlePaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Black,
IsAntialias = true
};
circlePath.AddCircle(rect.MidX, rect.MidY, 20);
canvas.DrawPath(circlePath, circlePaint);
}
foreach (var entry in Entries.OrderByDescending(e => e.Value))
{
using (SKPath pointerPath = new SKPath())
{
var colors = new[]
{
SKColors.SlateGray,
SKColors.Gray,
SKColors.Black
};
var shader = SKShader.CreateLinearGradient(
new SKPoint(128.0f, 0.0f),
new SKPoint(128.0f,256.0f),
colors,
null,
SKShaderTileMode.Clamp);
var labelPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
StrokeJoin = SKStrokeJoin.Miter,
Shader = shader,
IsAntialias = true
};
canvas.Save();
canvas.RotateDegrees(entry.Value/100*backgroundSweepAngle-120, rect.MidX, rect.MidY);
pointerPath.MoveTo(rect.MidX-10, rect.MidY);
pointerPath.LineTo(rect.MidX, rect.Top);
canvas.DrawCaptionLabels(string.Empty, SKColor.Empty, entry.Value.ToString() + "%", SKColors.Black, 20, pointerPath.LastPoint, SKTextAlign.Center);
pointerPath.LineTo(10+rect.MidX, rect.MidY);
pointerPath.Close();
canvas.DrawPath(pointerPath, labelPaint);
canvas.Restore();
}
}
}
}
}
Trying to figure out if I can disable the auto scroll that occurs when a user taps on a cell in a ListView (I'm using a Recycle List View if it matters). I'm already setting the sender.SelectedItem to null to disable the cell highlight.
gridList.ItemSelected += (s, e) => {
((Xamarin.Forms.ListView)s).SelectedItem = null;
};
UPDATE ItemTemplate Layout
headline = new Xamarin.Forms.Label();
cellImage = new Xamarin.Forms.Image();
LoadingText = new Xamarin.Forms.Label();
LoadingText.Text = "Loading...";
layout = new Xamarin.Forms.Grid();
layout.RowDefinitions.Add(new Xamarin.Forms.RowDefinition { Height = new Xamarin.Forms.GridLength(1, Xamarin.Forms.GridUnitType.Star) });
layout.ColumnDefinitions.Add(new Xamarin.Forms.ColumnDefinition { Width = new Xamarin.Forms.GridLength(1, Xamarin.Forms.GridUnitType.Star) });
layout.Children.Add(LoadingText, 0, 0);
layout.Children.Add(headline, 0, 0);
layout.Children.Add(cellImage, 0, 0);
layout.Children.Add(activityIndicator, 0, 0);
LoadingText.HorizontalOptions = Xamarin.Forms.LayoutOptions.CenterAndExpand;
LoadingText.VerticalOptions = Xamarin.Forms.LayoutOptions.CenterAndExpand;
layout.HorizontalOptions = Xamarin.Forms.LayoutOptions.CenterAndExpand;
layout.VerticalOptions = Xamarin.Forms.LayoutOptions.CenterAndExpand;
cellImage.HorizontalOptions = Xamarin.Forms.LayoutOptions.CenterAndExpand;
cellImage.VerticalOptions = Xamarin.Forms.LayoutOptions.CenterAndExpand;
if(!hasLoadedAdAtLeastOnce)
{
AdView.Content.TranslationY = 5;
AdView.Content.TranslationX = -20;
hasLoadedAdAtLeastOnce = true;
}
cellImage.WidthRequest = 260;
cellImage.HeightRequest = 173;
_ad = (Controls.Ad)AdView.Content;
View = layout;
How can i show a polynomial tread line in Am-line chart. I'm getting a straight trend line here, i need a polynomial trend line. My javascript code is shown below. Please someone help me to solve this issue.
function GenChartData() {
var data1 = [0.234761158, 0.23816127, 0.263960124, 0.282558558, 0.300607979, 0.318197719, 0.316059534, 0.319133276, 0.322505238, 0.323926338, 0.323720379, 0.3203703, 0.318837626, 0.318380371, 0.321465339, 0.316398839, 0.310238176, 0.301206892, 0.278454166, 0.268778255, 0.250299958, 0.23754735, 0.216277621, 0.182483871, 0.152057602, 0.129372542, 0.079524595, 0.044074801, 0.007279248, -0.021369877, -0.022801251, -0.043247196, -0.060677351, -0.055932729, -0.055847788, -0.032625365, -0.027289726, -0.022615401, -0.010850169, 0.015833104, 0.043923065, 0.055500831, 0.048043121, 0.054154849, 0.064038257, 0.049914887, 0.046542406, 0.03154397, 0.033614909, 0.030570225, 0.035606699, 0.001179461, -0.028934007, -0.019034206, 2.30344E-05];
var dates = ["12/31/2001", "1/31/2002", "2/28/2002", "3/31/2002", "4/30/2002", "5/31/2002", "6/30/2002", "7/31/2002", "8/31/2002", "9/30/2002", "10/31/2002", "11/30/2002", "12/31/2002", "1/31/2003", "2/28/2003", "3/31/2003", "4/30/2003", "5/31/2003", "6/30/2003", "7/31/2003", "8/31/2003", "9/30/2003", "10/31/2003", "11/30/2003", "12/31/2003", "1/31/2004", "2/29/2004", "3/31/2004", "4/30/2004", "5/31/2004", "6/30/2004", "7/31/2004", "8/31/2004", "9/30/2004", "10/31/2004", "11/30/2004", "12/31/2004", "1/31/2005", "2/28/2005", "3/31/2005", "4/30/2005", "5/31/2005", "6/30/2005", "7/31/2005", "8/31/2005", "9/30/2005", "10/31/2005", "11/30/2005", "12/31/2005", "1/31/2006", "2/28/2006", "3/31/2006", "4/30/2006", "5/31/2006", "6/30/2006"];
for (var i = 0; i < dates.length; i++) {
chartData.push({
date: new Date(dates[i]),
data1: data1[i]});
}
}
AmCharts.ready(function () {
// SERIAL CHART
GenChartData();
chart = new AmCharts.AmSerialChart();
chart.pathToImages = "JS/AmFiles/amcharts/images/";
chart.marginTop = 0;
chart.marginRight = 10;
chart.autoMarginOffset = 5;
//chart.backgroundColor = "#CCCCC";
chart.zoomOutButton = {
backgroundColor: '#000000',
backgroundAlpha: 0.15
};
chart.dataProvider = chartData;
chart.categoryField = "date";
// listen for "dataUpdated" event (fired when chart is rendered) and call zoomChart method when it happens
chart.addListener("dataUpdated", zoomChart);
// AXES
// category
var categoryAxis = chart.categoryAxis;
categoryAxis.parseDates = true; // as our data is date-based, we set parseDates to true
categoryAxis.minPeriod = "MM"; // our data is daily, so we set minPeriod to DD
categoryAxis.dashLength = 1;
categoryAxis.gridAlpha = 0.15;
categoryAxis.axisColor = "#DADADA";
categoryAxis.labelFrequency = 1;
categoryAxis.equalSpacing = true;
// value
var valueAxis = new AmCharts.ValueAxis();
valueAxis.axisAlpha = 0.2;
valueAxis.dashLength = 1;
chart.addValueAxis(valueAxis);
graph = new AmCharts.AmGraph();
graph.type = "smoothedLine";
graph.lineColor = "#180ad1";
graph.negativeLineColor = "#180ad1";
graph.bullet = "round";
graph.bulletSize = 5;
graph.lineThickness = 2;
graph.valueField = "data1";
chart.addGraph(graph);
// CURSOR
chartCursor = new AmCharts.ChartCursor();
chartCursor.cursorPosition = "mouse";
chart.addChartCursor(chartCursor);
// TREND LINES
// first trend line
var trendLine = new AmCharts.TrendLine();
trendLine.initialDate = new Date(chartData[0].date); // 12 is hour - to start trend line in the middle of the day
trendLine.finalDate = new Date(chartData[chartData.length-1].date);
trendLine.initialValue = 0.234761158;
trendLine.finalValue = 2.30344E-05;
trendLine.lineColor = "#CC0000";
chart.addTrendLine(trendLine);
// WRITE
chart.write("chartdiv");
});
Thanks for your help
You would need to basically have another graphline on there rather than a trendline.
You can look at this example - http://www.amcharts.com/javascript-charts/line-with-different-bullet-sizes/
to see how to equip multiple data graphs.