c# read specific columns from excel file - c#

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);
}

Related

Identify and highlight duplicate records in Excel sheet using EPPlus library

For example, I have a sheet called EmployeeSheet, which is having multiple columns like FirstName, LastName, Address etc.. and needs to identify the duplicates records based on several columns then apply highlighter with comments to the entire row. And let's assume this list is perfectly formatted and has no duplicates so every cell is unique in this sheet.
Here's the solution I can think of but not able to figure out how to implement and always returns true.
public static bool HasDuplicates(string path)
{
List<object> allObjects = new List<object>();
using (ExcelPackage excel = new ExcelPackage(new FileInfo(path)))
{
// Go through all sheets
foreach (var sheet in excel.Workbook.Worksheets)
{
// Go through all cells
foreach (var cell in sheet.Cells)
{
// Ignore null cells
if(cell.Value != null)
{
if (allObjects.Contains(cell.Value))
return true;
allObjects.Add(cell.Value);
}
}
}
return false;
}
}

How to remove columns from ArrayList or GridView c#

Is there any way to remove "columns" from an ArrayList?
I got this site up and running before attempting populating my DropDownLists from txt files so I hard-typed each value in. Now I've made an ArrayList from each DropDownList so I can display those lists in DataGridView on the site. The only issue is that "Enabled" and "Selected" show up as columns and I cannot seem to remove the column in the ArrayList, or specify which columns to bring in when creating the ArrayList, or GridView using GridView.Columns.Remove(); because the integers 0 or 1 or 2 don't seem to correspond with anything and the site doesn't run and I can't specify a string as the column title for what to remove.
The DataGrids show up with columns as |Enabled|Selected|Text|Value|
Here's the code for this piece as it stands (You can see what I've tried out and that didn't work that I've commented away):
// Create ListArrays from DropDownLists
ArrayList BuildingList = new ArrayList(Building.Items);
ArrayList DepartmentList = new ArrayList(Department.Items);
//Building.Items.Remove("Enabled");
//Building.Items.Remove("Selected");
// Populate Building GridView
BuildingGrid.DataSource = BuildingList;
BuildingGrid.DataBind();
//BuildingGrid.Columns.Remove;
//BuildingGrid.Columns[0].Visible = false;
// Populate Department GridView
DepartmentGrid.DataSource = DepartmentList;
DepartmentGrid.DataBind();
//DepartmentGrid.Columns[0].Visible = false;
//DepartmentGrid.Columns[1].Visible = false;
I would just go ahead and create a simple 2d array in a txt file with fields for "Value" and "Text" so the DropDownList will pull it in properly, but I can't figure that out either without being terribly inefficient and confusing.
Any help would be appreciated. Thanks.
So, here's the solution I ended up at. I finally figured out how to extract everything from a txt file, and place it into the grid the way I wanted to.
// Populate Department GridView
// get all lines of csv file
string[] BuildingString = File.ReadAllLines(Server.MapPath("Content/BuildingsCSV.csv"));
string[] DepartmentString = File.ReadAllLines(Server.MapPath("Content/DepartmentsCSV.csv"));
// create new datatable
DataTable BuildingTable = new DataTable();
DataTable DepartmentTable = new DataTable();
// Building Table
// get the column header means first line
string[] tempbuild = BuildingString[0].Split(',');
// creates columns of gridview as per the header name
foreach (string t in tempbuild)
{
BuildingTable.Columns.Add(t, typeof(string));
}
// now retrive the record from second line and add it to datatable
for (int i = 1; i < BuildingString.Length; i++)
{
string[] t = BuildingString[i].Split(',');
BuildingTable.Rows.Add(t);
}
// Department Table
// get the column header means first line
string[] tempdept = DepartmentString[0].Split(',');
// creates columns of gridview as per the header name
foreach (string t in tempdept)
{
DepartmentTable.Columns.Add(t, typeof(string));
}
// now retrive the record from second line and add it to datatable
for (int i = 1; i < DepartmentString.Length; i++)
{
string[] t = DepartmentString[i].Split(',');
DepartmentTable.Rows.Add(t);
}
// assign gridview datasource property by datatable
BuildingGrid.DataSource = BuildingTable;
BuildingGrid.DataBind();
BuildingGrid.Rows[0].Visible = false;
DepartmentGrid.DataSource = DepartmentTable;
DepartmentGrid.DataBind();
DepartmentGrid.Rows[0].Visible = false;
foreach (DataRow drb in BuildingTable.Rows)
{
BuildingDrop.Items.Add(new ListItem(drb[0].ToString(), drb[1].ToString()));
}
foreach (DataRow drd in DepartmentTable.Rows)
{
DepartmentDrop.Items.Add(new ListItem(drd[0].ToString(), drd[1].ToString()));
}

Writing a excel using closed XML - adding custom column names

I need to write a list of objects to excel sheet as a table, in which each column represents object attributes or values. To the below method, Im passing column names in a separate List and data objects in a List, I managed to get the data displayed like below, but still I could not get the columns to display properly.
I referred the below documentation, but I could not find a way to get the titles display properly.
https://github.com/closedxml/closedxml/wiki/Inserting-Tables
Code
public string CreateExcelFile<T>(IEnumerable<T> list, string sheetName, string headerTitle, List<string> titles, string fileName, string savedPath)
{
var wb = new XLWorkbook();
var ws = wb.Worksheets.Add(sheetName);
ws.Cell(1, 1).Value = headerTitle; // sets excel sheet header
var rangeTitle = ws.Range(3, 1, 3, titles.Count); // range for row 3, column 1 to row 3, column titles.Count
rangeTitle.AddToNamed("Titles");
// Need to add columns names with in rangeTitle
//rangeTitle.InsertData(titles);
// write data from row 4 onwards
if (list != null && list.Any())
{
ws.Cell(4, 1).InsertData(list);
}
else
{
ws.Cell(4, 1).Value = "No data to show";
}
// styles
var titlesStyle = wb.Style;
titlesStyle.Font.Bold = true;
titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
titlesStyle.Fill.BackgroundColor = XLColor.Amber;
// style titles row
wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle;
ws.Columns().AdjustToContents();
var filePath = savedPath + string.Format("{0}.xlsx", fileName);
wb.SaveAs(filePath);
return filePath;
}
Output excel
Output Im trying to get - I need to get values stored in titles in the Yellow highlighted row.
Can anyone help?
You could use InsertTable. The data is inserted as an Excel Table:
ws.Cell(1, 1).InsertTable(list.AsEnumerable());
I managed to get the columns to display by doing below.
// Need to add columns names with in rangeTitle
for (int i = 0; i < titles.Count; i++)
{
var columnNumber = i + 1;
ws.Cell(3, columnNumber).Value = titles[i];
}
This works for now. But, I wonder is there a better way to doing things (without manually assigning column names like above).
ws.Cell(3, 1).Value = new [] { titles };
If you set Value to an array, ClosedXML will write each object in the array to its own row, with one property of the object per column. (See https://github.com/ClosedXML/ClosedXML/wiki/Copying-IEnumerable-Collections)
In this case, the array we're passing in has only one object – an array of titles. That inner array gets written to the target row, and each item in the inner array gets written to a column in that row.

Export on .xlsxm file

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();
}
}

Read Excel Cell Format

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));
}
}
}
}

Categories

Resources