C# Update chart values - c#

How do I update values that have already been created?
Like, I have this chart:
And then I want to update the chart values with other values. I've already tried many things but I don't manage to make it work...
My code:
int ratio = (int)PlayerStats.Kills - PlayerStats.Deaths;
string[] seriesArray = { "Kills", "Assists", "Deaths", "MVPS" , "Headshots", "Ratio" };
int[] pointsArray = { PlayerStats.Kills, PlayerStats.Assists, PlayerStats.Deaths, PlayerStats.MVPS, PlayerStats.Headshots, ratio };
this.chart1.Titles.Add("Statistics Chart");
for (int i = 0; i < seriesArray.Length; i++)
{
Series series = series = this.chart1.Series.Add(seriesArray[i]);
series.Points.Add(pointsArray[i]);
}
chart1.ChartAreas[0].AxisX.Enabled = AxisEnabled.False;
chart1.ChartAreas[0].AxisY.Enabled = AxisEnabled.False;
double realRatio = Math.Round((double)PlayerStats.Kills / PlayerStats.Deaths, 2);
double hsRatio = Math.Round((double)PlayerStats.Headshots / PlayerStats.Kills, 2);
label6.Text = PlayerStats.Kills.ToString();
label7.Text = PlayerStats.Assists.ToString();
label8.Text = PlayerStats.Deaths.ToString();
label11.Text = PlayerStats.MVPS.ToString();
label10.Text = String.Format("{0:P2}", hsRatio);
label9.Text = realRatio.ToString();
When I try to run the function a second time (to update the values) it gives me a exception: (inside the loop, first line)
Additional information: There is already a chart element with the name 'Kills' in 'SeriesCollection'.
I've managed to that that specific function (chart1.Series.Add(...)) only called once on the startup, but then the other function inside the loop gave me exceptions too.
Well, I hope that you understand my english :P

Are you clearing out the chart elements before looping through to add them again? Something like
this.chart1.Series.Clear();

Related

Chart custom labels not appearing on X axis

I've tried a couple of the solutions posted and none of them seem to work.
salesTitle.Text = chartTitle;
this.chtSales.Series.Clear();
this.chtSales.Titles.Add(salesTitle);
this.chtSales.ChartAreas[0].AxisX.Interval = 1;
// Try for Custom Labels
foreach (string monthName in monthArray)
{
string sMonthName = monthName;
sMonthName = char.ToUpper(sMonthName[0]) + sMonthName.Substring(1);
CustomLabel monthLabel = new CustomLabel();
monthLabel.Text = sMonthName;
this.chtSales.ChartAreas[0].AxisX.CustomLabels.Add(monthLabel);
}
I think I may need to add some parameters to the CustomLabel function call but I can't work out what they should be?
On the bright side the data is showing correctly.
Thanks for the Links Rufus, indeed the FromPosition and ToPosition needed to be set. For anyone else battling the same issue, code below works though I am adding Month names to the x AXIS.
// Try for Custom Labels
for(int i = 0; i < monthArray.Length; i++)
{
string sMonthName = monthArray[i];
sMonthName = char.ToUpper(sMonthName[0]) + sMonthName.Substring(1);
CustomLabel lblMonth = new CustomLabel();
lblMonth.FromPosition = i;
lblMonth.ToPosition = i + 1;
lblMonth.Text = sMonthName;
this.chtAccum.ChartAreas[0].AxisX.CustomLabels.Add(lblMonth);
}

Adding values to a list box from array WITH text

I'm adding values from a file to an array and then adding those values to a list box in a form. I have methods performing calculations and everything is working great, but I'd like to be able to use a loop and insert the year prior to the values I am inserting into the list box. Is there a way to use the .Add method and include a variable which will be changing in this? Something like populationListbox.Items.Add(i, value); if i is my loop counter? Code is below so I'd like first line in the list box to have the year I specify with my counter, followed by the population. Like this, "1950 - 151868". As of now it only displays the value 151868. Thanks!
const int SIZE = 41;
int[] pops = new int[SIZE];
int index = 0;
int greatestChange;
int leastChange;
int greatestYear;
int leastYear;
double averageChange;
StreamReader inputFile;
inputFile = File.OpenText("USPopulation.txt");
while (!inputFile.EndOfStream && index < pops.Length)
{
pops[index] = int.Parse(inputFile.ReadLine());
index++;
}
inputFile.Close();
foreach (int value in pops)
{
**populationListbox.Items.Add(value);**
}
greatestChange = greatestIncrease(pops) * 1000;
leastChange = leastIncrease(pops) * 1000;
averageChange = averageIncrease(pops) * 1000;
greatestYear = greatestIncreaseyear(pops);
leastYear = leastIncreaseyear(pops);
greatestIncreaselabel.Text = greatestChange.ToString("N0");
leastIncreaselabel.Text = leastChange.ToString("N0");
averageChangelabel.Text = averageChange.ToString("N0");
greatestIncreaseyearlabel.Text = greatestYear.ToString();
leastIncreaseyearlabel.Text = leastYear.ToString();
Like this?
int i = 1950;
foreach (int value in pops)
{
populationListbox.Items.Add(i.ToString() + " - " + value);
i++;
}
Your life will be a lot easier if you stop trying to program C# as if it were 1980s C and use the power of it's Framework:
var pops = File.ReadLines("USPopulation.txt").Select(int.Parse);
populationListbox.Items.AddRange(pops.Select((p,i) => $"{i} - {p}"));

