Ok this might be a newb question but I've only been programming since a few days.
So I took an example chart from MScharts that is a dynamically created graph during run-time, code is as follows:
private void DynamicChartCreation_Load(object sender, System.EventArgs e)
{
// Create a Chart
Chart1 = new Chart();
// Create Chart Area
ChartArea chartArea1 = new ChartArea();
// Add Chart Area to the Chart
Chart1.ChartAreas.Add(chartArea1);
// Create a data series
Series series1 = new Series();
Series series2 = new Series();
// Add data points to the first series
series1.Points.Add(34);
// Add data points to the second series
series2.Points.Add(14);
// Add series to the chart
Chart1.Series.Add(series1);
Chart1.Series.Add(series2);
// Set chart control location
Chart1.Location = new System.Drawing.Point(16, 48);
// Set Chart control size
Chart1.Size = new System.Drawing.Size(360, 260);
// Add chart control to the form
this.Controls.AddRange(new System.Windows.Forms.Control[] { this.Chart1 });
}
It is a column chart and I want to be able to change the column values dynamically via a combobox.
The question is how do I overwrite the existing old values?
I tried it with Series.point.add like this:
Chart1.Series["Series1"].Points.AddY(comboBox_value);
But instead of applying the value to the first series1 column, it creates another column with the new value right next to it.
Any help would be appreciated, thanks.
I did customize in my project for axix "X", I paste code here, for your reference...
It's working fine in my environment... Please, check it in your environment.
protected void SpiLineAreaChart_Customize(object sender, EventArgs e)
{
foreach (ChartArea area in ((Chart)sender).ChartAreas) {
foreach (Axis axis in area.Axes) {
if (axis.Name.StartsWith("X")) {
foreach (CustomLabel label in axis.CustomLabels) {
counter = counter + 1;
}
lastpoint = counter;
midpoint = counter / 2;
for (int i = 0; i <= counter - 1; i++) {
if (i == 0) {
axis.CustomLabels(i).Text = MonthVal;
} else if (i == midpoint - 1) {
StartDate = StartDate.AddDays(31);
strstartdate = StartDate.ToString("MMM");
axis.CustomLabels(i).Text = strstartdate;
} else if (i == lastpoint - 1) {
StartDate = StartDate.AddDays(31);
strstartdate = StartDate.ToString("MMM");
axis.CustomLabels(i).Text = strstartdate;
} else {
axis.CustomLabels(i).Text = "";
}
}
}
}
}
}
Related
Currently we are having sync issue using TeeChart's Chart Control (.net / c#). In the example shown in attached screenshot, we have two chart controls whose right vertical axis are synced perfectly. Top chart contains area chart while bottom chart contains volume chart. While drawing vertical lines on both charts at specific time interval, we found that vertical lines drawn in both charts having the same value are not synced. Please note that both charts are plotted with same dataset.
We did some r&d on the same issue and our observations states that it is due to different chart styles used in TeeChart's. But as per our client's requirement we need to sync this vertical lines across multiple charts. Any help on this would be greatly appreciated.
The code below align the vertical line for both Series. I think you can do something like that:
public Form1()
{
InitializeComponent();
InitializeChart();
}
Steema.TeeChart.Axis axis1;
DateTime dt;
private void InitializeChart()
{
tChart1 = new Steema.TeeChart.TChart();
tChart1.Dock = DockStyle.Fill;
this.Controls.Add(tChart1);
tChart1.Aspect.View3D = false;
axis1 = new Steema.TeeChart.Axis();
tChart1.Axes.Custom.Add(axis1);
axis1.Horizontal = false;
tChart1.Axes.Right.StartPosition = 0;
tChart1.Axes.Right.EndPosition = 50;
axis1.StartPosition = 51;
axis1.EndPosition = 100;
axis1.OtherSide = true;
axis1.AxisPen.Visible = false;
dt = DateTime.Now;
tChart1.Axes.Bottom.Labels.DateTimeFormat = "dd/MM";
tChart1.Axes.Bottom.Labels.Style = Steema.TeeChart.AxisLabelStyle.Value;
InitializeSeries();
tChart1.Draw();
tChart1.AfterDraw += TChart1_AfterDraw;
}
private void TChart1_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
{
for (int i=0; i<tChart1[0].Count; i++)
{
Point point1, point2;
point1= new Point(tChart1.Axes.Bottom.CalcPosValue(tChart1[0].XValues[i]),tChart1.Axes.Right.CalcYPosValue(tChart1[0].YValues[i]));
point2= new Point(tChart1.Axes.Bottom.CalcPosValue(tChart1[0].XValues[i]), axis1.CalcYPosValue(tChart1[0].YValues[i]));
g.Line(point1, point2);
}
}
private void InitializeSeries()
{
for (int i=0; i<10; i++)
{
if (i==0)
{
new Steema.TeeChart.Styles.Area(tChart1.Chart);
tChart1.Series[i].XValues.DateTime = true;
(tChart1.Series[i] as Steema.TeeChart.Styles.Area).AreaLines.Visible = false;
(tChart1.Series[i] as Steema.TeeChart.Styles.Area).Color = Color.BlueViolet;
(tChart1.Series[i] as Steema.TeeChart.Styles.Area).Transparency = 20;
Random rnd = new Random();
for (int j=0; j<10; ++j)
{
tChart1.Series[i].Add(dt, rnd.Next(100));
dt = dt.AddDays(1);
}
tChart1.Series[i].Marks.Visible = false;
tChart1.Series[i].VertAxis = Steema.TeeChart.Styles.VerticalAxis.Right;
}
else if(i==1)
{
new Steema.TeeChart.Styles.Volume(tChart1.Chart);
tChart1.Series[i].DataSource = tChart1.Series[i - 1];
tChart1.Series[i].CustomVertAxis = axis1;
}
}
}
There are 2 lists called listversion & MIN_list. Using values of these list I have created a line chart. Everything is work fine. But I am wondering whether it is possible to add a data table with legend keys in to the chart like MS Excel.
chart.Series.Clear();
chart.ChartAreas[0].AxisX.Title = "Version";
chart.ChartAreas[0].AxisX.TitleFont = new System.Drawing.Font("Arial", 12, FontStyle.Regular);
chart.ChartAreas[0].AxisY.Title = "Time";
chart.ChartAreas[0].AxisY.TitleFont = new System.Drawing.Font("Arial", 12, FontStyle.Regular);
Series MIN = chart.Series.Add("Minimum");
MIN.Points.DataBindXY(listVersion, MIN_list[j]);
MIN.ChartType = SeriesChartType.Line;
MIN.Color = Color.Red;
MIN.BorderWidth = 3;
I am looking forward to something like this
If it is possible How can I do it ?
Thank you.
Yes you can do that:
Here are the steps I took:
First we disable the original Legend as it can't be manipulated the way we need to..:
chart1.Legends[0].Enabled = false;
Now we create a new one and a shortcut reference to it:
chart1.Legends.Add(new Legend("customLegend"));
Legend L = chart1.Legends[1];
Next we do some positioning:
L.DockedToChartArea = chart1.ChartAreas[0].Name; // the ca it refers to
L.Docking = Docking.Bottom;
L.IsDockedInsideChartArea = false;
L.Alignment = StringAlignment.Center;
Now we want to fill in one line for headers and one line per series.
I use a common function for both and pass in a flag to indicate whether the headers (x-values) or the cell data (y-values) should be filled in. Here is how I call the function:
addValuesToLegend(L, chart1.Series[0], false);
foreach (Series s in chart1.Series) addValuesToLegend(L, s, true);
Note that for this to work we need a few preparations in our Series:
We need to set the Series.Colors explicitly or else we can't refer to them.
I have added a format string to the Tag of each series; but maybe you find a better solution that avoids hard-coding the format for the header..
So here is the function that does all the filling and some styling:
void addValuesToLegend(Legend L, Series S, bool addYValues)
{
// create a new row for the legend
LegendItem newItem = new LegendItem();
// if the series has a markerstyle we show it:
newItem.MarkerStyle = S.MarkerStyle ;
newItem.MarkerColor = S.Color;
newItem.MarkerSize *= 2; // bump up the size
if (S.MarkerStyle == MarkerStyle.None)
{
// no markerstyle so we just show a colored rectangle
// you could add code to show a line for other chart types..
newItem.ImageStyle = LegendImageStyle.Rectangle;
newItem.BorderColor = Color.Transparent;
newItem.Color = S.Color;
}
else newItem.ImageStyle = LegendImageStyle.Marker;
// the rowheader shows the marker or the series color
newItem.Cells.Add(LegendCellType.SeriesSymbol, "", ContentAlignment.MiddleCenter);
// add series name
newItem.Cells.Add(LegendCellType.Text, addYValues ? S.Name : "",
ContentAlignment.MiddleLeft);
// combine the 1st two cells:
newItem.Cells[1].CellSpan = 2;
// we hide the first cell of the header row
if (!addYValues)
{
newItem.ImageStyle = LegendImageStyle.Line;
newItem.Color = Color.Transparent;
newItem.Cells[0].Tag = "*"; // we mark the 1st cell for not painting it
}
// now we loop over the points:
foreach (DataPoint dp in S.Points)
{
// we format the y-value
string t = dp.YValues[0].ToString(S.Tag.ToString());
// or maybe the x-value. it is a datatime so we need to convert it!
// note the escaping to work around my european locale!
if (!addYValues) t = DateTime.FromOADate(dp.XValue).ToString("M\\/d\\/yyyy");
newItem.Cells.Add(LegendCellType.Text, t, ContentAlignment.MiddleCenter);
}
// we can create some white space around the data:
foreach (var cell in newItem.Cells) cell.Margins = new Margins(25, 20, 25, 20);
// finally add the row of cells:
L.CustomItems.Add(newItem);
}
To draw the borders around the cells of our legend table we need to code the PrePaint event:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
LegendCell cell = e.ChartElement as LegendCell;
if (cell != null && cell.Tag == null)
{
RectangleF r = e.ChartGraphics.GetAbsoluteRectangle(e.Position.ToRectangleF());
e.ChartGraphics.Graphics.DrawRectangle(Pens.DimGray,Rectangle.Round(r));
// Let's hide the left border when there is a cell span!
if (cell.CellSpan != 1)
e.ChartGraphics.Graphics.DrawLine(Pens.White,
r.Left, r.Top+1, r.Left, r.Bottom-1);
}
}
You can add more styling although I'm not sure if you can match the example perfectly..
Initially I display data in cells as user scrolls I need to load more data in DataGridView.
I am using DataGridView CellPainting for drawing lines.
When I start scrolling in datagridview the cells get overlapped and it completely changes the output.
public partial class Display : Form
{
public Display()
{
InitializeComponent();
LoadData();
}
// To create the rows and columns and fill some data
private void LoadData()
{
int columnSize = 10;
DataGridViewColumn[] columnName = new DataGridViewColumn[columnSize];
for (int index = 0; index < columnSize; index++)
{
columnName[index] = new DataGridViewTextBoxColumn();
if (index == 0)
{
columnName[index].Name = "Name";
columnName[index].HeaderText = "Name";
}
else
{
columnName[index].Name = (index).ToString();
columnName[index].HeaderText = (index).ToString();
}
columnName[index].FillWeight = 0.00001f;
columnName[index].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
dataGridView1.Columns.Add(columnName[index]);
}
for (int rowIndex = 0; rowIndex < columnSize; rowIndex++)
{
dataGridView1.Rows.Add((rowIndex + 1).ToString());
dataGridView1.Rows[rowIndex].HeaderCell.Value = (rowIndex + 1).ToString();
}
}
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
Rectangle rectPos1 = this.dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
Pen graphPen = new Pen(Color.Red, 1);
Graphics graphics = this.dataGridView1.CreateGraphics();
Point[] points =
{
new Point(rectPos1.Left , rectPos1.Bottom),
new Point(rectPos1.Right, rectPos1.Bottom),
new Point(rectPos1.Right, rectPos1.Top)
};
graphics.DrawLines(graphPen, points);
e.PaintContent(rectPos1);
e.Handled = true;
}
}
Sample Download Link
Which I have shown in the below image
How can i avoid it please help me solve this issue.
A couple of issues. First and foremost, you should almost always use the provided Graphics object you get from the PaintEventArgs. CreateGraphics is a temporary canvas that gets easily erased. One of the parameters you get is the CellBounds rectangle, so you can use that. Your lines are actually drawing outside of the rectangle, and you aren't clearing the previous contents, so your code should look something like this:
Rectangle rectPos1 = e.CellBounds;
e.Graphics.FillRectangle(Brushes.White, rectPos1);
Graphics graphics = e.Graphics; // this.dataGridView1.CreateGraphics();
Point[] points =
{
new Point(rectPos1.Left , rectPos1.Bottom - 1),
new Point(rectPos1.Right - 1, rectPos1.Bottom - 1),
new Point(rectPos1.Right - 1, rectPos1.Top)
};
graphics.DrawLines(Pens.Red, points);
e.PaintContent(rectPos1);
e.Handled = true;
I have a charting application that has an overlay function which reassigns the 'from' chart series to the 'to' chart using this code :
chTo.Series.Add(chFrom.Series[s]); //Reassign series to new chart
chTo.Legends.Add(chFrom.Legends[s]); //Reassign legend to new chart
Works great. However, I am trying to implement tooltips for the legends and am running into an issue where only the first legend in the chart will show tooltips. When I do a hittest only the first legend is recognized. All subsequent legends, while visible on the chart, aren't 'seen' to the hittest method. I'm thinking this is why the tooltips aren't showing as there is no object to trigger the mouseover event for the tooltip.
I have been unable to find a way to 'expand' the legend area (as detected by the hittest method) to make this work.
Does anyone have any ideas? Thanks!
Responding to King King --
The original legend is created in the same method as the chart thus:
//Create the series legend
chartSel.Series[ySeries.Name].ChartArea = "ChartArea1";
chartSel.Legends.Remove(chartSel.Legends.FindByName("Legend1"));
chartSel.Legends.Add(ySeries.Name);
chartSel.Legends[0].Name = ySeries.Name;
//Format the series legend
chartSel.Legends[ySeries.Name].Docking = Docking.Right;
chartSel.Legends[ySeries.Name].DockedToChartArea = "ChartArea1";
chartSel.Legends[ySeries.Name].Alignment = StringAlignment.Near;
chartSel.Legends[ySeries.Name].IsDockedInsideChartArea = false;
chartSel.Legends[ySeries.Name].LegendStyle = LegendStyle.Table; //.Row;
chartSel.Legends[ySeries.Name].TableStyle = LegendTableStyle.Tall;
chartSel.Legends[ySeries.Name].IsEquallySpacedItems = false;
chartSel.Legends[ySeries.Name].Font = new Font("Segoe UI", 7, FontStyle.Bold);
//chartSel.Legends[ySeries.Name].TextWrapThreshold = 17; // 19;
chartSel.Legends[ySeries.Name].Position.Auto = false;
chartSel.Legends[ySeries.Name].Position.X = 80;
chartSel.Legends[ySeries.Name].Position.Y = 2;
chartSel.Legends[ySeries.Name].Position.Width = 18;
chartSel.Legends[ySeries.Name].Position.Height = 12;
//Format series data point value cell
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ""));
chartSel.Legends[ySeries.Name].CellColumns[0].Alignment = ContentAlignment.MiddleLeft; //.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[0].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(10, 10, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[0].MinimumWidth = 500;
chartSel.Legends[ySeries.Name].CellColumns[0].MaximumWidth = 500;
chartSel.Legends[ySeries.Name].CellColumns[0].BackColor = Color.FromArgb(120, chartSel.Series[ySeries.Name].Color);
//Format legend cell spacer
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ""));
chartSel.Legends[ySeries.Name].CellColumns[1].Alignment = ContentAlignment.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[1].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 0, 0);
chartSel.Legends[ySeries.Name].CellColumns[1].MinimumWidth = 25;
chartSel.Legends[ySeries.Name].CellColumns[1].MaximumWidth = 25;
chartSel.Legends[ySeries.Name].CellColumns[1].BackColor = Color.Black;
//Format series title cell
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ySeries.Name));
chartSel.Legends[ySeries.Name].CellColumns[2].Alignment = ContentAlignment.MiddleLeft;
chartSel.Legends[ySeries.Name].CellColumns[2].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[2].MinimumWidth = 1475; //1500;
chartSel.Legends[ySeries.Name].CellColumns[2].MaximumWidth = 1475; //1500;
chartSel.Legends[ySeries.Name].CellColumns[2].ToolTip = ySeries.Name;
After the series and legends have been reassigned (using the code in my original post) I then set the legend values based on the cursor position located by the following hittest in response to a mouse-down event:
pt = activePanel.PointToClient(Control.MousePosition);
ch = activePanel.GetChildAtPoint(pt) as Chart;
if (ch != null)
{
HitTestResult ht = ch.HitTest(e.X, e.Y, false);
if (ht.ChartElementType == ChartElementType.PlottingArea)
{
SetLegendValueText(ht, ch);
}
}
private void SetLegendValueText(HitTestResult ht, Chart ch)
{
//Get the datapoint 'x' index value
int dpIndex = 0;
if (ht != null)
{
switch (ht.ChartElementType)
{
case ChartElementType.DataPoint: //Cursor is on a series line
DataPoint dp = ht.Object as DataPoint;
if (dp != null)
{
dpIndex = ht.PointIndex;
}
break;
case ChartElementType.PlottingArea: //Cursor is somewhere in the plot area of the chart
dpIndex = (int)ht.ChartArea.CursorX.Position;
break;
}
}
//Set legend value and legend tooltip
for (int x = 0; x < ch.Legends.Count; x++) //foreach (Series s in ch.Series)
{
if (dpIndex > 0)
{
ch.Legends[x].Name = "Legend_" + x;
ch.Legends[x].CellColumns[0].Text = ch.Series[x].Points[dpIndex - 1].YValues[0].ToString();
ch.Legends[x].CellColumns[0].ToolTip = ch.Legends[x].CellColumns[0].Text;
}
}
}
So, I end up with the legends looking the way I want them, but the tooltips only show for the first legend item. I've tried to do custom items as well. With them I get the tooltips, but I lose the formatting. This has been driving me crazy for weeks (off and on) and I would really like to move on to other issues. Clearly (to me anyway), I am not doing something right simply because I don't know everything there is to know about the charts, and the MSChart Samples are of very limited benefit.
I'd be most grateful if I could be pointed in the right direction.
I have a win forms chart control with a RangeBar type chart where I add Series and DataPoints as follows:
public void AddSeries(List<Machine> machines)
{
string mID="";
chart.ChartAreas[0].AxisX.Minimum =0;
chart.ChartAreas[0].AxisX.Maximum =machines.Count+1;
int x = 1;
foreach (var m in machines)
{
if (x < 4)
{
mID = m.idMachine.ToString();
chart.Series.Add(new Series(mID));
chart.Series[mID].YValuesPerPoint = 2;
chart.Series[mID].Color = Color.Magenta;
chart.Series[mID].ChartType = SeriesChartType.RangeBar;
chart.Series[mID]["PointWidth"] = "0.7";
chart.Series[mID].IsVisibleInLegend = false;
chart.Series[mID].AxisLabel = m.MachineNo + "_" + m.idMachine;
chart.Series[mID]["DrawSideBySide"] = "true";
DateTime dt = new DateTime(2010, 1, 6);
chart.Series[mID].Points.AddXY(x, dt.ToOADate(), dt.AddDays(1).ToOADate());
}
x++;
}
}
My chart then looks as follows:
What I want is the DataPoints of Series P01_67 and P03_69 to be correctly aligned (in the middle of the series line) as with the Series P02_68. Any ideas how I can do this? Thanks!
If you want them aligned you need to set this property
chart.Series[mID]["DrawSideBySide"] = "false";
But then your series will not be drawn side by site and will overlap
Or you can try to remove the empty series from the Chart. ( Then you will need to take care of the Labels )
Eg:-
Check Here for more information