I need to get data from a single DataGridView into 3 distinct Charts, each column to a chart.
This is the first time I'm working with charts, so I've researched a bit about them but couldn't find anything that could help me with this specific problem.
Here follows a screenshot of what my DataGridView and my Charts are like, with their respective legends:
What I need is (at least in theory) a very simple thing. In the "SANITY" chart I need to get the value located at Table[0, 0] into something like sanityChart.Series[0] as its 'Y' value, and so on. Same thing for "UNIT" and "ISSUES DB".
Any help will be much appreciated.
From the way you asked the question and the image you have shown I believe that you want something like this:
I use only one Chart and I have added one Series for each of your Columns.
After setting up the chart I have added one data point for each row in the table to each of the series/columns..:
// setting up the datagridview
Table.Rows.Clear();
Table.Columns.Clear();
Table.Columns.Add("state", "");
Table.Columns.Add("sanity", "SANITY");
Table.Columns.Add("unit", "UNIT");
Table.Columns.Add("issuesDb", "ISSUES DB");
// filling in some data
Table.Rows.Add("ALL PASSED", 86, 21, 2);
Table.Rows.Add("FAILED", 7, 0, 1);
Table.Rows.Add("Cancelled", 0, 0, 0);
// Now we can set up the Chart:
List<Color> colors = new List<Color>{Color.Green, Color.Red, Color.Black};
chart1.Series.Clear();
for (int i = 0 ; i < Table.Rows.Count; i++)
{
Series S = chart1.Series.Add(Table[0, i].Value.ToString());
S.ChartType = SeriesChartType.Column;
S.Color = colors[i];
}
// and fill in all the values from the dgv to the chart:
for (int i = 0 ; i < Table.Rows.Count; i++)
{
for (int j = 1 ; j < Table.Columns.Count; j++)
{
int p = chart1.Series[i].Points.AddXY(Table.Columns[j].HeaderText, Table[j, i].Value);
}
}
Note that I have chosen to add the DataPoints with the AddXY overload and also decided that I want to make my life a little easier by adding the X-Values as strings. Internally they are still transformed to doubles and will all have a value of 0! Not a problem for us here, but it is important to understand that we didn't use valid numbers and therefore can't treat them as numbers! The advantage is that the axis labels are being set to the column headers without any further work..
Related
Ok I am stumped the set font works and sets the correct cell. The Fill.BackgroundColor.SetColor does not work. It literally does nothing. Am I missing something? Every search I do that syntax should work.
Yes this is reading a data table dt that is not the issue the loop works properly.
int startupRow = 2; // row 1 is header
for (int row = startupRow; row <= dt.Rows.Count; row++)
{
//if allocation check is populated it will have a value > 0
if (Convert.ToInt32(workSheet.Cells[row, 8].Value) > 0)
{
//if Balance Remaining Barrels < allocation check
if (Convert.ToInt32(workSheet.Cells[row, 7].Value) < Convert.ToInt32(workSheet.Cells[row, 8].Value))
{
//set the font to red
var cell = workSheet.Cells[row, 7];
cell.Style.Font.Color.SetColor(System.Drawing.Color.Red);
cell.Style.Font.Bold = true;
//Setting the background color to red
cell.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
cell.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.Red);
}
}
}
I resolved my own question so hopefully this helps someone else.
The border has to be last in the formatting. Certain elements take precedent over other elements and they will disappear.
I'm working wth .NET 4.7.2, Windowsform.
I have a datagridview and I manage to generate a powerpoint file pptx.
I made a first ppt slide and I'd like to add the datagridview content into the second ppt slide given that I need to have the option to change the data within the PPt slide.
Microsoft.Office.Interop.PowerPoint.Application pptApp = new Microsoft.Office.Interop.PowerPoint.Application();
pptApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Slides slides;
Microsoft.Office.Interop.PowerPoint._Slide slide;
Microsoft.Office.Interop.PowerPoint._Slide slide2;
Microsoft.Office.Interop.PowerPoint.TextRange objText;
// Create File
Presentation pptPresentation = pptApp.Presentations.Add(Microsoft.Office.Core.MsoTriState.msoTrue);
CustomLayout customLayout = pptPresentation.SlideMaster.CustomLayouts[PpSlideLayout.ppLayoutText];
// new Slide
slides = pptPresentation.Slides;
slide = slides.AddSlide(1, customLayout);
slide2 = slides.AddSlide(1, customLayout);
// title
objText = slide.Shapes[1].TextFrame.TextRange;
objText.Text = "Bonds Screner Report";
objText.Font.Name = "Haboro Contrast Ext Light";
objText.Font.Size = 32;
Shape shape1 = slide.Shapes[2];
slide.Shapes.AddPicture("C:\\mylogo.png", Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue, shape1.Left, shape1.Top, shape1.Width, shape1.Height);
slide.NotesPage.Shapes[2].TextFrame.TextRange.Text = "Disclaimer";
dataGridViewBonds.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
dataGridViewBonds.SelectAll();
DataObject obj = dataGridViewBonds.GetClipboardContent();
Clipboard.SetDataObject(obj, true);
Shape shapegrid = slide2.Shapes[2];
I know I'm not so far by now but I miss smething. Any help would be appreciated !
I am familiar with Excel interop and have used it many times and most likely have become numb to the awkward ways in which interop works. Using PowerPoint interop can be very frustrating for numerous reasons, however, the biggest I feel is the lack of documentation and the differences between the different MS versions.
In addition, I looked for a third-party PowerPoint library and “Aspose” looked like the only option, unfortunately it is not a “free” option. I will assume there is a free third-party option and I just did not look in the right place… Or there may be a totally different way to do this possibly with XML. I am confident I am preaching to the choir.
Therefore, what I have been able to put together may work for you. For starters, looking at your current posted code, there is one part missing that you need to get the “copied” grid cells into the slide…
slide.Shapes.Paste();
This will paste the “copied” cells from the grid into an “unformatted” table into the slide. This will copy the “row header” if it is displayed in the grid in addition to the “new row” if the grids AllowUserToAddRows is set to true. If this “unformatted paste” works for you, then you are good to go.
If you prefer to have at least a minimally formatted table and ignore the row headers and last empty row… It may be easier to simply “create” a new Table in the slide with the size we want along with the correct number of rows and columns. Granted, this may be more work, however, using the paste is going require this anyway “IF” you want the table formatted.
The method (below) takes a power point _Slide and a DataGridView. The code “creates” a new Table in the slide based on the number of rows and columns in the given grid. With this approach, the table will be “formatted” using the default “Table Style” in the presentation. So, this may give you the formatting you want by simply “creating” the table as opposed to “pasting” the table.
I have tried to “apply” one of the existing “Table Styles” in power point, however, the examples I saw used something like…
table.ApplyStyle("{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}");
Which uses a GUID id to identify “which” style to use. I am not sure why MS decided on this GUID approach… this is beyond me, and it worked for “some” styles but not all.
Also, more common-sense solutions that showed something like…
table.StylePreset = TableStylePreset.MediumStyle2Accent2;
Unfortunately using my 2019 version of Office PowerPoint, this property does not exist. I have abandoned further research on this as it appears to be version dependent. Very annoying!
Given this, it may be easier if we format the cells individually as we want. We will need to add the cells text from the grid into the individual cells anyway, so we could also format the individual cells at the same time. Again, I am confident there is a better way, however, I could not find one.
Below the InsertTableIntoSlide(_Slide slide, DataGridView dgv) method takes a slide and a grid as parameters. It will add a table to the slide with data from the given grid. A brief code trace is below.
First a check is made to get the number of total rows in the grid (not including the headers) totRows. If the grids AllowUsersToAddRows is true, then the total rows variable is decremented by 1 to ignore this new row. Next the number of columns in the grid is set to the variable totCols. The top left X and Y point is defined topLeftX and topLeftY to position the table in the slide along with the tables width and height.
ADDED NOTE: Using the AllowUserToAddRows property to determine the number of rows … may NOT work as described above and will “miss” the last row… “IF” AllowUserToAddRows is true (default) AND the grid is data bound to a data source that does NOT allow new rows to be added. In that case you do NOT want to decrement the totRows variable.
Next a “Table” “Shape” is added to the slide using the previous variables to define the base table dimensions. Next are two loops. The first loop adds the header cells to the first row in the table. Then a second loop to add the data from the cells in the grid… to the table cells in the slide.
The commented-out code is left as an example such that you want to do some specific formatting for the individual cells. This was not need in my case since the “default” table style was close to the formatting I wanted.
Also, a note that “ForeColor” is the “Back ground” color of the cell/shape. Strange!
I hope this helps and again, sympathize more about having to use PowerPoint interop… I could not.
private void InsertTableIntoSlide(_Slide slide, DataGridView dgv) {
try {
int totRows;
if (dgv.AllowUserToAddRows) {
totRows = dgv.Rows.Count - 1;
}
else {
totRows = dgv.Rows.Count;
}
int totCols = dgv.Columns.Count;
int topLeftX = 10;
int topLeftY = 10;
int width = 400;
int height = 100;
// add extra row for header row
Shape shape = slide.Shapes.AddTable(totRows + 1, totCols, topLeftX, topLeftY, width, height);
Table table = shape.Table;
for (int i = 0; i < dgv.Columns.Count; i++) {
table.Cell(1, i+1).Shape.TextFrame.TextRange.Text = dgv.Columns[i].HeaderText;
//table.Cell(1, i+1).Shape.Fill.ForeColor.RGB = ColorTranslator.ToOle(Color.Blue);
//table.Cell(1, i+1).Shape.TextFrame.TextRange.Font.Bold = Microsoft.Office.Core.MsoTriState.msoTrue;
//table.Cell(1, i+1).Shape.TextFrame.TextRange.Font.Color.RGB = ColorTranslator.ToOle(Color.White);
}
int curRow = 2;
for (int i = 0; i < totRows; i++) {
for (int j = 0; j < totCols; j++) {
if (dgv.Rows[i].Cells[j].Value != null) {
table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Text = dgv.Rows[i].Cells[j].Value.ToString();
//table.Cell(curRow, j + 1).Shape.Fill.ForeColor.RGB = ColorTranslator.ToOle(Color.LightGreen);
//table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Font.Bold = Microsoft.Office.Core.MsoTriState.msoTrue;
//table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Font.Color.RGB = ColorTranslator.ToOle(Color.Black);
}
}
curRow++;
}
}
catch (Exception ex) {
MessageBox.Show("Error: " + ex.Message);
}
}
I'm creating manually a boxplot chart. I have 4 double[] arrays with some calculations results that i want show on chart. I don't know how to connect correctly my arrays with chart Series.
Here is my chart:
Chart chart = new Chart();
chart.Series.Add("S1");
chart.Series.Add("S2");
chart.Series.Add("S3");
chart.Series.Add("S4");
chart.ChartAreas.Add("ChartArea1");
chart.ChartAreas[0].Visible = true;
chart.ChartAreas[0].Position.Auto = true;
chart.Series[0].ChartType = SeriesChartType.BoxPlot;
chart.Series[1].ChartType = SeriesChartType.BoxPlot;
chart.Series[2].ChartType = SeriesChartType.BoxPlot;
chart.Series[3].ChartType = SeriesChartType.BoxPlot;
chart.Parent = this;
chart.Visible = true;
double[] yValues = { 2, 3, 4, 5, 4, 5, 5, 2, 1, 9, 20, 4 };//example values
chart.Series["S1"].Points.DataBindY(yValues);
This is what i get:
As result i want get something like that:
You have tried to bind your data to a BoxPlot series. But this has only resulted in binding the first Y-value, which means you have created a set of 'lower whiskers'. All other 5 Y-values are empty, ie are 0. Hence the meager graphics you see..
MSDN: The values for a box are typically calculated values from
data that is present in another series. One box symbol (DataPoint
object) is associated with one data series.
The data for a Box Plot series may still be populated using
data binding, or by employing the Series.Points member (a
DataPointCollection object).
Let's look at all of these options:
Use regular data binding. This is what you tried but with a wrong syntax.
You can also add the DataPoints of the box plot series itself one by one with AddY or AddXY, supplying all 6 Y-Values, i.e. feeding in the results of a statistical analysis. Here only one array is used and it contains the six y-values.
Or you can use one or more data series and let the chart summarize those data in one box per series. The series are quite normal, read may be Point, Line or whatever.. They can even be invisible and of course you can use data binding for them.. - Once some data series are in place you can define the BoxPlot series and 'bind' it to the data series by seting its ["BoxPlotSeries"] special property to a string into which you concatenate the Series' Names...
Option 1. Use regular data binding to feed in the stats you have.
This is what you tried. The correct way is a little surprising, though; your data need to be ordered like this:
The outer array (or IEnumerable) must have the six y-values; the six inner arrays should contain one value for each of the data sets you want to show in a box. Let's look at an example with three data sets of faked stats:
double[][] yValues = {
new[]{ 15.6, 24.4, 36.1 }, // Lower whiskers
new[]{ 46.2, 52.2, 91.9 }, // Upper whiskers
new[]{ 22.3, 27.2, 55.9 }, // Lower boxes
new[]{ 33.2, 44.4, 77.9 }, // Upper boxes
new[]{ 25.2, 38.4, 68.5 }, // Averages and means
new[]{ 27.4, 32.4, 66.9 } // Medians
};
This is how it looks after binding it to a BoxPlot series S1
S1.Points.DataBindY(yValues);
You can also create individual series; see the update at the bottom for this!
Option 2: Feed in the BoxPlot data yourself
Let's look at an example of the first way: Here we need to have the statistics ready.. First I create a random data structure; it is a List of double arrays each with 6 elements:
Random R = new Random(23);
List<double[]> yValues = new List<double[]>();
for (int i = 0; i < 8; i++)
{
{ R.Next(5), R.Next(5) + 20, R.Next(5) + 3,
R.Next(5) + 10, R.Next(5) + 5, R.Next(5) + 7 });
}
Now we add those fake statistics to the BoxPlot series:
S1.ChartType = SeriesChartType.BoxPlot;
S1.Points.Clear();
for (int i = 0; i < yValues.Count; i++ ) S1.Points.Add(new DataPoint(i, yValues[i]));
Note that each DataPoint is created from an array of 6 doubles!
Here is the result:
The chart now shows stats on 8 data sets, all faked ;-)
Option 3a: Associate some data series with the BoxPlot and let it do the math
The other usage is to let the chart to the math: It can calculate the statistics for any number of data series you have and create one box for each.
We will use the same data set as before, but now they are used to create 6 data series, each with 8 points:
for (int i = 0; i < 6; i++)
{
Series ds = chart.Series.Add("D" + (i+1)); // set a name D1, D2..
dx.ChartType = SeriesChartType.Line;
dx.Points.DataBindY(yValues.Select(x => x[i]).ToArray());
}
They are bound to the same numbers as above, but these now have a different meaning. Therefore the results will and should not look alike!!
In fact you can look at the boxes and graphs and see how they nicely fit.
Note the Names I gave the data series; we will need them now, when we 'bind' them to the BoxPlot series:
S1.ChartType = SeriesChartType.BoxPlot;
S1["BoxPlotSeries"] = "D1;D2;D3;D4;D5;D6"; // box plot binding!
S1.LegendText = "BoxPlot";
I use quotes for the 'binding' because it isn't real data binding; instead the data series are only associated with the BoxPlot series with the sprecial property string "BoxPlotSeries".
Your example has more than one BoxPlot series; here the same rules apply.
Do have a look at this post which shows another use of BoxPlot including setting individual colors..
Option 3b: Associate some data series with DataPoints you add to the BoxPlot series; here too it will do the math for us
While option 3a seems rather simple I found no way to color the boxes. Here is how we can do that:
First we force the chart to copy the default colors to the series. While we're at it, let's also hide the Legend item:
S1.Color = Color.Transparent;
S1.LegendText = " ";
chart.ApplyPaletteColors()
Then we create one DataPoint in out BoxPlot series S1 for each data series; not how my series are ordered: 0=The BoxPlot, 1-6 some data series! You may need to adapt this!
for (int i = 1; i < chart.Series.Count; i++)
{
DataPoint dp = new DataPoint();
S1.Points.Add(dp);
dp["BoxPlotSeries"] = "D" + i; // names D1-D6
dp.Color = chart.Series[i].Color;
}
Here is the result:
Update:
Your example actually shows three boxplot series; hence the colors and most notably: the clustered (i.e. gapless) display.
This would be a way to bind the above data (from option 1) to three individual boxes:
for (int i = 0; i < 3; i++)
{
Series bps = chart.Series.Add("BoxPlotSeries" + i);
bps.ChartType = SeriesChartType.BoxPlot;
var yValOne = yValues.Select(x => new[] { x[i] }).ToArray();
bps.Points.DataBindY(yValOne);
}
Final note: Your example code contains an array with 12 doubles and also 4 boxplot series. This makes no sense. You can add the 12 values to a normal series and associate it with one boxplot series using option 3, though..
I am trying to figure out how to turn the following, one-line CSV file into a 30x30 2D array.
http://pastebin.com/8NP7s7N0
I've tried looking it up myself, but I just can't seem to wrap my brain around the concept of multidimensional arrays, and I don't know how to turn a one-line file like this into an array of a specified size.
I want to be able to make an array that would look like this when printed:
0,0 = 2
0,1 = 2
All the way to 30,30.
Most of the numbers in the CSV are indeed 2's, but some are 1s. The difference is very important though. I am trying to make collision detection for a game, and this CSV file is the map. All I need left is how to create this array - leave the rest to me. :)
Thank you very much to all, have a nice day.
This should be a complete example using a 5 x 5 grid. I've tried it and seems to work as expected:
namespace ConsoleApplication1
{
using System;
class Program
{
const int MapRows = 5;
const int MapColumns = 5;
static void Main(string[] args)
{
// Create map and the raw data (from file)
var map = new int[MapRows, MapColumns];
string rawMapData = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25";
string[] splitData = rawMapData.Split(',');
int index = 0;
// Loop through data
for (int row = 0; row < MapRows; row++)
{
for (int column = 0; column < MapColumns; column++)
{
// Store in map and show some debug
map[row, column] = int.Parse(splitData[index++]);
Console.WriteLine(string.Format("{0},{1} = {2}", row, column, map[row, column]));
}
}
// Wait for user to read
Console.ReadKey();
}
}
}
Assuming your file is 900 elements first you need to read it in..
something along the lines of
line = myStreamReader.readLine().Split(',').. then in John U's example, value would be the next index in this array called line
I'll let you work out whats missing from my example :P
well, first you need to get the numbers...
var numbers = Read_File_As_String().Split(new char[',']).Select(n => int.Parse(n)).ToList();
then, you need to build your array
const int ROWS = 30;
const int COLS = 30;
var result = new int[ROWS, COLS];
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLS; col++)
result[row, col] = numbers[(row * COLS) + col];
for(row=0;row<30;row++)
{
for(col=0;col<30;col++)
{
array[row][col] = value;
}
}
Value would need to be moved along to point to the next thing each time, but I'm sure you can figure that out.
Edited to add: If it's a map it might be easier to store it as an array in the first place.
Since you asked about the concept of multi-dimensional arrays, here are some useful ways of thinking about arrays. Please note these are analogies, meant to help you visualize them.
Think of a 1D array as a list of items (not in the programming sense of list!).
Think of a 2D array as a table (again, not in the programming sense!). In a table (like a spreadsheet) you have rows and columns, and each dimension in your array accesses one of these.
For higher dimensional arrays, it may help to think geometrically. For instance, you can think of 3D arrays as 3-dimensional points in space, and 4D arrays as 4-dimensional points in space-time.
So if you have a single CSV file, start off by conceptualizing how this would be re-structured as a table. Once you have that, you have a pretty straight-forward mapping to the array.
I want to ask if its possible to color the negative values of the chart a different color from the positive ones. Also how can i enter marks on the values like tagg the values at 1,2,3..10 etc! The values range from -300000 up to 700000
The chart is in C# and i have a button that executes the following:
decimal[] numbers = new decimal[20];
for (int i = 0; i < 20; i++)
{
numbers[i] = Convert.ToDecimal(dataGridView1[7, i].Value);
chart1.Series["Loan_Balance"].Points.AddXY(i+1, numbers[i]); }
chart1.Series["Loan_Balance"].Color = Color.Blue;
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Interval = 50000;
chart1.Series["Loan_Balance"].ChartType = SeriesChartType.Range;
}
Also i wanted to ask, why when i enter the values straight from the database (datagridview). For example in the for loop, instead of creating an array and convert the values from the datagridview to decimal, to plot them straight in like:
chart1.Series["Loan_Balance"].Points.AddXY(i, dataGridView2[i, 0].Value);
the chart1.ChartAreas[0].AxisY.Interval = 50000;
behaves strangely, most of the times it doesn't work at all
The coloring problem is much more important if you could help me i would truly appreciate it.
Thanking you in advance
Best Regards
George Georgiou
You should be creating the points directly rather than using AddXY. This gives you full control over the point including it's color. So, rather than:
chart1.Series["Loan_Balance"].Points.AddXY(i+1, numbers[i]);
Use:
var point = new DataPoint(i+1, numbers[i]);
point.Color = numbers[i] < 0 ? Color.Red : Color.Black; // or whatever logic
point.Label = numbers[i].ToString(); // and so on
chart1.Series["Loan_Balance"].Points.Add(point);
You can see what other properties are available on the DataPoint here: http://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.datapoint.aspx