I have a Windows Form with a data grid view where I can input some data.
My C# code simply reads these data from the data grid view, processes it and then needs to plot the results.
In particular, the results are stored in a list. There are also some additional data (which are intermediate results of the calculations) that are stored in two dimensional arrays.
The point where I'm stuck is the plotting. I want to do this in Excel and I want to make it update dynamically whenever I change some input in the data grid view.
The code below open an Excel workbook and pastes the data of the list in column A.
how can I add the two dimensional array in column B and C?
how can I plot these columns in a line chart?
how can I make the Excel chart update automatically as soon as I update some value in my data grid view (without opening every time a new Excel workbook)?
Thank you for the help.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Excel = Microsoft.Office.Interop.Excel;
namespace NewPoject
{
class plotter
{
public void plotGraphExcel_2D(List<double> x, double[,] y, double[,] yUP, double[,] yDOWN)
{
Excel.Application xlApp;
Excel.Workbook workbook;
Excel.Worksheet sheet;
object misValue = System.Reflection.Missing.Value;
xlApp = new Excel.Application();
xlApp.Visible = true;
workbook = xlApp.Workbooks.Add(misValue);
sheet = (Excel.Worksheet)workbook.Worksheets.get_Item(1);
var range = sheet.get_Range("A1", "A1");
range.Value2 = "Scores";
//now the list
string cellName;
int counter = 2;
foreach (var item in x)
{
cellName = "A" + counter.ToString();
var range2 = sheet.get_Range(cellName, cellName);
range2.Value2 = item.ToString();
++counter;
}
// How can I save the results of the array y in column B and C?
// How can I plot a line chart with the values I have pasted in column A, B and C?
// How can I make it update automatically when I change some value in the data grid view?
}
}
}
Related
i have a tables like this
and i added checkboxs elements to form like this
i want to add the checkbox element text to datagridview then read the checked columns from excel file
if Date, Time, Price are checked datagridview will be like this
then get full Date column from excel file and add it to Date column in datagrid
my code to add checked boxes text as a columns in datagridview
DataTable dt = new DataTable();
foreach (Control checkbox in pnl.Controls)
if (checkbox.GetType() == typeof(CheckBox) && ((CheckBox) checkbox).Checked)
{
string txt = ((CheckBox)checkbox).Text;
dt.Columns.Add(new DataColumn(txt, typeof(object)));
}
datagrid.DataSource = dt;
There are a few steps which are needed before being able to grab data from an Excel file. So without your code, I don't know how much of this you have done. But here is the full explanation.
First
You have to add a reference to the Microsoft.Office.Interop.Excel dll (this assumes you aren't using epplus or another Nuget package). This link describes how to do this: How to reference Microsoft.Office.Interop.Excel dll?
Second
Include this library in whichever source file it is needed, and initialize an excel application (you'll also want InteropServices included):
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
// Global excel app object to be used anywhere
public Application ExcelApp;
// Intitializes an excel application by looking for an active one,
// and creating a new one if none are active
public void InitExcelApp()
{
try
{
ExcelApp = (Application)Marshal.GetActiveObject("Excel.Application")
}
catch(COMException ex)
{
ExcelApp = new Application
{
Visible = true
};
}
}
Third
You must initialize a workbook object. Here is how I do it, but my solution assumes you know the path to the desired Excel workbook:
Workbook myWorkbook = null;
// Checks open workbooks first
// Note that the path must be windows style. Ex: "C:\\Desktop\\myWorkbook.xlsx"
foreach (Workbook openWorkbook in ExcelApp.Workbooks)
{
if (openWorkbook.FullName == "<path to workbook>")
{
myWorkbook = openWorkbook;
}
}
// If no open workbooks were found at the known path, try opening one
if(myWorkbook is null)
{
myWorkbook = Excelapp.Workbooks.Open("<path to workbook>", Editable: true);
}
Fourth
Get the data you want. There are several ways to do this, and mine might not be the most efficient, but it works. In the code below I have included parameter names to hopefully make it more understandable.
// Gets the names of the checked items from the DataTable with columns you already added
// You could get these names from your checkboxes' names if you preferred
List<string> checkItems = new List<string>();
foreach (System.Data.DataColumn column in dt.Columns)
{
checkItems.Add(column.ColumnName);
}
// This dictionary holds info helpful for getting the correct data from excel:
// Key: a string containing the name of the column header, i.e. Date, Time, Price, etc.
// Value: an integer containing the number of that column in excel
Dictionary<string, int> excelColumnsInfo = new Dictionary<string, int>();
for (int columnNum = 1; columnNum <= myWorkbook.UsedRange.Columns.Count; columnNum++)
{
string columnHeader = myWorkbook.Cells[RowIndex: 1, ColumnIndex: columnNum].Value2.ToString();
if (checkedItems.Contains(columnHeader))
{
excelColumnsInfo.add(columnHeader, columnNum);
}
}
// Populates the data table with the data you need
// Start at row 2 to ignore the excel sheet's column headers
for (int rowNum = 2; rowNum <= myWorkbook.UsedRange.Rows.Count; rowNum++)
{
System.data.dataRow newRow = dt.NewRow();
foreach (KeyValuePair<string, int> columnInfo in excelColumnsInfo)
{
newRow[columnName: columnInfo.Key] = myWorkbook.Cells[RowIndex: rowNum, ColumnIndex: columnInfo.Value].Value2.ToString();
}
dt.Rows.Add(newRow);
}
I am trying to develop a reusable List to excel method. Using this example, "https://msdn.microsoft.com/en-us/library/dd264733.aspx"
I see the cells are being referenced individually like so,
workSheet.Cells[1, "A"] = "ID Number";
I want to be able to auto fill the column names, and also each cell from left to right dynamically. I would imagine there is a better way than having to type out "A", "B"....."A1" etc into the workSheet.Cells[] directly or even a better way than storing the alphabet into a list.
Here is what I have so far,
public static void DownloadExcelFile<T>(List<T> list, string fileName)
{
Excel.Application excelApp = new Excel.Application();
// Make the object visible.
excelApp.Visible = true;
excelApp.Workbooks.Add();
Excel._Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;
//create column headings
foreach (System.ComponentModel.PropertyDescriptor descriptor in System.ComponentModel.TypeDescriptor.GetProperties(list))
{
//how can I reference workSheet.Cells by row, and have the
//column incremented somehow within the loop?
workSheet.Cells[1, "A"] = descriptor.name;
}
}
You can use the ordinal number instead of the letter code:
int colIndex = 1;
foreach (System.ComponentModel.PropertyDescriptor descriptor in System.ComponentModel.TypeDescriptor.GetProperties(list))
{
workSheet.Cells[1, colIndex++] = descriptor.Name;
}
The title already explains my problem pretty well. I have an excel file which contains checkboxes and I would like to read their state (checked or not) using the EPPlus library.
I am not sure if this is even supported. So far I have found no documentation or examples for that specific problem using EPPlus.
If you add a Cell link then pulling the value is straight forward. I don't believe that the Drawing Object contains the value.
using System.Linq;
using OfficeOpenXml;
using OfficeOpenXml.Drawing;
namespace EPPlus {
public void Run() {
var excelFile = new System.IO.FileInfo(System.IO.Path.Combine(BaseDirectory, "Excel", "Checkbox.xlsx"));
using (ExcelPackage excel = new ExcelPackage(excelFile))
{
ExcelWorksheet sheet = excel.Workbook.Worksheets.SingleOrDefault(a => a.Name == "Sheet1");
ExcelDrawing checkbox2 = sheet.Drawings.SingleOrDefault(a => a.Name == "Check Box 2");
var value = sheet.Cells["G5"].Value.ToString();
}
}
}
}
For existing excel, just designate a cell somewhere and link it to the checkbox. Insert the true/false value directly to that cell (NOT TO THE CHECKBOX). The checkbox will automatically reflect the value of the cell in the checkbox.
You can put all designated cells in a certain column, then hide that column. :)
In my WinApp I export data to a specific tab of an Excel spreadsheet, in which there are macros (file extension .xlxm).
In this workbook, the data is always inserted from the same cell when it is empty, but it can happen that has already had the previous entries, so you have to retrieve the first available blank cell on the new line after the one that has already had the data inserted. The sequence of entries in the cells is similar to the following: in cells A1: A3, and then such as A10: A15, dropping the cells A4 to A9 because they are cells with formulas. I would to add that I must to control every cell of workbook for to fill in data from winApp. I hope to be able to explain the scenario.
You could use a library such as EPPlus from NuGet to achieve this. Something like this would do the trick.
static void Main(string[] args)
{
List<string> ExampleData = new List<string> { "my", "intestesting", "data" };
using (ExcelPackage package = new ExcelPackage(new FileInfo(#"C:\Temp\example.xlsm")))
{
ExcelWorksheet ws = package.Workbook.Worksheets["MySheet"];
int lastRowIndex = ws.Dimension.End.Row;
int idx = lastRowIndex + 1;
foreach (var datum in ExampleData)
{
ws.Cells[idx, 1].Value = datum;
idx++;
}
package.Save();
}
}
I'm working on this program that will read the data in excel file and put it into our database. The program is written in Visual Studio 2010 using C#, and I'm using the NPOI library.
In the past, I was able to read the spreadsheet row by row and cell by cell to get the data, but the new format of the excel file will not allow me to do this easily. (The excel is given by another user, so I can't really make big changes to it).
There are several "tables" in one sheet (using borders and headers for each column name), and I will need to get data mainly from the tables but sometimes outside the tables too.
I was wondering if I were to read the spreadsheet row by row (which is what I'm a bit for familiar with), is there a way I can tell that I have reached a table? Is there a way I can read the "format" of the cell?
What I mean is, for example, "this cell has borders around it so starting this row is a table." or "the text in this cell is bold, so this row is the header row for this new table."
In the past I was only able to read the "text" for the spreadsheet and not the format/style. I've been searching on the internet and I can only find how to set the style for output excel but not how to read the format from input.
Any help is appreciated, thanks!
It would be better to have the various tables in your source workbook defined as named ranges with known names. Then you can get the associated area like this -
using System.IO;
using System.Windows;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
// ...
using (var file = new FileStream(workbookLocation, FileMode.Open, FileAccess.Read))
{
var workbook = new XSSFWorkbook(file);
var nameInfo = workbook.GetName("TheTable");
var tableRange = nameInfo.RefersToFormula;
// Do stuff with the table
}
If you have no control over the source spreadsheet and cannot define the tables as named ranges, you can read the cell formats as you suggest. Here is an example of reading the TopBorder style -
using (var file = new FileStream(workbookLocation, FileMode.Open, FileAccess.Read))
{
var workbook = new XSSFWorkbook(file);
var sheet = workbook.GetSheetAt(0);
for (int rowNo = 0; rowNo <= sheet.LastRowNum; rowNo++)
{
var row = sheet.GetRow(rowNo);
if (row == null) // null is when the row only contains empty cells
continue;
for (int cellNo = 0; cellNo <= row.LastCellNum; cellNo++)
{
var cell = row.GetCell(cellNo);
if (cell == null) // null is when the cell is empty
continue;
var topBorderStyle = cell.CellStyle.BorderTop;
if (topBorderStyle != BorderStyle.None)
{
MessageBox.Show(string.Format("Cell row: {0} column: {1} has TopBorder: {2}", cell.Row.RowNum, cell.ColumnIndex, topBorderStyle));
}
}
}
}