I have a C# Windows Forms application in which there is a screen designed to show a Graph using the DataVisualization.Charting.Chart class where the X axis are composed of DateTime and the Y Axis are composed of integers (the goal is to represent the memory usage in MB over time of some other processes). So, I want to display this in a format of a Continuous function. But when I set the DataVisualization.Charting.Series object type to SeriesChartType.Line the Form plots the graph in a very strange way, see image below:
and when I set the object series type to SeriesChartType.Point the displayed graph is:
Notice that there are a lot of points that are in blank and that's ok because there aren't any registry of memory usage between those time intervals. The only problem I'm complaining here is that in the Line mode the graph is being plotted in this strange way. The code for the generation of these graphs is:
private void CarregaSerieMemoria()
{
// this InvokeRequired is because it is called in a separeted Thread, the graph creation happens in the Else block
if (this.InvokeRequired)
{
VoidVoidDelegate d = new VoidVoidDelegate(CarregaSerieMemoria);
this.Invoke(d);
}
else
{
try {
// Data table containing the Memory Usage history
foreach (DataRow row in Dados.dsComponentes.Tables["MemoryHistory"].Rows)
{
string proc = row["NomeProcesso"].ToString();
if (!string.IsNullOrEmpty(proc))
{
string dataStr = row["TimeStamp"].ToString();
string memoriaStr = row["Memoria"].ToString();
DateTime data;
int memoria;
try
{
data = DateTime.ParseExact(dataStr, "yyyyMMdd-HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
memoria = int.Parse(memoriaStr) / 1000;
}
catch (FormatException)
{
continue;
}
if (TemSerieProc(proc))
{ // if there is already a Series object with proc name
Series s = this.chartMemory.Series.Where(x => x.Name.Equals(proc)).FirstOrDefault();
s.Points.AddXY(data, memoria);
}
else
{ // else creates a new Series object and add this current point (data,memoria)
Series s = DefineNovaSerie(proc);
s.XValueType = ChartValueType.DateTime;
s.Points.AddXY(data, memoria);
this.chartMemory.Series.Add(s);
}
}
}
chartMemory.ChartAreas[0].AxisX.LabelStyle.Format = "dd/MM/yyyy HH:mm:ss";
chartMemory.ChartAreas[0].AxisX.Interval = 30;
chartMemory.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Minutes;
chartMemory.ChartAreas[0].AxisX.IntervalOffset = 1;
chartMemory.ChartAreas[0].AxisX.Minimum = graphDateBegin.ToOADate();
chartMemory.ChartAreas[0].AxisX.Maximum = graphDateEnd.AddHours(24).ToOADate();
chartMemory.ChartAreas[0].AxisX.MajorGrid.LineWidth = 0;
chartMemory.ChartAreas[0].AxisY.MajorGrid.LineWidth = 0;
chartMemory.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chartMemory.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
chartMemory.ChartAreas[0].AxisX.Title = "Horário";
chartMemory.ChartAreas[0].AxisY.Title = "Memória (MegaBytes)";
chartMemory.MouseWheel += chartMemory_MouseWheel;
chartMemory.MouseClick += chartMemory_MouseClick;
chartMemory.Visible = true;
labelLoad.Visible = false;
btnReload.Visible = true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
private Series DefineNovaSerie(string proc)
{
Series temp = new Series(proc);
temp.ChartType = SeriesChartType.Line;
//temp.MarkerSize = 10;
temp.Color = GetNextColor(nextColorInt++);
return temp;
}
You populating chart from not ordered data. Your x axis is date so your data should be ordered by date when adding point to chart.
Here is an example (not tested) how you might fix the issue by sorting your data from datatable on TimeStamp field.
var dataTable = Dados.dsComponentes.Tables["MemoryHistory"];
var orderedDataView = new DataView(dataTable);
orderedDataView.Sort = "TimeStamp";
foreach (DataRow row in orderedDataView.ToTable().Rows)
{
//rest of code
}
Related
I plotted a graph by using .csv file.Now user enable to select the chart area and clicking export button then selected data want to write another .csv file.
In here I want to approach this methods.User want to get chart1.ChartAreas[0].CursorX.SelectionStart,chart1.ChartAreas[0].CursorX.SelectionEndthen want to read .csv file and deleting others raw that in not selected area.
Note:the user want to select multiple chart area also.Please give me a clarification about this.This is winform application and I use MsChart.
my code as follows;
private void btnExport_Click(object sender, EventArgs e)
{
List<Graph> ObservingData = new List<Graph>(); // List to store all available Graph objects from the CSV
int index = 0;
using (StreamWriter sw = new StreamWriter(#"D:\CSVFile\NEWFile\Export\NewFile.csv"))
{
// Loops through each lines in the CSV
foreach (string line in System.IO.File.ReadAllLines(pathToCsv))
{
// here line stands for each line in the csv file
string[] CsvLine = line.Split(',');
// creating an object of type Graph based on the each csv line
// and adding them to the List<Graph>
Graph Instance1 = new Graph();
if (index == 0)
{
sw.WriteLine(line);
}
else
{
//Add the code here..
// if (((chart1.ChartAreas[0].CursorX.SelectionStart))<= && ( <= (chart1.ChartAreas[0].CursorX.SelectionEnd)))
{
sw.WriteLine(line);
}
}
index++;
}
sw.Close();
}
MessageBox.Show("Data are copied to the new .CSV file");
}
Following code for my multiple selection for chart area.
SizeF curRange = SizeF.Empty;
List<SizeF> ranges = new List<SizeF>();
List<int> selectedIndices = new List<int>();
private void chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
{
ranges.Add(curRange);
selectedIndices.Union(collectDataPoints(chart1.Series[0],curRange.Width, curRange.Height)).Distinct();
StripLine sl = new StripLine();
sl.BackColor = Color.FromArgb(255, Color.LightSeaGreen);
sl.IntervalOffset = Math.Min(curRange.Width, curRange.Height);
sl.StripWidth = Math.Abs(curRange.Height - curRange.Width);
chart1.ChartAreas[0].AxisX.StripLines.Add(sl);
}
List<int> collectDataPoints(Series s, double min, double max)
{
List<int> hits = new List<int>();
for (int i = 0; i < s.Points.Count; i++)
if (s.Points[i].XValue >= min && s.Points[i].XValue <= max) hits.Add(i);
return hits;
}
private void chart1_SelectionRangeChanging(object sender, CursorEventArgs e)
{
curRange = new SizeF((float)e.NewSelectionStart, (float)e.NewSelectionEnd);
}
Following code is plotted graph;
private void Output_Load(object sender, EventArgs e)
{
List<Graph> ObservingData = new List<Graph>(); // List to store all available Graph objects from the CSV
// Loops through each lines in the CSV
foreach (string line in System.IO.File.ReadAllLines(pathToCsv).Skip(1)) // .Skip(1) is for skipping header
{
// here line stands for each line in the csv file
string[] InCsvLine = line.Split(',');
// creating an object of type Graph based on the each csv line
Graph Inst1 = new Graph();
Inst1.Date = DateTime.ParseExact(InCsvLine[0], dateFormatString, CultureInfo.InvariantCulture);
Inst1.AvE = double.Parse(InCsvLine[15]);
Inst1.AvI = double.Parse(InCsvLine[16]);
chart1.Series["Speed"].YAxisType = AxisType.Primary;
chart1.Series["Velocity"].YAxisType = AxisType.Secondary;
chart1.Series["Speed"].Points.AddXY(Inst1.Date.TimeOfDay.ToString(), Inst1.AvE);
chart1.Series["Speed"].ChartType = SeriesChartType.FastLine;
chart1.Series["Velocity"].Points.AddXY(Inst1.Date.TimeOfDay.ToString(), Inst1.AvI);
chart1.Series["Velocity"].ChartType = SeriesChartType.FastLine;
ChartArea CA = chart1.ChartAreas[0];
CA.AxisX.ScaleView.Zoomable = false;
CA.AxisY.ScaleView.Zoomable = false;
CA.CursorX.AutoScroll = true;
CA.CursorX.IsUserSelectionEnabled = true;
}
}
This is out put of graph
Here is an example you may want to study.
It assumes that the x-values are added as DateTimes; set the XValueType!
eachSeries.XValueType = ChartValueType.DateTime;
It also assumes that you have a class level List of sereis to be exported:
var series2Export = new List<Series>() { chart.Series[0], chart.Series[1]};
It assumes further that the points in the series are added in parallel, so that you want to export the same range together, e.g. to first 20 or points from index 33-66..
Now you can for example code the SelectionRangeChanged event (or, of course a Save Button click):
private void chart_SelectionRangeChanged(object sender, CursorEventArgs e)
{
ChartArea ca = chart.ChartAreas[0];
var d0 = (int) ca.CursorX.SelectionStart;
var d1 = (int) ca.CursorX.SelectionEnd;
var spoints = series2Export[0].Points
.Where(x => x.XValue >= d0 && x.XValue <= d1).ToList();
string del1 = ";";
string del2 = "\t";
string fmt1 = "yyyy.MM.dd";
string fmt2 = "#0.0";
SaveFileDialog sfd = new SaveFileDialog();
sfd.FileName = suggestedFilePath;
sfd.Title = (d1 - d0) + " points selected. Save data to..";
if (sfd.ShowDialog() == DialogResult.OK)
{
StringBuilder sb = new StringBuilder();
foreach (var dp0 in spoints)
{
int pi = chart.Series[0].Points.IndexOf(dp0);
string st = "";
foreach (var s in series2Export)
{
DataPoint dp = s.Points[pi];
st += (DateTime.FromOADate(dp.XValue)).ToString(fmt1) +
del1 + dp.YValues[0].ToString(fmt2) + del2;
}
sb.AppendLine(st);
}
File.WriteAllText(sfd.FileName, sb.ToString());
}
}
}
Note that I have used strings for both the delimiters and the formts of the data. You can adapt them as needed..
The above code is exporting the data from all participating series in the format: x-value1, yvalue1..; this means it assumes that..
the datapoints contain all the information you want to export and
you want to export the x-values for each point.
If you want to export just the csv line as you had read it in, the simplest way is to store that line with each DataPoint, of, say the 1st series. The natural spot to store them in are the Tags.
Here are two functions to create a list of data points from a line and to restore that line from a series:
List<DataPoint> PointsFromCsvLine(string line)
{
var parts = line.Split(',').ToList(); // use your separator
DateTime dt = Convert.ToDateTime(parts[0]); // add checks!
double d1 = Convert.ToDouble(parts[1]); // use..
double d2 = Convert.ToDouble(parts[4]); // ..your..
double d3 = Convert.ToDouble(parts[9]); // ..numbers!
var points = new List<DataPoint>();
points.Add(new DataPoint( dt.ToOADate(), d1));
points.Add(new DataPoint( dt.ToOADate(), d2));
points.Add(new DataPoint( dt.ToOADate(), d3));
points[0].Tag = line;
return points;
}
string CsvLineFromPoint(Series series0, int index )
{
DataPoint dp = series0.Points[index]; // check index
string s = dp.Tag.ToString();
return s;
}
I am trying to create a line chart that shows test results over a period of time (interval of weeks). It's the first time I've used the chart control and I seem to keep displaying a grey square if I add points in from a loop:
Like this http://imageshack.us/a/img69/4718/69sq.png
I just can't see where I'm going wrong in my code - if i add some generic points in by hand then it will display properly.
Here is the code I'm using:
chtBreakdown.ChartAreas[0].AxisY.Minimum = 0;
chtBreakdown.ChartAreas[0].AxisY.Maximum = 100;
chtBreakdown.ChartAreas[0].AxisY.Interval = 10;
chtBreakdown.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Weeks;
chtBreakdown.ChartAreas[0].AxisX.Interval = 1;
dtiStart.Value = DateTime.Now.AddMonths(-3);
dtiEnd.Value = DateTime.Now;
chtBreakdown.Series.Clear();
DateTimeOffset minimum = dtiStart.Value;
DateTimeOffset maximum = dtiEnd.Value;
chtBreakdown.ChartAreas[0].AxisX.Minimum = minimum.DateTime.ToOADate();
chtBreakdown.ChartAreas[0].AxisX.Maximum = maximum.DateTime.ToOADate();
foreach (User u in allUsers)
{
List<Training> userTraining = u.TrainingList.Where(t => t.StartTime >= minimum && t.StartTime <= maximum).OrderBy(t => t.EndTime).ToList();
if (userTraining.Count != 0)
{
Series series = new Series(u.DisplayName);
series.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series.BorderWidth = 2;
series.XValueType = ChartValueType.DateTime;
foreach (Training t in userTraining) series.Points.AddXY(t.StartTime.DateTime, t.PassPercentage);
chtBreakdown.Series.Add(series);
}
}
Can anybody show me where I'm going wrong?
Your Series instances are probably not associated to any ChartArea by default. Try to add this:
series.ChartArea = chtBreakdown.ChartAreas[0].Name;
I have 2 problems:
I want the names from the datatable but it is showing me in numeric form.
I would like a gap between the two bars but I can't find a way.
Here is the code:
private void InitializeGraph (DataTable poDt)
{
Telerik.Charting.ChartSeries chartseries = new Telerik.Charting.ChartSeries();
try
{
chartseries.Type = Telerik.Charting.ChartSeriesType.Bar;
Telerik.Charting.ChartSeriesItem csItem;
RadChart1.PlotArea.XAxis.AutoScale = true;
RadChart1.PlotArea.XAxis.DataLabelsColumn = "Name";
for (int iRow = 0; iRow < poDt.Rows.Count; iRow++)
{
chartseries = new Telerik.Charting.ChartSeries();
chartseries.Type = Telerik.Charting.ChartSeriesType.Bar;
chartseries.Name = poDt.Rows[iRow]["Name"].ToString().Trim();
csItem = new Telerik.Charting.ChartSeriesItem();
csItem.Name = poDt.Rows[iRow]["Name"].ToString();
csItem.Label.TextBlock.Text = poDt.Rows[iRow]["Value"].ToString();
RadChart1.PlotArea.XAxis.Appearance.TextAppearance.AutoTextWrap = Telerik.Charting.Styles.AutoTextWrap.True;
csItem.YValue = Int32.Parse(poDt.Rows[iRow]["Value"].ToString());
chartseries.AddItem(csItem);
RadChart1.Series.Add(chartseries);
}
RadChart1.PlotArea.XAxis.AddRange(1, poDt.Rows.Count, 1);
RadChart1.PlotArea.XAxis[poDt.Rows.Count].TextBlock.Text = chartseries.Name;
poDt.Rows.Count.ToString();
RadChart1.PlotArea.XAxis.AutoShrink = false;
RadChart1.PlotArea.XAxis.AutoShrink = true;
RadChart1.Series.Add(chartseries);
RadChart1.PlotArea.Appearance.Border.Visible = false;
RadChart1.Appearance.Border.Visible = true;
RadChart1.PlotArea.YAxis.IsLogarithmic = true;
RadChart1.PlotArea.YAxis.AutoScale = true;
RadChart1.PlotArea.YAxis.Appearance.ValueFormat=Telerik.Charting.Styles.ChartValueFormat.Number;
RadChart1.Appearance.BarWidthPercent = 50;
RadChart1.Chart.Appearance.FillStyle.MainColor = System.Drawing.Color.Red;
RadChart1.Chart.Appearance.FillStyle.MainColor = System.Drawing.Color.Transparent;
RadChart1.Legend.Appearance.FillStyle.MainColor = System.Drawing.Color.Transparent;
}
catch (Exception Ex)
{
//throw;
}
finally
{
poDt.Clear();
poDt = null;
chartseries = null;
}
}
Sorry, I do not believe there is a way to display two X-axis at the same time.
My suggestion is you use a CategoricalAxis for your X axis and create a custom bar chart that has a legend which differentiates the two values. I don't have any working samples, however you can use this Telerik Silverlight demo for starters.
Also, switch to RadChartView if you can. Because I would then suggest an easier approach, which is using a Categorical X-Axis and create multiple Y axes. If you go that route, you can do something like this for a DateTimeContinuous (or Categorical) X-axis with multiple Y-axes :
int count = 0;
LineSeries lineSeries = new LineSeries();
lineSeries.CategoryBinding = new PropertyNameDataPointBinding() { PropertyName = "TimeStamp" };
lineSeries.ValueBinding = new PropertyNameDataPointBinding() { PropertyName = "Value" };
lineSeries.VerticalAxis = new LinearAxis()
{
Title = "Title Here"
};
lineSeries.ItemsSource = yourCollection.Values;
//First Y-axis to be placed on the left of X-axis,
//additional Y-axes to be placed on right
if (count > 0 )
{
lineSeries.VerticalAxis.HorizontalLocation = Telerik.Charting.AxisHorizontalLocation.Right;
}
count++;
chartName.Series.Add(lineSeries);
Hope this helps.
Look at this picture from my web application ASP.NET 4.0.
As you can se the lines have a different length. Also there is duplicate x axis entry.
The blue serie has a missing datapoint, the yellow does not.
Question 1:
How do I align them so the x- axis stays the same. Currently im doing this. And make the lines equally long?
Question 2: Is there a way to make the chart interactive so that you can some and hold the cursor on the line to see data from that point, using ASP.NET?
int amountofrows = Convert.ToInt32(dt.Rows[0]["antal"].ToString());
for (int i = 0; i < amountofrows; i++)
{
List<string> xvals = new List<string>();
List<decimal> yvals = new List<decimal>();
string serieName = dt.Rows[i]["doman_namn"].ToString();
Chart1.Series.Add(serieName);
Chart1.Series[i].ChartType = SeriesChartType.Line;
foreach (DataRow dr in dt.Rows)
{
try
{
if (String.Equals(serieName, dr["doman_namn"].ToString(), StringComparison.Ordinal))
{
xvals.Add(dr["ranking_date"].ToString());
yvals.Add(Convert.ToDecimal(dr["ranking_position"].ToString()));
}
}
catch (Exception)
{
throw new InvalidOperationException("Diagrammet kunde inte ritas upp");
}
}
try
{
Chart1.Series[serieName].XValueType = ChartValueType.String;
Chart1.Series[serieName].YValueType = ChartValueType.Auto;
Chart1.Series[serieName].Points.DataBindXY(xvals.ToArray(), yvals.ToArray());
Chart1.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, serieName);
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.Message);
}
}
Chart1.DataBind();
Chart1.Visible = true;
This was the answer!
Thanks for pointing that out JBL!
foreach (System.Web.UI.DataVisualization.Charting.Series serien in Chart1.Series)
{
foreach(System.Web.UI.DataVisualization.Charting.DataPoint dataPoint in serien.Points)
{
if (dataPoint.YValues[0] == 0)
{
dataPoint.IsEmpty = true;
}
}
serien.Sort(PointSortOrder.Ascending,sortBy:("X"));
}
I want to make a survey with asp.net and c#.
It is clear that I can do it. But for the results I want to show them in a pie-chart and histogram.
The questions have only two answers "yes" and "no".
In pie-chart the whole percentage of "yes" and percentage of "no" like %55 "yes", %45 "no".
In histogram I want to show each question which is divided into 2 part ("yes" and "no").
In order to do these (pie-chart and the histogram), should I use component like Telerik?
Or can I do this with drawing library in .NET?
You can use Asp.net Chart controls for representing the data in pie charts......and it also depends on databinding.
if you are using datatable to get data from database then asp.net chart controls much better ...
would you pls take a look at this link for more information....
https://web.archive.org/web/20211020111731/https://www.4guysfromrolla.com/articles/120804-1.aspx
EDIT
This is function getting data from database..
public DataTable GetVisits(System.DateTime startdate , System.DateTime enddate)
{
const string sql = #"SELECT CONCAT(UPPER(SUBSTRING(visit_Status, 1, 1)), SUBSTRING(visit_Status FROM 2)) as Status, COUNT('x') AS Visits
FROM visits
WHERE visit_Date BETWEEN #startdate AND #enddate
GROUP BY visit_Status";
return sqlexecution(startdate, enddate, sql);
}
I am representing this data in stackcolumn chart.
if you want to represent in pie chart , you can change in code
public void DrawMembersvisits(Chart targetchartcontrol, DateTime startdate, DateTime enddate)
{
chart1 = targetchartcontrol;
Series series = null;
Title title;
string area = null;
chart1.ChartAreas.Clear();
chart1.Series.Clear();
chart1.Titles.Clear();
DataTable membervisits = null;
area = "Visits";
chart1.ChartAreas.Add(area);
series = chart1.Series.Add(area);
series.ChartArea = area;
title = chart1.Titles.Add("Member Visits");
title.Alignment = ContentAlignment.MiddleCenter;
title.Font = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular);
title.DockedToChartArea = area;
chart1.Titles.Add("").DockedToChartArea = area;
foreach (Title titles in chart1.Titles)
{
titles.IsDockedInsideChartArea = false;
}
foreach (ChartArea areas in chart1.ChartAreas)
{
areas.Area3DStyle.Enable3D = true;
areas.AxisX.LabelStyle.IsEndLabelVisible = false; areas.AxisX.LabelStyle.Angle = -90;
areas.AxisX.LabelStyle.IsEndLabelVisible = true;
areas.AxisX.LabelStyle.Enabled = true;
}
foreach (Legend legends in chart1.Legends)
{
legends.Enabled = false;
}
foreach (Series serie in chart1.Series)
{
serie.ChartType = SeriesChartType.StackedColumn;
// change here to get the pie charts
// charttypes.ChartType = SeriesChartType.Pie;
// charttypes["LabelStyle"] = "Outside";
// charttypes["DoughnutRadius"] = "30";
// charttypes["PieDrawingStyle"] = "SoftEdge";
// charttypes.BackGradientStyle = GradientStyle.DiagonalLeft;
serie["LabelStyle"] = "Outside";
serie["ColumnDrawingStyle"] = "SoftEdge";
serie["LabelStyle"] = "Top";
serie.IsValueShownAsLabel = true;
serie.BackGradientStyle = GradientStyle.DiagonalLeft;
}
membervisits = visistsdataf.GetVisits(startdate, enddate);
chart1.Series[0].Points.DataBindXY(membervisits.Rows, "Status", membervisits.Rows, "Visits");
foreach (Series chartSeries in chart1.Series)
{
foreach (DataPoint point in chartSeries.Points)
{
switch (point.AxisLabel)
{
case "Accepted": point.Color = Color.Green; break;
case "Refused": point.Color = Color.Red; break;
}
point.Label = string.Format("{0:0} - {1}", point.YValues[0], point.AxisLabel);
}
}
}