How to get actual columns range Spreadsheetgear - c#

I have an Excel worksheet which has 50 Rows and 38 columns "Filled" with actual data but when I try to get the .UsedRange I am getting More than 1025 Rows and 250 columns as Used range! Please let me know how can I get only actual Used ranges with filled data and retrive the actual range data using Spreadsheetgear lib? I tried something like, but when user added some column, it's does not work.
var workbook = Factory.GetWorkbook(filePath);
var worksheet = workbook.Worksheets[0];
var cells = worksheet.UsedRange;
var headerCount = 0;
// get columns size
for (var columnCount = 0; columnCount <= range.ColumnCount; ++columnCount)
{
if (columnCount > headers.Length && string.IsNullOrEmpty(range[HeaderRow, columnCount].Text))
{
headerCount = columnCount - 1;
break;
}
}

SpreadsheetGear (and Excel itself) will include cells into the UsedRange that are empty but have formatting of some kind. For instance, if cell ABC123 has a custom NumberFormat, Font color or similar, the UsedRange will include that cell, thereby potentially blowing up your UsedRange A1:ABC123 even if the actual "value-populated" or "filled" portion of the worksheet is much smaller.
If this is a problem and you need to only include the portion of a sheet that is actually populated with cell values, below is one possible routine you might be able to use to only include cells that have cell values of some kind. It is written as an extension method off of IWorksheet to make utilization of it easier, and includes a bool flag that you can pass in so as to return either the "normal" UsedRange (including cells with other types of formatting) or the "altered" UsedRange discussed here:
public static class SGExtensionMethods
{
public static IRange GetUsedRange(this IWorksheet worksheet, bool ignoreEmptyCells)
{
IRange usedRange = worksheet.UsedRange;
if (!ignoreEmptyCells)
return usedRange;
// Find last row in used range with a cell containing data.
IRange foundCell = usedRange.Find("*", usedRange[0, 0], FindLookIn.Formulas,
LookAt.Part, SearchOrder.ByRows, SearchDirection.Previous, false);
int lastRow = foundCell?.Row ?? 0;
// Find last column in used range with a cell containing data.
foundCell = usedRange.Find("*", usedRange[0, 0], FindLookIn.Formulas,
LookAt.Part, SearchOrder.ByColumns, SearchDirection.Previous, false);
int lastCol = foundCell?.Column ?? 0;
// Return a new used range that clips of any empty rows/cols.
return worksheet.Cells[worksheet.UsedRange.Row, worksheet.UsedRange.Column, lastRow, lastCol];
}
}
A couple additional notes about this approach. It does generally take into account hidden rows or columns--meaning hidden rows or columns that have cell values will be included in the UsedRange. One exception / edge-case to this inclusion of hidden rows or columns is if AutoFilters is enabled on the worksheet. This puts the worksheet in a special "mode" that excludes hidden rows from search results. So if AutoFilters is enabled (in such cases IWorksheet.AutoFilterMode will be true), you may not be able to rely on this approach if the last row(s) or column(s) of the AutoFiltered range could possibly have been filtered out.

Related

How to know editable cell address in merged cells

