C# Select specific rows in existing Excel file - c#

Hi I'd like to know how to highlight cells with same value in existing Excel file. My excel file contains more rows with names. I want highlight names what are equal as I use in textbox.
string workbookPath = #"PathOfExcelFile";
_Excel.Application ExcelApp = new _Excel.Application();
ExcelApp.Visible = true;
_Excel.Workbook workbook = ExcelApp.Workbooks.Open(workbookPath);
_Excel.Worksheet worksheet = workbook.ActiveSheet;
_Excel.Range selectRange;
selectRange = worksheet.get_Range(""); //
Thank you

This is my proposal to highlight cells :
//find last cell if needed
int lastCell = xlWorkSheet.Cells.Find(
"*",
System.Reflection.Missing.Value,
Excel.XlFindLookIn.xlValues,
Excel.XlLookAt.xlWhole,
Excel.XlSearchOrder.xlByRows,
Excel.XlSearchDirection.xlPrevious,
false,
System.Reflection.Missing.Value,
System.Reflection.Missing.Value).Row;
Excel.Range rangeCheck = xlWorkSheet.Range["A1:A" + lastCell];
foreach (Excel.Range cell in rangeCheck.Cells)
{
string checkStrigng = Convert.ToString(cell.Value);//converts cell value to string for comparision
if (checkString == textbox.Text)
{
cell.Interior.Color = System.Drawing.Color.Yellow;
}
}

Related

How to read key value pairs from excel sheet using c#?