Chart: Red X Error when trying to display multiple series in an indexed chart

I have an Issue with C# charting.
This is using System.Windows.Forms.DataVisualization.Charting
My series are configured to be indexed as such:
{
Name = name,
Color = color,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType =
System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine
};
I have 3 series in this chart
I am adding data to the series as it arrives via an Event Handler
for (int i = 0; i < samples; i++)
{
double time_point = DData[i * 4]
chart1.Series[0].Points[lastSample + i].XValue = time_point;
chart1.Series[0].Points[lastSample + i].YValues[0] = DData[i * 4 + 1];
chart1.Series[1].Points[lastSample + i].XValue = time_point;
chart1.Series[1].Points[lastSample + i].YValues[0] = DData[i * 4 + 2];
chart1.Series[2].Points[lastSample + i].XValue = time_point;
chart1.Series[2].Points[lastSample + i].YValues[0] = DData[i * 4 + 3];
}
lastSample = (lastSample + samples) % maxPlotPoints;
chart1.Invalidate();
The issue is as follows:
If I turn on each series individually (via chart1.Series[0].Enabled), they all work fine
If I turn on more than 1 series at a time, a big red X appears instead oft eh chart, and I have to restart the application to resume streaming charts. This either happens immediately or after a few seconds.
If I set time_point to some other number, like 0, this issue doesn't happen, and all 3 charts can be displayed simultaneously
Next, I understand that this happens when each series has a different X-value for the same Point[] location. But I am explicitly setting all 3 series to use the same exact time_point
My next assumption was that the event handler was executing the tread before the previous thread finishes.
So I added a lock around the graphing call, it did not help
private Object thisLock = new Object();
lock (thisLock)
{
}
MY questions are:
Does anyone know if there is another reason why this may be caused?
Is it at all possible to use just the X-indexes from the first series for the chart but to display all 3 series simultaneuously?
EDIT: This fix did not work, the problem disappeared, then came back after a while
The comment above by TaW helped me fix this issue.
This really seems to be a problem with Microsoft charts
I originally though it was due to EventHandler firing multiple times before it finishes, and I opted out to use a Queue that would be filled with an EventHandler and Dequeued on a separate thread.
The problem was still there.
The solution to the problem was to simply set IsXValueIndexed to false
then to set it to true before drawing the chart
Below is the code used
In case anyone is wondering what this does:
It graphs 3 Values simultaneously on the chart with increasing time.
The chart updates from left to right instead of scrolling (think EKG machine in a hospital)
There is a fixed total number of points that is set to maxPlotPoints
The EventHandler receives some Data packet (made up here) that contains a certain number of points which = samplePoints.
It constructs a DataPoints struct and adds it to a queue
A separate thread MainTask dequeues an element from DataPoints and uses it to populate a 100 point section of each of the three series. That section then increments to the next 100 points, and so on.
This is useful if you want to continuously chart a fixed number of points
Unfortunately, I can't seem to mark the answer above from TaW as correct because it's in a comment. But I would be happy to try if you can explain how to do this. Thanks
private struct DataPoint
{
public double timePoint;
public double X;
public double Y;
public double Z;
}
private struct DataPoints
{
public DataPoints(int samples)
{
dataPoints = new DataPoint[samples];
}
public DataPoint[] dataPoints;
}
Queue queue = new Queue();
int lastSample = 0;
static int maxPlotPoints = 1000;
static int samplePoints = 100;
public ChartForm(UdpMgrControl RefUdpMgr, byte GyroChartNum, string ConfigFile)
{
for ( Row = 0; Row < 3; Row++)
{
var series1 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = name,
Color = Color.Blue,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine
};
//Fill in chart with blank points
for (int i = 0; i < maxPlotPoints; i++)
{
series1.Points.AddY(0);
}
chart1.Series.Add(series1);
}
chart1.Series[0].Enabled = true;
chart1.Series[1].Enabled = true;
chart1.Series[2].Enabled = true;
running = true;
Thread main_thread = new Thread(new ThreadStart(this.MainTask));
}
private void OutputHandler(byte[] DData)
{
if (running)
{
DataPoints data_element = new DataPoints(samplePoints);
for (int i = 0; i < samplePoints; i++)
{
data_element.dataPoints[i].X = DData[i].X;
data_element.dataPoints[i].Y = DData[i].Y;
data_element.dataPoints[i].Z = DData[i].Z;
data_element.dataPoints[i].timePoint = DData[i].timePoint;
}
queue.Enqueue(data_element);
}
}
private void MainTask()
{
while (running)
{
try
{
if (queue.Count > 0)
{
chart1.Series[0].IsXValueIndexed = false;
chart1.Series[1].IsXValueIndexed = false;
chart1.Series[2].IsXValueIndexed = false;
DataPoints data_element = (DataPoints)queue.Dequeue();
for (int i = 0; i < data_element.dataPoints.Length; i++)
{
chart1.Series[0].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[0].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].X;
chart1.Series[1].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[1].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].Y;
chart1.Series[2].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[2].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].Z;
}
//Adjust the next location to end of first
lastSample = (lastSample + samples) % maxPlotPoints;
chart1.Series[0].IsXValueIndexed = true;
chart1.Series[1].IsXValueIndexed = true;
chart1.Series[2].IsXValueIndexed = true;
GC_UpdateDataGrids();
chart1.Invalidate();
}
else
{
Thread.Sleep(10);
}
}
catch (Exception error)
{
LogErrors.AddErrorMsg(error.ToString());
}
}
}

