I'm creating a dotnet high chart by using the code:
object[,] data1 = new object[Dt.Rows.Count, 2];
for (int i = 0; i < Dt.Rows.Count; i++)
{
data1[i, 0] = Dt.Rows[i]["IncDate"];
data1[i, 1] = Dt.Rows[i]["IncCost"];
}
object[,] data2 = new object[Dt2.Rows.Count, 2];
for (int i = 0; i < Dt2.Rows.Count; i++)
{
data2[i, 0] = Dt2.Rows[i]["ExpDate"];
data2[i, 1] = Dt2.Rows[i]["ExpCost"];
}
Highcharts chart = new Highcharts("graph")
.SetTitle(new Title { Text = "Money Analysis" })
.SetXAxis(new XAxis { Type = AxisTypes.Datetime })
.SetYAxis(new YAxis { Title = new YAxisTitle { Text = "Amount (£)" } })
.SetSeries(new[]
{
new Series { Name = "Incomings", Color = Color.LimeGreen, Data = new Data(data1) },
new Series { Name = "Expenditures", Color = Color.Red, Data = new Data(data2) }
})
which outputs:
I was wondering how I'm able to make the red line continue along the current cost until the end of the graph (the last date for the incoming.) And also do the same for the green line if the expenditures have a later date than the last incomings date. Hopefully people know what I mean. Thanks in advance
So, no - the chart won't do that for you automatically, but we can do it ourselves easy enough.
1) retrieve the final y value of your data
2) retrieve the final x value that you want it to extend until
3) add a new point to the data with those x and y values.
Example function:
function extendData(series, axis) {
var ext = axis.getExtremes();
var x = ext.dataMax;
var y = series.data[series.data.length -1].y;
series.addPoint({'x':x, 'y':y, marker: { enabled: true }});
}
Working example:
http://jsfiddle.net/jlbriggs/omtugbvo/
And just to show an example using any number of series:
http://jsfiddle.net/jlbriggs/omtugbvo/3/
Example image:
Related
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'm trying to set a percent format when I create a cell, this is my code.
new Data.CellData
{
UserEnteredValue = new Data.ExtendedValue
{
FormulaValue = "=(B2/C2)-1"
},
EffectiveFormat = new Data.CellFormat
{
NumberFormat = new Data.NumberFormat
{
Pattern = "00.0%",
Type = "NUMBER"
}
}
}
Current value shown is "-0.2488570834" and I need this "-24.88%". What I'm missing ?
Thanks in advance.
Try following the Number format examples provided in the document:
I've also found a sample code to test this (converting format to your sample):
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getActiveRange();
var rows = range.getNumRows();
var cols = range.getNumColumns();
for(var row = 1; row <= rows; row++) {
for(var col = 1; col <= cols; col++) {
var cell = range.getCell(row, col);
var value = cell.getValue();
if(typeof(value) == 'number') {
cell.setNumberFormat("##.#%");
}
}
}
Result:
The code sample is for Apps script but the implementation to C# would be the same. Change Pattern = "00.0%", to Pattern = "##.#%",.
Hope this helps.
Ive been working at this for hours and cant seem to figure out how to correctly display the data in a table
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int pointX = 30;
int pointY = 40;
while (!csvParser.EndOfData) {
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
int index = 0;
for(index = 0; index < rowNums;index++) {
string Name = fields[index];
TextBox n = new TextBox();
n.Text = Name;
n.Location = new Point(pointX, pointY);
panel2.Controls.Add(n);
panel2.Show();
pointY += 20;
if(index != 0) {
pointX += 100;
}
}
}
}
Whats happening so far is im grabbing a csv file stored in the path variable and reading it the output is accessible through fields[] This works fine then I am trying to create textbox to put the data into based on rows however what i currently have comes out look like this
Program Display
I would like to display the column names and rows correctly in order here is an example image of what it looks like in notepad
Notepad Display
In notepad you will see each new line is a row and every , dictates a new entry in the row and i wanna display it this way in my program but in textbox
Also note that not all csv files that this program will be opening are short most will be large files with thousands or rows or more so theres no way that it could be simply putting fields[0] hard coded
You are much better off using a DataGridView to display this type of data in a table format.
From the toolbox add the DataGridView control to your form. You will need to build a DataTable that will bind to your DataGridview.
Below is what you can use(I commented out where you are skipping the header in your CSV file, and am using that line to get the column headers to be used in the datagrid)
var dt = new DataTable();
var lineNo = 0;
using (var csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
//csvParser.ReadLine();
while (!csvParser.EndOfData)
{
var fields = csvParser.ReadFields();
var rowNums = fields.Length;
var row = dt.NewRow();
lineNo += 1;
int index = 0;
for (index = 0; index < rowNums; index++)
{
if (lineNo==1)
{
dt.Columns.Add(fields[index]);
}
else
{
row[index] = fields[index];
}
}
if (lineNo == 1) continue;
dt.Rows.Add(row);
dt.AcceptChanges();
}
}
dataGridView1.DataSource = dt;
I think the controls are overlapping, so you can not see them. That they are overlapping like chairs is the problem. You are not resetting your coordinates.
Here an improvement:
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int offsetX = 30;
int offsetY = 40;
int counter;
while (!csvParser.EndOfData) {
int pointerY = ++counter * offsetY; // first counter increments by one, then counter times offsetY occurs
int pointerX;
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
for(int index = 0; index < rowNums;index++) {
pointerX = (index + 1) * offsetX;
string name = fields[index];
TextBox n = new TextBox() { Text = name, Location = new Point(pointerX, pointerY) };
panel2.Controls.Add(n);
panel2.Show(); // should be unnecessary
}
}
}
I'm doing currently some first Windows Phone 8 / XAML experiments and use the WinRTXamlToolkit chart control. While I managed to get my data drawn, I have problems to redraw the control to display changed data.
CHART_Overview.Series.Add(_lsChartOvw);
((AreaSeries)CHART_Overview.Series[0]).ItemsSource = _lstLogOvw;
LinearAxis dta = new LinearAxis();
dta.Title = "X";
dta.Orientation = AxisOrientation.X;
dta.ShowGridLines = true;
CHART_Overview.Axes.Add(dta);
CHART_Overview.Axes.Add(new LinearAxis()
{
Minimum = 0,
Maximum = 100,
Orientation = AxisOrientation.Y,
Interval = 20,
ShowGridLines = true,
Title = "Y"
});
Random rd = new Random((int)DateTime.Now.ToFileTimeUtc());
for(int i = 0; i < 20; i++)
{
_lstLogOvw.Add(new GenericValueItem() { X = i, Y = rd.Next(1, 100) });
}
I tried the following update scheme
_lstLogOvw.Clear();
for (int i = 0; i < 20; i++)
{
_lstLogOvw.Add(new GenericValueItem() { X = i, Y = rd.Next(1, 100) });
}
((AreaSeries)CHART_Overview.Series[0]).ItemsSource = _lstLogOvw;
The list is of type ObservableCollection. It's probably a binding problem but I've not much XAML experience at the moment to fully understand the refresh mechanism.
Don't know the proper way of doing it, but you can get around it by force the ItemSource to be null before assigning back to the actual list.
((AreaSeries)CHART_Overview.Series[0]).ItemsSource = null;
((AreaSeries)CHART_Overview.Series[0]).ItemsSource = _lstLogOvw;
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.