I'm new to C#. I have used the code below to read data from Excel, but I need help modifying it to read key-value pairs.
public String getData(int row, int col, String var)
{
Excel.Application excelApp = new Excel.Application();
if (excelApp != null)
{
List<string> prop = new List<string>(var.Split(new string[] {"."}, StringSplitOptions.None));
Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(#"D:\\test.xlsx", 0, true, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelWorkbook.Sheets[prop[0]];
excelWorksheet.Select(Type.Missing);
Excel.Range range = (excelWorksheet.Cells[row, col] as Excel.Range);
string cellValue = range.Value.ToString();
excelWorkbook.Close();
excelApp.Quit();
return cellValue;
}
return null;
}
Here is an example; it assumes you have a using clause..
using Excel = Microsoft.Office.Interop.Excel;
..and prepared access to a worksheet:
Excel.Application xl = new Excel.Application();
xl.Workbooks.Open(filename);
Excel.Worksheet ws = xl.Workbooks[1].Worksheets[1]; // pick your sheet!
int keyColum = 3;
Now you can grab a Range of Cells:
Excel.Range keyRange= ws.Range["C3:C53"];
..or the whole column:
Excel.Range keyRange= ws.Range["C:C"];
And search all occurences of a search string:
Excel.Range keysFound = keyRange.Find(textBox1.Text);
Then you can access the range of found cells like this:
string msg1 = keysFound.Count + " records found.";
string msg2 = "1st in row " + keysFound.Row;
string msg3 = "value from next column is "
+ ws.Cells[keysFound.Row + 1, keyColum + 1].value;
notes:
indexing start with 0 in c# but not in excel (hence [keysFound.Row + 1, )
my value column is one column right of the keys. Best use named indices!
if nothing is found keysFound will be null! (do add a check!)
since you want to match a whole key, you will want to do an exact search:
Excel.Range keysFound = keyRange.Find(textBox1.Text, LookAt: Excel.XlLookAt.xlWhole);
I still think grabbing all data and stuffing them into a Dictionary will be the cleanest and fastest solution, unless, that is you only need to do one lookup..

c# working on excel files with large data

I'm copying data from first sheet of different excel files to a single workbook. I already have tried it with different alternatives like npoi, spire.xls and Interop which works good, but it kills too much of my time. It would really be thankful if anyone can suggest me with a better one. Been through many forms on the web, but couldn't find.
FYI: Each of My files are more than 50 MB in size. A few being 10 MB or less.
This is one of which I have tried (Uses Spire.xls):
workbook = new Workbook();
//laod first file
workbook.LoadFromFile(names[0]);
//load the remaining files starting with second file
for (int i = 1; i < cnt; i++)
{
LoadFIle(names[i]);
//merge the loaded file immediately and than load next file
MergeData();
}
private void LoadFIle(string filePath)
{
//load other workbooks starting with 2nd workbbook
tempbook = new Workbook();
tempbook.LoadFromFile(filePath);
}
private void MergeData()
{
try
{
int c1 = workbook.ActiveSheet.LastRow, c2 = tempbook.Worksheets[0].LastRow;
//check if you have exceeded 1st sheet limit
if ((c1 + c2) <= 1048575)
{
//import the second workbook's worksheet into the first workbook using a datatable
//load 1st sheet of tempbook into sheet
Worksheet sheet = tempbook.Worksheets[0];
//copy data from sheet into a datatable
DataTable dataTable = sheet.ExportDataTable();
//load sheet1
Worksheet sheet1 = workbook.Worksheets[workbook.ActiveSheetIndex];
sheet1.InsertDataTable(dataTable, false, sheet1.LastRow + 1, 1);
}
else if ((c1 >= 1048575 && c2 >= 1048575) || c1 >= 1048575 || c2 >= 1048575 || (c1 + c2) >= 1048575)
{
workbook.Worksheets.AddCopy(tempbook.Worksheets[0]);
indx = workbook.ActiveSheet.Index;
workbook.ActiveSheetIndex = ++indx;
}
else
{
//import the second workbook's worksheet into the first workbook using a datatable
//load 1st sheet of tempbook into sheet
Worksheet sheet = tempbook.Worksheets[0];
//copy data from sheet into a datatable
DataTable dataTable = sheet.ExportDataTable();
//load sheet1
Worksheet sheet1 = workbook.Worksheets[workbook.ActiveSheetIndex];
sheet1.InsertDataTable(dataTable, false, sheet1.LastRow + 1, 1);
}
}
catch (IndexOutOfRangeException)
{
}
}
}
Well, this works good but as said takes a long time. Any suggestions are welcome. Thanks in advance.
Here is my (fastest I know of) implementation using Excel interop. Although I looked carefully to release all (must have missed one), 2 Excel instances remain in the processes list, they are closed after the program ends.
The key is to only have 2 Open Excel instances and to copy the data as a Block using Range.Value2.
//Helper function to cleanup
public void ReleaseObject(object obj)
{
if (obj != null && Marshal.IsComObject(obj))
{
Marshal.ReleaseComObject(obj);
}
}
public void CopyIntoOne(List<string> pSourceFiles, string pDestinationFile)
{
var sourceExcelApp = new Microsoft.Office.Interop.Excel.Application();
var destinationExcelApp = new Microsoft.Office.Interop.Excel.Application();
// TODO: Check if it exists
destinationExcelApp.Workbooks.Open(pDestinationFile);
// for debug
//destinationExcelApp.Visible = true;
//sourceExcelApp.Visible = true;
int i = 0;
var sheets = destinationExcelApp.ActiveWorkbook.Sheets;
var lastsheet = destinationExcelApp.ActiveWorkbook.Sheets[sheets.Count];
ReleaseObject(sheets);
foreach (var srcFile in pSourceFiles)
{
sourceExcelApp.Workbooks.Open(srcFile);
// get extends
var lastRow = sourceExcelApp.ActiveSheet.Cells.Find("*", System.Reflection.Missing.Value,
System.Reflection.Missing.Value, System.Reflection.Missing.Value, XlSearchOrder.xlByRows,
XlSearchDirection.xlPrevious, false, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
var lastCol = sourceExcelApp.ActiveSheet.Cells.Find("*", System.Reflection.Missing.Value, System.Reflection.Missing.Value,
System.Reflection.Missing.Value, XlSearchOrder.xlByColumns, XlSearchDirection.xlPrevious, false,
System.Reflection.Missing.Value, System.Reflection.Missing.Value);
var startCell = (Range) sourceExcelApp.ActiveWorkbook.ActiveSheet.Cells[1, 1];
var endCell = (Range) sourceExcelApp.ActiveWorkbook.ActiveSheet.Cells[lastRow.Row, lastCol.Column];
var myRange = sourceExcelApp.ActiveWorkbook.ActiveSheet.Range[startCell, endCell];
// copy the values
var value = myRange.Value2;
// create sheet in new Workbook at the end
Worksheet newSheet = destinationExcelApp.ActiveWorkbook.Sheets.Add(After: lastsheet);
ReleaseObject(lastsheet);
lastsheet = newSheet;
//its even faster when adding it at the front
//Worksheet newSheet = destinationExcelApp.ActiveWorkbook.Sheets.Add();
// change that to a good name
newSheet.Name = ++i + "";
var dstStartCell = (Range) destinationExcelApp.ActiveWorkbook.ActiveSheet.Cells[1, 1];
var dstEndCell = (Range) destinationExcelApp.ActiveWorkbook.ActiveSheet.Cells[lastRow.Row, lastCol.Column];
var dstRange = destinationExcelApp.ActiveWorkbook.ActiveSheet.Range[dstStartCell, dstEndCell];
// this is the actual paste
dstRange.Value2 = value;
//cleanup
ReleaseObject(startCell);
ReleaseObject(endCell);
ReleaseObject(myRange);
ReleaseObject(value);// cannot hurt, but not necessary since its a simple array
ReleaseObject(dstStartCell);
ReleaseObject(dstEndCell);
ReleaseObject(dstRange);
ReleaseObject(newSheet);
ReleaseObject(lastRow);
ReleaseObject(lastCol);
sourceExcelApp.ActiveWorkbook.Close(false);
}
ReleaseObject(lastsheet);
sourceExcelApp.Quit();
ReleaseObject(sourceExcelApp);
destinationExcelApp.ActiveWorkbook.Save();
destinationExcelApp.Quit();
ReleaseObject(destinationExcelApp);
destinationExcelApp = null;
sourceExcelApp = null;
}
I have tested it on small excel files and are curious how it behaves with larger files.

How to efficiently retrieve time values from an Excel file?

I need to retrieve time values from an Excel file.
Here is the structure of the document :
ID | COLUMN_1 | COLUMN_2
1 | 00:10:00 | 00:08:23
2 | 00:23:00 | 01:45:00
3 | 02:01:45 | 01:23:35
...
So my Excel workbook contains few time columns (formatted as "Hour") but when I change the format to "Standard" it displays float numbers.
I created a script using C# to read this document.
I need to copy the content of the column as a Time to another Excel file.
The script processes all columns and it populates another Excel workbook, but the INSERT doesn't work because it tries to insert a float to a time field.
How to get a Time value ?
foreach (DataRow row in rawDataTable.Rows)
{
// INSERT INTO ....
row[1] // Time column. How to get a Time value ?
// I retrieved something like 0,00684027777777778, I need something formatted as HH:mm:ss
// Because I need to copy the Time value to a Time field in another Excel workbook.
}
Excel date values are in days since 1/1/1900, so if you have a time value without date, you can multiply it by 86400 to get the seconds.
But if you want to copy a cell formatted as time between workbooks, you don't have to convert it, just copy it as a double and format the cell correctly.
Let me try to answer your question. The key of saving data is to format it, like #grahamj42 said.
Basically, I created one button. On click handler I read data from source Excel app and I save it to newly created target Excel app. I included notes so it is pretty clear.
You have to include at the top of page these directives:
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;
Here is complete code:
public class ExcelRow
{
public int ID { get; set; }
public string Date1 { get; set; }
public string Date2 { get; set; }
}
protected void btnCopy_Click(object sender, EventArgs e)
{
//source excel app
var sourceExcelApp = new Excel.Application()
{
Visible = false,
ScreenUpdating = false,
DisplayAlerts = false
};
//get source workBook - assuming that path of source excel file is in C:\\
Excel.Workbook sourceWorkbook = sourceExcelApp.Workbooks.Open(#"c:\\source.xls",
Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value);
//assuming that data is inside of first worksheet (WorkSheets[1])
Excel._Worksheet sourceWorksheet = (Excel._Worksheet)sourceWorkbook.Worksheets[1];
//get column ranges including header names
Excel.Range ID = sourceWorksheet.Range["A1"];
Excel.Range date1 = sourceWorksheet.Range["B1"];
Excel.Range date2 = sourceWorksheet.Range["C1"];
//get last row in table(from A1 to last cell in C column)
int lastRowCount = sourceWorksheet.UsedRange.Rows.Count;
var excelRow = new ExcelRow();
List<ExcelRow> excelRowList = new List<ExcelRow>();
//note that we start iteration from 1 in order not to read first cells values
for (int i = 1; i < lastRowCount; i++)
{
excelRowList.Add(new ExcelRow
{
ID = int.Parse(ID.Offset[i, 0].Value2.ToString()),
Date1 = date1.Offset[i, 0].Value2.ToString(),
Date2 = date2.Offset[i, 0].Value2.ToString()
});
}
//create new instance of target Excel app and
var targetExcelApp = new Excel.Application();
targetExcelApp.Visible = true;
Excel.Workbook targetWorkbook = targetExcelApp.Workbooks.Add();
Excel._Worksheet targetWorksheet = (Excel._Worksheet)targetExcelApp.ActiveSheet;
//set columns headers titles
targetWorksheet.Cells[1, "A"] = "ID";
targetWorksheet.Cells[1, "B"] = "COLUMN_1";
targetWorksheet.Cells[1, "C"] = "COLUMN_2";
//formating target rows
targetWorksheet.Range[String.Concat("B1:", "B", lastRowCount)].NumberFormat = "#";
targetWorksheet.Range[String.Concat("C1:", "C", lastRowCount)].NumberFormat = "#";
//fill targeted rows
int counter = 1;
foreach (var row in excelRowList)
{
counter++;
targetWorksheet.Cells[counter, "A"] = row.ID;
targetWorksheet.Cells[counter, "B"] = row.Date1;
targetWorksheet.Cells[counter, "C"] = row.Date2;
}
//autofit columns width
targetWorksheet.Columns.AutoFit();
//save data to new excel app
targetWorksheet.SaveAs("target", Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
//release resources
ReleaseObject(sourceWorksheet);
ReleaseObject(sourceWorkbook);
ReleaseObject(sourceExcelApp);
ReleaseObject(targetWorksheet);
ReleaseObject(targetWorkbook);
ReleaseObject(targetExcelApp);
}
And this is method for relasing excel resources:
public static void ReleaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
finally
{
GC.Collect();
}
}

DateTimePicker value from Excel

I am wondering if it is possible to set the value of a DateTimePicker from a queried row in excel. I have tried different ways of doing it but have never came close and anyone help?
Also it all is displayed on a windows form
public void ReadAndWriteToExcel()
{
string myPath = #"C:\Excel.xls";
FileInfo fi = new FileInfo(myPath);
if (!fi.Exists)
{
Console.Out.WriteLine("file doesn't exists!");
}
else
{
var excelApp = new Microsoft.Office.Interop.Excel.Application();
var workbook = excelApp.Workbooks.Open(myPath);
Worksheet worksheet = workbook.ActiveSheet as Worksheet;
// To write to excel
Microsoft.Office.Interop.Excel.Range range = worksheet.Cells[1, 1] as Range;
DateTime dt = dateTimePicker1.Value;
range.NumberFormat = "dd/MMM/yyyy";
range.Value2 = dt;
// To read,
var date = worksheet.Cells[1, 1].Value;
Console.Out.WriteLine(date.ToString());
workbook.Save();
workbook.Close();
}
}
you must add "Microsoft.Office.Core" and "Microsoft.Office.Interop.Excel" references to your project and use them.

How to access current row index of excel file?

public bool StoreInExecel(String name,String csFIleName)
{
int cellno = ;//Initialize to current row no
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook workbook = (Microsoft.Office.Interop.Excel.Workbook)excelApp.Workbooks.Add(Missing.Value);
Worksheet worksheet;
// Opening excel file
workbook = excelApp.Workbooks.Open(fileName, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
// Get first Worksheet
worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workbook.Sheets.get_Item(1);
// Setting cell values
((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[cellno,"B"]).Value2 = "5";
((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[cellno,"B"]).Value2 = "7";
workbook.Save();
workbook.Close(0, 0, 0);
//excelApp.Quit();
return true;
}
I want to initialize "cellno" to current row index of excel file so that data should not get overridden. Is there any specific function to get filled row count?
You can use
Excel.Range range = (Excel.Range)excelApp.ActiveCell;
int cellno = range.Row;
You can also get the used range in a worksheet using the UsedRange property.

Categories

Resources