My goal is to create a chart that will sit inside of a panel restricting it's size.
I managed to achieve this some time ago but today I noticed that the chart was growing inside of the panel, not allowing the data to be seen.
I have attached a picture bellow which should help understand the issue.
UPDATE
I noticed that if I rmeove 'bottom' from the Anchor property of the panel the chart does not exceed the parent panel but it does not increase with the change of the form which is what I'm looking for.
I also noticed that there was also another chart on the form that was exceeding the parent form, this time the chart would extend to the right not allowing to see the data.
This is the code that generates this second chart and places is inside of the parent panel.
panel_chart.Controls.Clear();
chart1 = new Chart();
chart1.MouseMove += chart1_MouseMove;
chart1.ChartAreas.Add(new ChartArea("chartArea1"));
chart1.Series.Clear();
chart1.Titles.Clear();
var serieOEE = new Series("OEE");
serieOEE.ChartType = SeriesChartType.Line;
serieOEE.XValueType = ChartValueType.String;
var serieProd = new Series("Prod");
serieProd.ChartType = SeriesChartType.Column;
serieProd.XValueType = ChartValueType.String;
var serieDisp = new Series("Disp");
serieDisp.ChartType = SeriesChartType.Column;
serieDisp.XValueType = ChartValueType.String;
var serieQual = new Series("Qual");
serieQual.ChartType = SeriesChartType.Column;
serieQual.XValueType = ChartValueType.String;
DateTime DataReg = DateTime.MinValue;
List<AreaOEE> listaChart = new List<AreaOEE>();
foreach (var item in ListaGrafico) //listaOEE
{
if (item.Designacao == DesignacaoLista)
{
listaChart.Add(item);
}
}
listaChart = listaChart.OrderBy(a => a.IDReg).ToList();
DateTime DataUltimoReg = DateTime.MinValue;
int j = 0;
foreach (var item in listaChart)
{
string HoraGraf = Convert.ToDateTime(item.Hora).ToString("HH:mm");
if (j == 0 || j == listaChart.Count - 1 ||
Math.Abs(Convert.ToDateTime(item.Hora).Subtract(DataUltimoReg).TotalMinutes) >= 30)
{
serieOEE.Points.AddXY(HoraGraf, item.OEE);
serieProd.Points.AddXY(HoraGraf, item.Produtividade);
serieQual.Points.AddXY(HoraGraf, item.Qualidade);
serieDisp.Points.AddXY(HoraGraf, item.Disponibilidade);
DataUltimoReg = Convert.ToDateTime(item.Hora);
if (j == listaChart.Count - 2)
{
break;
}
}
j++;
}
//Adicionar o ultimo
foreach (var item in listaOEE)
{
if (item.Designacao == DesignacaoLista)
{
string sHora = "";
try
{
sHora = item.Hora.Substring(1, 5);
}
catch (Exception ex)
{
string sEx = ex.Message;
}
foreach (var itemOee in serieOEE.Points)
{
if (itemOee.AxisLabel == sHora)
{
itemOee.YValues[0] = item.OEE;
}
}
foreach (var itemP in serieProd.Points)
{
if (itemP.AxisLabel == sHora)
itemP.YValues[0] = item.Produtividade;
}
foreach (var itemD in serieDisp.Points)
{
if (itemD.AxisLabel == sHora)
itemD.YValues[0] = item.Disponibilidade;
}
foreach (var itemQ in serieQual.Points)
{
if (itemQ.AxisLabel == sHora)
itemQ.YValues[0] = item.Qualidade;
}
}
}
chart1.Series.Add(serieProd);
chart1.Series.Add(serieQual);
chart1.Series.Add(serieDisp);
chart1.Series.Add(serieOEE);
serieOEE.BorderWidth = 4;
chart1.ChartAreas[0].AxisX.LabelStyle.Angle = 90;
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = 140;
chart1.Legends.Clear();
chart1.Legends.Add(serieOEE.Legend);
chart1.Titles.Add(DesignacaoLista + " " + DataTitulo.ToString("dd-MM HH:mm"));
chart1.Titles[0].Font = new Font("Arial", 13, FontStyle.Bold);
chart1.Visible = true;
chart1.Dock = DockStyle.Fill;
panel_chart.Controls.Add(chart1);
you can change the size of Chart with Chart.Size:
Chart1.Size = new Size(1000, 200); //1000px * 200px
Related
I want to add a dynamic micro chart to my application but it doesn't work. After a call from a method a value gets added and it makes a completely new micro chart for my chart to have the new values, but the change isn't visible in the app. So the old Values stayed and there is no new one. Thanks for helping me.
WeightList = new List<float>();
WeightList.Add(0);
WeightList.Add((float)74.3);
entries = new ChartEntry[30];
SyncArray();
private void SyncArray()
{
if (WeightList.Count != entries.Length)
{
entries = new ChartEntry[WeightList.Count];
}
for (int i = 0; i <= WeightList.Count - 1; i++)
{
if (i == WeightList.Count - 1 || i == 0)
{
entries[i] = new ChartEntry(WeightList[i]) { Label = "" + i, ValueLabel = "" + WeightList[i] };
}
else
{
entries[i] = new ChartEntry(WeightList[i]) { Label = "" + i };
}
}
chart = new LineChart() { Entries = entries, BackgroundColor = SKColors.Transparent };
Chart = chart;
}
public LineChart Chart
{
get => chart;
set => SetProperty(ref chart, value);
}
public float Weight
{
get => weight;
set
{
weight = value;
WeightList.Add(weight);
SyncArray();
}
}
Credits: #Jason
What to change:
private void SyncArray()
{
if (WeightList.Count != entries.Length)
{
entries = new ChartEntry[WeightList.Count];
}
for (int i = 0; i <= WeightList.Count - 1; i++)
{
if (i == WeightList.Count - 1 || i == 0)
{
entries[i] = new ChartEntry(WeightList[i]) { Label = "" + i, ValueLabel = "" + WeightList[i] };
}
else
{
entries[i] = new ChartEntry(WeightList[i]) { Label = "" + i };
}
}
Chart = new LineChart() { Entries = entries, BackgroundColor = SKColors.Transparent };
}
I am using EPPlus library to exporting charts from the application. But I want to change the text direction of X-Axis labels of exported graphs. I am not able to find the property in epplus.
I have attached the image of property which I want to change in the graph using EPPLUS.
This is code to export the graph from Dataset to excel sheet.
using (ExcelPackage objExcelPackage = new ExcelPackage())
{
int i = 0, startIndex = 1, endIndex = 1, j = 0;
string[] seriesValues; string[] seriesHeader;
foreach (DataTable item in ds.Tables)
{
ExcelWorksheet objWorksheet = objExcelPackage.Workbook.Worksheets.Add("Sheet" + i.ToString());
objWorksheet.Cells.Style.Font.SetFromFont(new System.Drawing.Font("Calibri", 10));
objWorksheet.Cells.AutoFitColumns();
dynamic chart;
//Add the chart to the sheet
if (item.Rows[0].CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GetEnumDescription(CommonEnum.GraphType.ScatterChart) || item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GraphType.ScatterChart.ToString())
{
chart = objWorksheet.Drawings.AddChart(string.Empty, eChartType.XYScatterLines);
}
else if (item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GetEnumDescription(CommonEnum.GraphType.BarChart) || item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GraphType.BarChart.ToString())
{
chart = objWorksheet.Drawings.AddChart(string.Empty, eChartType.ColumnClustered) as ExcelBarChart;
}
else if (item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GetEnumDescription(CommonEnum.GraphType.BubbleChart) || item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GraphType.BubbleChart.ToString())
{
chart = objWorksheet.Drawings.AddChart(string.Empty, eChartType.Bubble) as ExcelBubbleChart;
}
else if (item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GetEnumDescription(CommonEnum.GraphType.BarSideBySideStackedSeries2D) || item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GraphType.BarSideBySideStackedSeries2D.ToString())
{
chart = objWorksheet.Drawings.AddChart(string.Empty, eChartType.BarStacked) as ExcelBarChart;
}
else if (item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GetEnumDescription(CommonEnum.GraphType.PointSeries2D) || item.Rows[0][CommonEnum.CommonColumnName.GraphType.ToString()].ToString() == CommonEnum.GraphType.PointSeries2D.ToString())
{
chart = objWorksheet.Drawings.AddChart(string.Empty, eChartType.XYScatter);
}
else
{
chart = objWorksheet.Drawings.AddChart(string.Empty, eChartType.Area);
}
if (item.TableName.Contains("TimeBased"))
{
//var chart1= objWorksheet.Drawings.AddChart(string.Empty, eChartType.XYScatter);
//Create the worksheet
objWorksheet.Cells["A1"].LoadFromDataTable(item, true);
DataView view = new DataView(item);
DataTable distinctMeasNumber = view.ToTable(true, CommonEnum.CommonColumnName.Name.ToString());
objWorksheet.Column(2).Style.Numberformat.Format = "yyyy-mm-dd hh:mm:ss";
foreach (DataRow dtItemRow in distinctMeasNumber.Rows)
{
startIndex = endIndex + 1;
endIndex = GetStartEndRowCount(item, dtItemRow[CommonEnum.CommonColumnName.Name.ToString()].ToString()) + 1;
chart.Series.Add(ExcelRange.GetAddress(startIndex, 3, endIndex, 3), ExcelRange.GetAddress(startIndex, 2, endIndex, 2)).Header = dtItemRow[CommonEnum.CommonColumnName.Name.ToString()].ToString();
}
chart.Title.Font.Bold = true;
chart.Title.Font.Size = 8;
chart.PlotArea.Border.LineStyle = eLineStyle.Solid;
chart.SetSize(650, 320);
chart.XAxis.MajorTickMark = eAxisTickMark.Cross;
chart.XAxis.MinorTickMark = eAxisTickMark.Cross;
chart.XAxis.LabelPosition = eTickLabelPosition.Low;
chart.YAxis.LabelPosition = eTickLabelPosition.Low;
chart.YAxis.MajorTickMark = eAxisTickMark.None;
chart.YAxis.MinorTickMark = eAxisTickMark.None;
chart.XAxis.Title.Text = item.Columns[1].ColumnName;`enter code here`
chart.XAxis.Title.Font.Size = 8;
chart.YAxis.Title.Text = item.Columns[2].ColumnName;
chart.YAxis.Title.Font.Size = 8;
i++;
}
chart.Title.Font.Bold = true;
chart.Title.Font.Size = 8;
chart.PlotArea.Border.LineStyle = eLineStyle.Solid;
chart.SetSize(580, 300);
chart.XAxis.MajorTickMark = eAxisTickMark.None;
chart.XAxis.MinorTickMark = eAxisTickMark.None;
chart.XAxis.LabelPosition = eTickLabelPosition.Low;
chart.YAxis.LabelPosition = eTickLabelPosition.Low;
chart.YAxis.MajorTickMark = eAxisTickMark.None;
chart.YAxis.MinorTickMark = eAxisTickMark.None;
chart.XAxis.Title.Text = item.Rows[0][CommonEnum.CommonColumnName.Col1Value.ToString()].ToString();
chart.XAxis.Title.Font.Size = 8;
chart.YAxis.Title.Text = item.Rows[0][CommonEnum.CommonColumnName.Col2Value.ToString()].ToString();
chart.YAxis.Title.Font.Size = 8;
}
chart.SetPosition(4, 0, 7, 0);
chart.Style = eChartStyle.Style2;
}
if (File.Exists(saveAsLocation))
File.Delete(saveAsLocation);
//Create excel file on physical disk
FileStream objFileStrm = File.Create(saveAsLocation);
objFileStrm.Close();
//Write content to excel file
File.WriteAllBytes(saveAsLocation, objExcelPackage.GetAsByteArray());
}
I dont believe EPPlus supports rotating the chart axis (it can do the chart title).
So, you could use the option of manually setting the XML. If you do something like this:
[TestMethod]
public void Chart_Rotate_x_Axis()
{
//https://stackoverflow.com/questions/55743869/x-axis-label-formatting-issue-while-exporting-chart-to-excel-using-epplus#comment98253473_55743869
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[] {
new DataColumn("Col1", typeof(int)),
new DataColumn("Col2", typeof(int)),
new DataColumn("Col3", typeof(object))
});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = i;
row[1] = i * 10;
row[2] = Path.GetRandomFileName();
datatable.Rows.Add(row);
}
//Create a test file
var fileInfo = new FileInfo(#"c:\temp\Chart_Rotate_x_Axis.xlsx");
if (fileInfo.Exists)
fileInfo.Delete();
using (var pck = new ExcelPackage(fileInfo))
{
var workbook = pck.Workbook;
var worksheet = workbook.Worksheets.Add("Sheet1");
worksheet.Cells.LoadFromDataTable(datatable, true);
var chart = worksheet.Drawings.AddChart("chart test", eChartType.XYScatter);
var series = chart.Series.Add(worksheet.Cells["B2:B11"], worksheet.Cells["A2:A11"]);
//Get the chart's xml
var chartXml = chart.ChartXml;
var chartNsUri = chartXml.DocumentElement.NamespaceURI;
var mainNsUri = "http://schemas.openxmlformats.org/drawingml/2006/main";
//XML Namespaces
var nsm = new XmlNamespaceManager(chartXml.NameTable);
nsm.AddNamespace("c", chartNsUri);
nsm.AddNamespace("a", mainNsUri);
//Get the axis nodes
var xdoc = worksheet.WorksheetXml;
var valAxisNodes = chartXml.SelectNodes("c:chartSpace/c:chart/c:plotArea/c:valAx", nsm);
foreach (XmlNode valAxisNode in valAxisNodes)
{
//Axis one should be the X Axis
if (valAxisNode.SelectSingleNode("c:axId", nsm).Attributes["val"].Value == "1")
{
var txPrNode = valAxisNode.SelectSingleNode("c:txPr", nsm) ?? valAxisNode.AppendChild(chartXml.CreateNode(XmlNodeType.Element, "c:txPr", chartNsUri));
var bodyPrNode = txPrNode.SelectSingleNode("a:bodyPr", nsm) ?? txPrNode.AppendChild(chartXml.CreateNode(XmlNodeType.Element, "a:bodyPr", mainNsUri));
//Set the rotation angle
var att = chartXml.CreateAttribute("rot");
att.Value = "-5400000";
bodyPrNode.Attributes.Append(att);
var att2 = chartXml.CreateAttribute("vert");
att2.Value = "horz";
bodyPrNode.Attributes.Append(att2);
txPrNode.AppendChild(chartXml.CreateNode(XmlNodeType.Element, "a:lstStyle", mainNsUri));
var pNode = chartXml.CreateNode(XmlNodeType.Element, "a:p", mainNsUri);
txPrNode.AppendChild(pNode);
var pPrNode = chartXml.CreateNode(XmlNodeType.Element, "a:pPr", mainNsUri);
pNode.AppendChild(pPrNode);
var defRPrNode = chartXml.CreateNode(XmlNodeType.Element, "a:defRPr", mainNsUri);
pPrNode.AppendChild(defRPrNode);
}
}
pck.Save();
}
}
You can get this:
The documentation for the rot or Rotation attribute is here:
Specifies the rotation that is being applied to the text within the bounding box. If it not specified, the rotation of the accompanying shape is used. If it is specified, then this is applied independently from the shape. That is the shape can have a rotation applied in addition to the text itself having a rotation applied to it. If this attribute is omitted, then a value of 0 is implied.
Consider the case where a shape has a rotation of 5400000, meaning 90 degrees clockwise, applied to it. In addition to this, the text body itself has a rotation of 5400000, or 90 degrees counter-clockwise, applied to it. Then the resulting shape would appear to be rotated but the text within it would appear as though it had not been rotated at all. The DrawingML specifying this would look like the following:
<p:sp>
<p:spPr>
<a:xfrm rot="5400000">
…
</a:xfrm>
</p:spPr>
…
<p:txBody>
<a:bodyPr rot="-5400000" … />
…
(Some text)
…
</p:txBody>
</p:sp>
https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.bodyproperties.rotation?view=openxml-2.8.1#DocumentFormat_OpenXml_Drawing_BodyProperties_Rotation
In EPPlus 5 at least (not sure about earlier versions), this can be done via the XAxis.TextBody.Rotation property. Be sure to set it between -90 and +90 for the values to align with the tick marks properly.
I have a loop that iterates through each date between two selected dates.
It creates a panel for each product on that date and then moves on.
That works fine, but if I run the loop again, it produces a blank space between the new panels and the old panels. Here is a example:
(small cause of my small screen)
The black lines indicate what I'm speaking about. The short one is the normal spacing between each panel and the long one indicates when I run the loop again.
The reason I'm worried is because if you want to add products after your initial add, it will be the second or third or fourth time the loop runs and leave that gap which just creates an unpleasant view.
Can someone please tell me what is causing this gap, here is the code for this section(i know about the SQL injections, i do send parameters to a DAL but for the sake of this question I'm using a SQL query):
for (DateTime date = dtpSDate.Value; date.Date <= dtpCDate.Value; date = date.AddDays(1))
{
pnlReport.Controls.Add(new Label { Text = date.ToString(), Height = 20, Width = 150, Name = date.ToString(), Location = new Point(20, globalY), Font = new Font("Arial", 10, FontStyle.Bold) });
globalY += 30;
foreach (DataRowView lstvItem in chkListProducts.CheckedItems)
{
DataTable ProdTmp2 = new DataTable();
string sqlP2 = "SELECT * FROM Product WHERE ProdID = '" + lstvItem[this.chkListProducts.ValueMember] + "'";
ProdTmp2 = db.GetDataTable(sqlP2);
if (Side == "Left")
{
Panel pnltmp = new Panel();
pnltmp.Size = new Size(472, 120);
pnltmp.Location = new Point(5, globalY);
pnltmp.BackColor = Color.Green;
pnltmp.Name = ProdTmp2.Rows[0][1].ToString();
Label l = new Label();
l.Text = ProdTmp2.Rows[0][1].ToString();
l.Location = new Point(5, 5);
pnltmp.Controls.Add(l);
pnlReport.Controls.Add(pnltmp);
Side = "Right";
last = ProdTmp2.Rows[0][1].ToString();
}
else
{
Panel pnltmp = new Panel();
pnltmp.Size = new Size(472, 120);
pnltmp.Location = new Point(487, globalY);
pnltmp.BackColor = Color.Red;
pnltmp.Name = ProdTmp2.Rows[0][1].ToString();
Label l = new Label();
l.Text = ProdTmp2.Rows[0][1].ToString();
l.Location = new Point(5, 5);
pnltmp.Controls.Add(l);
pnlReport.Controls.Add(pnltmp);
Side = "Left";
globalY += 140;
last = ProdTmp2.Rows[0][1].ToString();
}
}
if (Side == "Right")
{
Side = "Left";
globalY += 170;
}
}
Extra Info: The bigger the date gap. The larger the blank space gap.
So the end Code looks like this and the space is now non existent.
Thanx to all.
private void AddProducts()
{
foreach (Panel p in pnlReport.Controls.OfType<Panel>())
{
if (p.Name == last)
{
globalY = p.Location.Y + 140;
}
}
for (DateTime date = dtpSDate.Value; date.Date <= dtpCDate.Value; date = date.AddDays(1))
{
pnlReport.Controls.Add(new Label { Text = date.ToString(), Height = 20, Width = 150, Name = date.ToString(), Location = new Point(20, globalY), Font = new Font("Arial", 10, FontStyle.Bold) });
globalY += 30;
foreach (DataRowView lstvItem in chkListProducts.CheckedItems)
{
string table = "";
string select = "";
if (Regex.IsMatch(lstvItem[this.chkListProducts.DisplayMember].ToString(), #"^[a-hA-H]"))
{
table = "ProductHistoryAH";
}
else if (Regex.IsMatch(lstvItem[this.chkListProducts.DisplayMember].ToString(), #"^[i-qI-Q]"))
{
table = "ProductHistoryIQ";
}
else if (Regex.IsMatch(lstvItem[this.chkListProducts.DisplayMember].ToString(), #"^[r-zR-Z]"))
{
table = "ProductHistoryRZ";
}
DataTable ProdTmp1 = new DataTable();
string sqlP1 = "SELECT * FROM " + table + " WHERE ProdID = '" + lstvItem[this.chkListProducts.ValueMember] + "' AND ProdHDate = '" + date.ToString(#"yyyy\/MM\/dd") + "'";
ProdTmp1 = db.GetDataTable(sqlP1);
DataTable ProdTmp2 = new DataTable();
string sqlP2 = "SELECT * FROM Product WHERE ProdID = '" + lstvItem[this.chkListProducts.ValueMember] + "'";
ProdTmp2 = db.GetDataTable(sqlP2);
if (Side == "Left")
{
Panel pnltmp = new Panel();
pnltmp.Size = new Size(472, 120);
pnltmp.Location = new Point(5, globalY);
pnltmp.BackColor = Color.Green;
pnltmp.Name = ProdTmp2.Rows[0][1].ToString() + count.ToString();
//Name
Label lblName = new Label();
lblName.Text = lstvItem[this.chkListProducts.DisplayMember].ToString();
lblName.Location = new Point(5, 5);
pnltmp.Controls.Add(lblName);
int Location = 0;
//Count
if (chkPCount.Checked)
{
Label lblCount = new Label();
if(ProdTmp1.Rows.Count>0)
{
lblCount.Text = "Count: " + ProdTmp1.Rows[0][4].ToString();
}
else
{
lblCount.Text = "Count: 0";
}
lblCount.Location = new Point(17, 32);
pnltmp.Controls.Add(lblCount);
}
pnlReport.Controls.Add(pnltmp);
Side = "Right";
}
else if(Side == "Right")
{
Panel pnltmp = new Panel();
pnltmp.Size = new Size(472, 120);
pnltmp.Location = new Point(487, globalY);
pnltmp.BackColor = Color.Red;
pnltmp.Name = ProdTmp2.Rows[0][1].ToString() + count.ToString();
//Name
Label lblName = new Label();
lblName.Text = lstvItem[this.chkListProducts.DisplayMember].ToString();
lblName.Location = new Point(5, 5);
pnltmp.Controls.Add(lblName);
//Count
if (chkPCount.Checked)
{
Label lblCount = new Label();
if (ProdTmp1.Rows.Count > 0)
{
lblCount.Text = "Count: " + ProdTmp1.Rows[0][4].ToString();
}
else
{
lblCount.Text = "Count: 0";
}
lblCount.Location = new Point(17, 32);
pnltmp.Controls.Add(lblCount);
}
pnlReport.Controls.Add(pnltmp);
Side = "Left";
globalY += 140;
}
last = ProdTmp2.Rows[0][1].ToString() + count.ToString();
}
if (Side == "Right")
{
Side = "Left";
globalY += 140;
}
}
count++;
}
and the output:
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.
I'm currently building an application that shows a line-graph and uses MSChartControl.
Here is the relevant code:
WeatherDatabase _db = new WeatherDatabase(Properties.Settings.Default.SqlConnectionString);
private void AddSensorDataToGraph(int sensorType, string xTitle, string yTitle, string Title)
{
var sensorIdList = (from d in _db.Sensors where d.TypeId == sensorType select d.Id).ToArray();
var startDate = dateTimePickerStart.Value;
var stopDate = dateTimePickerStop.Value;
SetMaxMinValues(dateTimePickerStart.Value.ToOADate(), dateTimePickerStop.Value.ToOADate());
this.Text = chartValues.Titles["Titel"].Text = String.Format("{0} vom {1} bis {2}", Title, startDate, stopDate);
chartValues.ChartAreas[CHARTAREA].AxisX.Title = xTitle;
chartValues.ChartAreas[CHARTAREA].AxisY.Title = yTitle;
// Alte Datensätze löschen
chartValues.Series.Clear();
foreach (var sensorId in sensorIdList)
{
var values = (from d in _db.DeviceIO where d.IdSensor == sensorId && d.Timestamp > startDate && d.Timestamp < stopDate orderby d.Timestamp ascending select d).ToArray();
if (values.Length > 0)
{
var desc = values[0].Sensors.SensorType.Description;
chartValues.Series.Add(GenerateDefaultSeries(String.Format("{0}\r\nSensorId: {1}", desc, sensorId)));
//chartValues.Series.Add("Test" + sensorId);
int seriesId = chartValues.Series.Count - 1;
foreach (var item in values)
{
if (item.Value == null)
{
continue;
}
if ((double)item.Value == NOVALUE)
{
continue;
}
AddPointToChart((DateTime)item.Timestamp, (double)item.Value, seriesId);
}
}
}
}
private void AddPointToChart(DateTime timestamp, double value, int series)
{
DataPoint dataPoint = chartValues.Series[series].Points.Add(value);
dataPoint.XValue = timestamp.ToOADate();
dataPoint.AxisLabel = "dd.MM.yy hh:mm";
}
private void SetMaxMinValues(double min, double max)
{
if (min < max)
{
chartValues.ChartAreas[CHARTAREA].AxisX.Minimum = min;
chartValues.ChartAreas[CHARTAREA].AxisX.Maximum = max;
}
}
private Series GenerateDefaultSeries(string Name)
{
// Grundeinstellungen für die Data-Series setzen
Series series = new Series();
series.ChartType = SeriesChartType.Line;
series.Name = Name;
series.IsValueShownAsLabel = false;
series.IsVisibleInLegend = true;
series.IsXValueIndexed = false;
series.AxisLabel = "dd.MM.yy hh:mm";
// series.LabelFormat = "g";
series.XAxisType = AxisType.Primary;
series.XValueType = ChartValueType.Auto;
series.YValuesPerPoint = 1;
// series.AxisLabel = "dd.MM.yy hh:mm";
return series;
}
If i add a series in designer mode x-axies Labels are correct
Click for pic
But if i start my application and add a series programmatically it loks like this
Anyone knows why the x-axis caption / label is not displayed correctly?
EDIT:
Based on Tom's help I change some things:
var seriesName = String.Format("{0}\r\nSensorId: {1}", desc, sensorId);
var curSeries = chartValues.Series.Add(seriesName);
UpdateSeriesSettings(ref curSeries);
[...]
AddPointToChart((DateTime)item.Timestamp, (double)item.Value, curSeries);
[...]
private void UpdateSeriesSettings(ref Series series)
{
// Grundeinstellungen für die Data-Series setzen
// Series series = new Series();
series.ChartType = SeriesChartType.Spline;
series.IsValueShownAsLabel = false;
series.IsVisibleInLegend = true;
series.IsXValueIndexed = false;
series.AxisLabel = "dd.MM.yy hh:mm";
// series.LabelFormat = "g";
series.XAxisType = AxisType.Primary;
series.XValueType = ChartValueType.Auto;
series.YValuesPerPoint = 1;
// series.AxisLabel = "dd.MM.yy hh:mm";
}
But my X axis stil ain't having a label ):
Used the following code:
Fixed my problem.
var desc = values[0].Sensors.SensorType.Description;
var seriesName = String.Format("{0}\r\nSensorId: {1}", desc, sensorId);
curSeries = chartValues.Series.Add(seriesName);
curSeries.ChartType = SeriesChartType.Line;
curSeries.XValueType = ChartValueType.DateTime;
foreach (var item in values)
{
curSeries.Points.AddXY(item.Timestamp, item.Value);
}
It looks like you are adding each point as its own series. Usually, you would add many points into one series.
Do you intend to change seriesId right before you use it?
int seriesId = chartValues.Series.Count - 1;
This happens before you call AddPointToChart() so that is getting a different value than you used in the series
chartValues.Series.Add(GenerateDefaultSeries(String.Format("{0}\r\nSensorId: {1}", desc, sensorId)));
EDIT
The line chartValues.Series.Add(...) will return a variable of type Series which you can add to directly. see http://www.dotnetperls.com/chart
SensorID is your number and I think that there are no guarantees that the system will choose to populate its collection of Series in any particular order.
EDIT
It looks like you're adding your points to the series before you finish setting them up. Instead, set them all the way up, then add them:
private void AddPointToChart(DateTime timestamp, double value, int series)
{
DataPoint dataPoint = new DataPoint(timestamp.ToOADate(), value);
dataPoint.AxisLabel = "dd.MM.yy hh:mm"; // not sure how this will work, but try it.
chart1.Series[series].Points.Add(dataPoint );
}