Input string was not in a correct format - calculating total of labels

Basically the first for statement below creates a list of testvalue labels depending on the user input.
The second for statement is supposed to work out the total of the dynamic labels created but when i play it i get an error saying "Input string was not in a correct format." relating to tots += double.Parse(value[p].ToString()); Any help would be appreciated. Thanks
ArrayList value = new ArrayList();
int p =0;
for (int i = 0; i < weight.Count; i++)
{
Label test = new Label();
System.Drawing.Point l8 = new System.Drawing.Point(440, 48 + s);
test.Location = l8;
value.Add(test);
k += 35;
Label l2 = testpercent1[i] as Label;
Double.TryParse(l2.Text.Trim(), out label2);
Label l = testpercent2[i] as Label;
Double.TryParse(l.Text.Trim(), out label1);
Double testvalue = Math.Round(((label1 * .3) + (label2 * .4)));
test.Text = testvalue.ToString();
}
Double total = 0;
for (int p = 0; p < value.Count; p++)
{
tots += double.Parse(value[p].ToString());
}
tots += double.Parse(((Label)value[p]).Text);
value[p] is of type Label. If you want to get the text value of the label, you should use value[p].Text.
Double total = 0;
for (int p = 0; p < value.Count; p++)
{
tots += double.Parse(((Label)value[p]).Text);
}
Another thing, you should use List<Label> for value instead of ArrayList.
You are trying to parse the ToString() of a label. Were you instead looking to parse some property of the label?
When you call value[p], whats being returned is a on object of type Label. If you wanted to parse the text of the label your code would instead be
tots += double.Parse(((Label)value[p]).Text);
Storing your data in labels is a very bad idea. Use a data structure that is better suited for this purpose like an array or a list of doubles. Only use the lables for displaying the data.
double[] values = new double[N];
Label[] lables = new Label[N]; // Only for display!
// Calculate (just as an example)
double result = values[i] + values[k];
// NOT LIKE THIS!
double result = Double.Parse(labels[i]) + Double.Parse(labels[k]);
It's obvious you are adding a control to the ArrayList. So this don't work:
tots += double.Parse(value[p].ToString());
I reccomend you to do:
value.Add(test.Text);
Then:
tots += double.Parse(value[p]);
PS:
Please use a List<string> instead of an ArrayList.

Force a gap between points on the x axis (MS .Net Chart Controls, Column Chart)

