I'm trying to use the Excel Interop C# libraries to copy cell formatting from one cell to a range of other cells. To do this, I'm using the following code:
var excelWrapper = new Application();
excelWrapper.DisplayAlerts = false;
var workBook = excelWrapper.Workbooks.Open(#"C:\myFile.xlsx");
Worksheet ws = workBook.Sheets[1] as Worksheet;
Range formatSource = ws.Cells[8, 1] as Range; //cell containing formatting that I want to apply
Range startCell = ws.Cells[9, 1] as Range; //first cell to format
Range endCell = ws.Cells[300, 1] as Range; //last cell to format
Range formatTarget = ws.Range[startCell, endCell];
//Copy formatting from source cell to destination range
formatSource.Copy();
formatTarget.PasteSpecial(XlPasteType.xlPasteFormats, XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
As you can see, I'm applying the format from cell [8,1] to the entire range [9,1] -> [300,1].
The problem I'm having is that this only applies the formatting to the first ~80 or so cells, after that the formatting is not applied. In particular, the formatting stops after row 91, so it is only applied to the first 84 of the 291 cells in the range.
I don't understand why the formatting is not applied to the entire range I selected, but only to the first 84 cells. What am I missing here? Note that the range I'm applying the style to starts empty (I write the data only after setting the formatting), so it isn't a problem derived from the data in the cells.
Well, I figured it out: the excel file looked empty, but actually it contained a table that stopped at row 91. Evidently, when you paste a format to a range that is "hybrid" (half inside table and half normal cells) the format only gets applied to the portion inside the table.
Related
I have an Excel sheet formatted as the image below.
I would like to retrieve the list of cell texts next to a merged column. I am thinking something of this kind. My idea is to get the total number of merged cells and then iterate over the next set of cells. I cannot find any method in Range that returns the total number of cells in a merged cell. The properties EntireRow and EntireColumn doesn't give the total number of cells merged. Any hints would be helpful.
List<String> GetNextTexts(Microsoft.Office.Interop.Excel.Range range)
{}
Examples
GetNextTexts(rangeTest) should return {TestRx, TestSPC}
GetNextTexts(rangeTestRx) should return {P4_7, P0_0, P0_14, P3_2}
GetNextTexts(rangeTestSPC) should return {P2_4}
Here are some Range properties that can be useful (assuming rangeTest is in B2:B6) :
rangeTest = Range["B2"].MergeArea gives the range of the merged cell Test - B2:B6
rangeTest.Rows.Count and rangeTest.Count give the number of rows and number of cells - 5
rangeTest.Offset[0, 1] gives the range C2, and .MergeArea of that gives the rangeTestRx.
rangeTestRx.Count can be used to get the number of cells in rangeTestRx (4), and compare that to the number of cells in rangeTest to check if there is another cell below rangeTestRx.
rangeTestRx.Offset[4, 0] can be used to get the Range below rangeTestRx - C6 and so on.
An excel file is generated by a third party app. I am writing a utility to further process that Excel file. When I read a column containing date value, I am getting a numeric string like 05/02/2016 (dd/MM/yyyy) is coming as 42405.
I have tried using .Text, .Formula, .Value, .Value2 - all the approaches are returning the numeric value.
I know about FromOADateTime, but my requirement is to read the date value as a string, exactly the way it is displayed on the screen.
Thanks.
EDIT 1:
When I convert the column to Text in Excel, by using Format Cells and selecting Text, all the date values convert to numeric values even in Excel! Does that give any clue? Probably the third party app stores the data as numeric value, but Excel displays it as a date string on screen. I want to read the value displayed on screen.
EDIT 2:
This problem does not come with all the date values in that column. My first impression is that the third party app is not consistent while writing date values to Excel. But I have no control over it.
EDIT 3:
Here is the link to download file: http://wikisend.com/download/316956/Prob.xls . In this file, I need to read all date values in Column B as text starting from Row 13
And here is the link to the screenshot of how it looks in my machine: http://wikisend.com/download/443994/Screenshot1.jpg
EDIT 4:
Found the culprit in my code with the help of NineBerry's answer: a few lines before reading the Text property, I was calling ws.Columns.ClearFormats() and ws.Rows.ClearFormats();
I needed to do this as per this: How to get the range of occupied cells in excel sheet…, in order to correctly get the used range in the sheet!
The original problem is solved. Now the problem is how to use ws.UsedRange without calling ClearFormats() to correctly get the range of used cells.
My specific requirement is to get the range of used ROWS having data in any cells up to Col H. I Dont need the entire UsedRange in Excel, UsedRange within Col H is good enough for me.
Any solutions to this? Or should I post a new thread?
Using the Text property does give the string that would be displayed to a user.
private void button_Click(object sender, EventArgs e)
{
string path = #"C:\Test.xlsx";
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = excel.Workbooks.Open(path);
Worksheet excelSheet = wb.ActiveSheet;
//Read a specific cell
Range cell = excelSheet.Cells[1, 3] as Range;
// Show text content
MessageBox.Show(cell.Text);
wb.Close();
}
If you don't see here what you see in Excel, you have to give us more details. Maybe provide a sample Excel file where a cell does not behave as expected.
This code DOES output the content of the second column in the file provided exactly as displayed in Excel:
private void button1_Click(object sender, EventArgs e)
{
string path = #"D:\Downloads\Windows\Prob.xls";
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = excel.Workbooks.Open(path);
Worksheet excelSheet = wb.ActiveSheet;
//Read a specific cell
for (int i = 1; i <= 150; i++ )
{
Range cell = excelSheet.Cells[i + 1, 2] as Range;
textBox1.AppendText(cell.Text + Environment.NewLine);
}
wb.Close();
}
If you don't see that, you should check that you are actually reading the correct file, and that you do not modify the file yourself from code before reading.
One can define ranges in C# Excel Interop like so:
var homeHomeOnTheRange = _xlSheet.Range[_xlSheet.Cells[3, 7], _xlSheet.Cells[42, 11]];
This range will encompass the subset of cells on the page from row 3 down to row 42, and across from columns 7 (or "G") through 11 (or "K").
What, though, if I want a "jagged" range - is it possible to concatenate an array of ranges into one range?
You can use this format sheet.Range("A1:A2,B2:B3,C3:C4").
Pay attention that sheet should be of type dynamic so don't use Worksheet class for this purpose.
Example
Set the specified range background color to red:
var range = sheet.Range("A1:A2,B2:B3,C3:C4");
range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
I am attempting to do this:
IWorksheet worksheet = Factory.GetWorkbook().Worksheets[0];
IRange range = worksheet.Cells["A1"];
range.CopyFromDataTable(dataTable, SetDataFlags.None);
worksheet.Cells.Columns.AutoFit();
return worksheet;
This works great normally, however I've run into an issue. I have one column that has a really long number, possibly with zeroes in the front and I need it to be entered and displayed as text. If I do a lookup of that particular cell like:
var cell = range["U34"].Value;
The data has already been turned into scientific notation so no amount of formatting afterwards fixes it. I tried SetDataFlags.AllText and that works great, except it breaks the rest of the worksheet because all of the numbers are stored as text, which is unacceptable.
I'm at a loss of how to fix this.
Solution:
Since I'm just looking to change one column, if it's present and a lot of the columns are dynamic I went with the "preformatting" route. Find the column index from the datatable:
int ColumnIndex = -1;
for (int x = 0; x < dataTable.Columns.Count; x++)
{
if (dataTable.Columns[x].ColumnName.Equals("Whatever"))
{
ColumnIndex = x;
}
}
worksheet.Cells[0, ColumnIndex, 0, ColumnIndex].EntireColumn.NumberFormat = "#";
Then perform the CopyFromDataTable, with Flags set to None and everything is perfect!
The IRange.CopyFromDataTable(...) method can be passed in a SetDataFlags.InsertCells enum option, which allows you to pre-format your destination range so that the inserted DataTable data picks up the formatting you specify. This formatting includes a cell's IRange.NumberFormat, which can be set to "#" and specifies that input to that cell should be treated as Text.
So, if you know what columns will have these unusually-large numbers that trigger scientific notation, another option would be to pre-format your worksheet's destination range with IRange.NumberFormat = "#" and will preserve your values for these columns as-is.
Please see the documentation for the IRange.CopyFromDataTable(...) method, as it provides important information on what range needs this "pre-formatting." Also, assuming you've installed SpreadsheetGear on your machine, check out the Reporting > DataSet to Workbook example in the SpreadsheetGear Explorer Solutions for C#/VB (found in the "SpreadsheetGear" folder under the Start Menu) for a live demo of this SetDataFlags.InsertCells option.
I'm exporting a table from SQLite to Excel (2010) in C#. It works fine. I'm using the Excel.Range.set_Value() method.
How can I format an Excel.Range like Excel's format (like a table) would?
To expand upon my comment and add to D Stanley.
Range range = ws.get_Range("A1:D5");
wrksheet.ListObjects.AddEx(XlListObjectSourceType.xlSrcRange, range, missing, Microsoft.Office.Interop.Excel.XlYesNoGuess.xlNo, missing).Name = "MyTableStyle";
wrksheet.ListObjects.get_Item("MyTableStyle").TableStyle = "TableStyleMedium1";
This example selects a rectangular range of every cell in the active sheet. Also, it uses indexed parameters of Range to get the range points. Furthermore, AddEx() (and most methods in Interop.Excel) uses default parameters so you don't have to use System.Reflection.Missing.
// define points for selecting a range
// point 1 is the top, leftmost cell
Excel.Range oRng1 = oSheet.Range["A1"];
// point two is the bottom, rightmost cell
Excel.Range oRng2 = oSheet.Range["A1"].End[Excel.XlDirection.xlToRight]
.End[Excel.XlDirection.xlDown];
// define the actual range we want to select
oRng = oSheet.Range[oRng1, oRng2];
oRng.Select(); // and select it
// add the range to a formatted table
oRng.Worksheet.ListObjects.AddEx(
SourceType: Excel.XlListObjectSourceType.xlSrcRange,
Source: oRng,
XlListObjectHasHeaders: Excel.XlYesNoGuess.xlYes);
Here's the VBA that does it:
ActiveSheet.ListObjects.Add xlSrcRange, Range("$J$10:$N$12"), , xlYes
Shouldn't be too hard to translate into an automation call. You can read the documentation as well.