enter image description here
Here, there are four cells(A1,B1,C1,D1) have been merged, I would like to get the editable cell address(A1).
According to Excel A1 Cell able to edit, others not editable.
I could able to get merged cells count as 4 using below code.
Also I can get whether cell has been merged or not.
But not able to get the address of editable cell(A1) in merged cells.
Microsoft.Office.Interop.Excel.Application appl = null;
appl = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook wbk;
wbk = appl.Workbooks.Open("C:\\Users\\test\\Desktop\\test.xlsx");
Worksheet sheet = wbk.Worksheets.get_Item(1);
Range range = sheet.Cells[1, 2]; //B1 Cell
var a = range.Cells.MergeArea.Count; // can be able to get merged count cells
string b = range.Address;
**int ab = range.Cells.Areas.Count;**
Range ac = range.Cells.Areas.get_Item(1);
for (int i = 1; i <= 4; i++)
{
**if (sheet.Cells[1, i].Mergecells)** //merged cell
{
MessageBox.Show("Merged");
}
else //Unmerged cell
{
MessageBox.Show("UnMerged Cells");
}
}
wbk.Save();
wbk.Close(false);
appl.Quit();
The MergeArea property of a Range is again a Range. If you have a cell, just use something like the following to get the first (=top left) cell of the merged area (untested in C# as I don't have it available, but works in VBA and should also work for C#):
Range range = sheet.Cells[1, 2];
Range firstCell = range.MergeArea.Cells[1, 1];
This works even if a cell is not merged, MergeArea is always set, so on an unmerged cell range and firstCell would point to the same cell.
If I correctly understood your real need, please try the next approach. It is based on the fact that a merged area keeps the value in its first cell:
'your existing code...
if (sheet.Cells[1, i].Mergecells) //merged cell
{
MessageBox.Show(sheet.Cells[1, i].MergeArea.cells[1, 1].Value);
}
else //Unmerged cell
{
MessageBox.Show(sheet.Cells[1, i].Value);
}
'your existing code...
Here is the solution
string mergedCellsAddress = mergedRange.Address;
string[] firstMergedCell = mergedCellsAddress.Split(':');
string firstCell = firstMergedCell.GetValue(0).ToString().Replace("$", "");

C# Reorder columns of Excel worksheet

I have an Excel worksheet with some data. I also have a List of the column headers of the worksheet. The headers in the list are in a different order than the headers in the worksheet, and I need to reorder the Excel worksheet's columns to be the same order as the list.
List<string> dataset1Variables = new List<string>() { "Variable1", "Variable2", "Variable3", "Variable4" };
The headers of my Excel sheet may look like this:
Variable 3 | Variable 1 | Variable 4 | Variable 2
I have come across this code to shift columns but this is only for moving 1 column to a specific location. The list might be completely mixed up so I would need to shift many columns.
Excel.Range copyRange = xlWs.Range["C:C"];
Excel.Range insertRange = xlWs.Range["A:A"];
insertRange.Insert(Excel.XlInsertShiftDirection.xlShiftToRight,
copyRange.Cut());
What would be the best approach for doing this? Preferably using Interop.
If you have an empty row right above your worksheet with data, you can add matching formula right above headers. In below assuming your list is on Sheet2 range B2:B5 and your data headers start on Sheet1 range A2:D2
Excel.Workbook myBook = xlApp.Workbooks.Open(#"path\excel.xlsx");
Excel.Worksheet ws = myBook.Worksheets[1];
// You can use all dynamic ranges instead
// Excel.Range xlRng = ws.Range[ws.Cells[yourRow, firsColumn], ws.Cells[yourRow, lastColumn]];
Excel.Range xlRng = ws.Range[ws.Cells[1, 1], ws.Cells[1, 4]]; // ws.Range["A1:D1"];
xlRng.FormulaR1C1 = "=MATCH(R[-1]C,Sheet2!R2C2:$R5C2,0)";
// Below is how you can get full address for your list if it's in different workbook and replace Sheet2!R2C2:$R5C2 with rangeFullAddress
// string rangeFullAddress = xlRng.Address[true,true,Excel.XlReferenceStyle.xlR1C1,true];
xlRng.Calculate();
xlRng.Value = xlRng.Value;
After this you can just sort your data using Excel Sort by 1st row. After sorting your 1st row it would look like this: 1, 2, 3, 4. Then clear data on 1st row ws.Range["A1:D1"].Clear();.
Your sort key would be Key1: ws.Range["A1:D1"], here is c# sort example using c# to sort a column in excel only change Excel.XlSortOrientation and adjust for your range.
There are other ways to sort but this way you'll keep individual formats, comments of each cell within your data
I missed 1 important detail - that your list is not in Excel sheet, but you can add it to Excel, perhaps in temporary workbook or right on the same sheet:
object [,] columnHeaders = new object[3,0]; // or object[0,3]; if you'd like to add into 1 row
columnHeaders[0, 0] = "Variable1";
columnHeaders[1, 0] = "Variable2";
columnHeaders[2, 0] = "Variable3";
columnHeaders[3, 0] = "Variable4";
xlRng.FormulaR1C1 = columnHeaders; // xlRng would be in the above Sheet2!R2C2:$R5C2

Auto expansion of formatted table in Excel using C#

I was trying to insert a new row at the end of a worksheet using OLEDB. The worksheet has a format table in a Range (a1:xx), with format and formula stored. But OLEDB insert does not come with any format.
I have read the post How to copy format of one row to another row in Excel with c# talking about get the format, but doesn't work for me. Also, I don't think it will get the formula.
In the Excel UI, at the lower right corner of a formatted table, a double arrow would appear, and we can drag it to expand the format table range.
Anything we could do through C#?
Thanks.
Excel.Range last = xlWS.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
Excel.Range RngToCopyOri = xlWS.get_Range("A1", last).EntireRow;
Excel.Range RngToCopy = RngToCopyOri.Resize[RngToCopyOri.Rows.Count + 1, RngToCopyOri.Columns.Count]; //because insert will add only 1 row, so the range would be one row larger
Excel.Range RngToInsert = xlWS.get_Range("A1", Type.Missing).EntireRow;
RngToInsert.Insert(Excel.XlInsertShiftDirection.xlShiftDown, RngToCopy.Copy(Type.Missing));
I tried to copy Range(A1,lowerleft cell) to its original location, but nothing changed.
I tried Range.resize, autofill, autoformat. All of them has sort of problems. I finally gave up using OLEDB to insert data. Instead, i used
worksheet.UsedRange.Item[rowNo,getColumnIndex(worksheet,columnTitle)]=value
private int getColumnIndex(Excel.Worksheet sheetname, string header) {
int index=0;
Excel.Range activeRange=sheetname.UsedRange;
for (int i = 1; i <= activeRange.Columns.Count; i++) {
if (header == (string)(activeRange.Item[1,i] as Excel.Range).Value) {
index = i;
}
}
if(index==0)
throw some exception you like;
return index;
}
The getColumnIndex function aims to locate the column in SELECT [column] from...
In this way, the format table will automatically expand to the range you input the value.

Read Excel Cell And Set Row Colour

I am using Com Interop and C#. I have to iterate through an Excel file looking for certain values in each of the rows (always in column 2). For some values I need to set the background colour of the row to red.
I am having trouble:
Reading the value in cell [i][2] for row i, and
Setting the background colour of this row.
Basically I am looking for something like this (which is the best I can find after much Googling):
// ws is the worksheet
for (int i = 1; i <= ws.Rows.Count; i++)
{
Range range = ws.Cells[i][2];
int count = Convert.ToInt32(range.Value2.ToString());
if (count >= 3)
{
Range chronic = ws.UsedRange.Rows[i];
chronic.EntireRow.Cells.Interior.Color = 0xFF0000;
}
}
Of course this doesn't work. I can't get past the first hurdle of just reading the cell. Any advice is appreciated.
Try this. The code assumes that the value in the column 2 cell is a number.
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
Missing noValue = Missing.Value;
Excel.Range conditionalCell;
foreach (Excel.Range usedRange in ws.UsedRange.Rows)
{
conditionalCell = usedRange.Cells[noValue, 2] as Excel.Range;
if (Convert.ToInt32(conditionalCell.Value2) >= 3)
{
usedRange.Cells.Interior.Color = Excel.XlRgbColor.rgbRed;
}
}

.net c# excel column AutoFit

I'm working with an excel object in c#. I want to auto-fit the columns, but like this: I want the columns' width to be 5 bigger than what the AutoFit method set.
How can I get the width after AutoFit() is used?
How can I make the columns 5 bigger than this width?
If you wish to use the Selection object and have IntelliSense with early binding, you need to cast the Selection object to a Range first:
Excel.Range selectedRange = (Excel.Range)myExcelApp.Selection;
selectedRange.Columns.AutoFit();
foreach (Excel.Range column in selectedRange.Columns)
{
column.ColumnWidth = (double)column.ColumnWidth + 5;
}
-- Mike
Assuming that you are on cell A1 & have long text in it, following code will make the column Autofit and then increase the width by 5 characters.
Selection.Columns.Autofit
Selection.Columns(1).ColumnWidth = Selection.Columns(1).ColumnWidth + 5
Try to loop through your rows to get the text length of it:
var row = 1;
ws.Column(1).AutoFit(ws.Cells[row, 1].Text.Length + 5);
Where ws is your Worksheet:
var pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Plan1")
Try Like this,
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Sheet1");
//Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
ws.Cells["A1"].LoadFromDataTable(data_table, true);
//Set full Sheet Auto Fit
ws.Cells[1, 1, data_table.Rows.Count, data_table.Columns.Count].AutoFitColumns();

Categories