I have a column chart with multiple series each containing multiple points. Currently the columns all touch each other. I want to force a gap between each column. How can I achieve this?
I found that applying a PointWidth (Chart1.Series[seriesName]["PointWidth"] = (0.6).ToString();) gives me a separation between the x value groups but not between each series point in an individual group (which I need). Using an empty spacer series as suggested elsewhere does not solve the problem.
I am using .Net 4, VS 2010, Web Application. My chart code follows:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
namespace WebApplication1
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
Chart1.ChartAreas.Add("Default");
Chart1.ChartAreas["Default"].BackColor = Color.White;
Chart1.ChartAreas["Default"].BackSecondaryColor = Color.AliceBlue;
Chart1.ChartAreas["Default"].BackGradientStyle = GradientStyle.TopBottom;
Chart1.BackColor = Color.AliceBlue;
Chart1.BackSecondaryColor = Color.White;
Chart1.BackGradientStyle = GradientStyle.TopBottom;
Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
var colors = new List<Color>(GetSystemColors().Where(c=>c.Name.StartsWith("Dark")));
var rng = new Random();
var start = rng.Next(0, colors.Count - 1);
for (var c = start; c < start + 6; c++)
{
var color = colors[c % colors.Count];
Chart1.Series.Add(color.Name);
Chart1.Series[color.Name].BorderColor = color;
Chart1.Series[color.Name].BorderWidth = 1;
Chart1.Series[color.Name].Color = Color.FromArgb((int)(255 * .7), color);
Chart1.Series[color.Name].BackSecondaryColor = Color.White;
Chart1.Series[color.Name].BackGradientStyle = GradientStyle.TopBottom;
for (var year = DateTime.Now.AddYears(-5).Year; year < DateTime.Now.Year; year++)
Chart1.Series[color.Name].Points.Add(new DataPoint(year, rng.Next(0, 20)));
Chart1.Series[color.Name]["PointWidth"] = (0.6).ToString();
//Chart1.Series.Add("Spacer:" + color.Name);
//Chart1.Series["Spacer:" + color.Name]["PointWidth"] = (0.6).ToString();
}
Chart1.Legends.Add("Default");
}
static IEnumerable<Color> GetSystemColors()
{
Type type = typeof(Color);
return type.GetProperties().Where(info => info.PropertyType == type).Select(info => (Color)info.GetValue(null, null));
}
}
}
I had the devils own job reproducing your situation. I wanted to help because I thought I could learn something by doing so, but I needed your markup, or better yet, the whole solution! I struggled through the "Error executing child request for ChartImg.axd" when I tried a simple page with a chart. I discovered I needed to add a handler in the config. I then fought through the failure to load assembly System.Web.DataVisualization because the handler element I copied referenced the 3.5 DataVisualization assembly so I changed that to 4.0 and finally saw a graph. What a job THAT was!
The reason your spacer series is not creating a gap is because there are no values in that series. Notice the last two lines of code below, that add zero values to the spacer series. This produces the desired gap between the other series, but you will also find the spacer series listed in your legend if you have one, which is ugly to say the least.
for (var c = start; c < start + 6; c++)
{
var color = colors[c % colors.Count];
var seriesName = "Series "+ c;//color.Name);
Chart1.Series.Add(seriesName);
Chart1.Series[seriesName].BorderColor = color;
Chart1.Series[seriesName].BorderWidth = 1;
Chart1.Series[seriesName].Color = Color.FromArgb((int)(255 * .7), color);
Chart1.Series[seriesName].BackSecondaryColor = Color.FromArgb((int)(255 * .2), color);
Chart1.Series[seriesName].BackGradientStyle = GradientStyle.TopBottom;
for (var year = DateTime.Now.AddYears(-5).Year; year < DateTime.Now.Year; year++)
Chart1.Series[seriesName].Points.Add(new DataPoint(year, rng.Next(0, 20)));
Chart1.Series[seriesName]["PointWidth"] = (0.6).ToString();
seriesName = "Spacer:" + seriesName;
Chart1.Series.Add(seriesName);
Chart1.Series[seriesName]["PointWidth"] = (0.6).ToString();
for (var year = DateTime.Now.AddYears(-5).Year; year < DateTime.Now.Year; year++)
Chart1.Series[seriesName].Points.Add(new DataPoint(year, 0));
}
You can set the legend text to a space (NB. empty string is ignored and legend text is not set) as follows, but the Legend will still show these spacer series.
Chart1.Series[seriesName].LegendText = " ";
If you're lucky, you won't need to show the legend, or you can set the spacer series colours to the same colour as the legend background and the legend text to spaces. This results in a double-spacing look in the Legend which is likely to be acceptable.
you can control whether or not the series shows up in the legend by setting the "IsVisibleInLegend" value to false

Categories