Getting Excel sheet cell value next to a merged column - c#

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.

Related

Applying formatting to a big range

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.

Read Excel range as array of text(string representing what is displayed on the screen for the cell)

I am trying to read the entire excel data as array of text(The exact same text as displayed in the cell).
I am using this code to convert the excel content to array
Microsoft.Office.Interop.Excel.Range range = excelSheet.get_Range("A1", "P" + excelRowCount);
object[,] values = (object[,])(range.Text);
but I am getting error like cannot convert DBNull to object[,]
The code will work if I replace range.Text with range.Value or range.Value2
But that won't give the correct data, for example if the data in the cell is 11:00:00 PM the range.Value gives 0.95833333333333337
here I need to use DateTime.FromOADate() method to convert value 0.95833333333333337 to 11:00:00 PM
But if I go with excelSheet.Cells[rowNo, columnNo].Text I will get the correct text, ie:11:00:00 PM, but that will slowdown the process because there are 40K records in the excel sheet.
Is there any way to get the exact text in to array like object[,] values = (object[,])(range.Value);, why range.Text is not working?
From https://fastexcel.wordpress.com/2011/11/30/text-vs-value-vs-value2-slow-text-and-how-to-avoid-it/
If you try getting .Text from multiple cells into a variant (varr = Range(“A1:A10”).Text) you do NOT get an array of results. Instead if all the cells in the range contain the same value formatted in the same way you get a single formatted value, but if ANY of the cells has different values or formats you get Null ( this could be a useful trick).
That article also concurs with your speed issues with .Text each cell.
But .Text tries to read a lot of the cell properties(including date formatting which you want) - that's why its slow.
Also be aware that .Text can return ###### if the cell contents don't fit, so you would have to autofit all columns/rows...

Can Excel Ranges be jagged (rather than always contiguous)?

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

How to pass non-contiguous cells to Excel UDF

in myUDF, I can reference a range of cells like "A1:A12", but how can I reference non-contiguous cells like "A1,B4,H3" etc.
I use ExcelDNA, the parameter type is object
but it seems it will take string, and a range of cells, not non-contiguous cells
[ExcelArgument(AllowReference = true, Name = "Relations", Description = "a set of relations")]object rels
It sounds like you're entering the formula onto the worksheet, along with its parameters, from your code, and you want users to then be able to edit the formula normally in Excel's formula bar. Is that correct?
If so, enclose the parameter in parens. For example, for a UDF like this...
Public Function MyUDF(my_param As Range)
'concatenate all cell values in a non-contiguous range:
Dim rgCell As Range, rgArea As Range
For Each rgArea In my_param.Areas
For Each rgCell In rgArea
MyUDF = MyUDF & CStr(rgCell.Value)
Next rgCell
Next rgArea
End Function
...enter it in the worksheet cell like this:
=MyUDF((A1,A3,A7:A11,C8:E10))
Note the extra set of parens compared to using a built-in function like SUM.
BTW as you may already know, when looping through a non-contiguous range you have to loop through the areas of the range, then loop through the cells in each area; looping through the cells in the range only gives you the cells in the first area.

In C#, how do I use the Excel Interop to speed up writing several cell values

I have a piece of hardware for which I am getting 30 data points. Each of these points is recorded in a spreadsheet at several different places before the sheet is made visible, then another program takes over the excel spreadsheet. It is required all these values are written to the spreadsheet before the other program takes over. If I write each cell individually, the writes are taking approximately 50ms, which takes about 1.25 seconds to complete the data acquisition.
If I could write all the values to the spreadsheet at one time, I feel this will significantly speed up the writing of all these cells. The problem I see is that Ranges work very well for updating contiguous cells where as my data isn't contiguous. Essentially, this would be an example of what I want to write:
A1 = 1
B23 = a
F8 = 2012/12/25
D53 = 4.1235
B2 = 5
I have tried creating a range of "A1,B23,F8,D53,B2", then set the values using an array of values. I tried 3 different arrays: object[5], object[1,5], and object[5,1]. These all set the values of the specified cells in the range to the first index of the array I created in all cases.
Is there a way to update these 30 cells data without iterating through the cells one at a time?
Thanks,
Tom
If your architecture would permit, another idea is using a hidden sheet with a continuous rectangular range, set names to its parts and use these names on all other sheets.
I would define a rectangular range object that includes all the cells whose values you want to modify. Get a rectangular object[,] array from that range's value property. Write the new values to the array, and then set the range's value using the modified array.
You could write the values to contiguous cells that are somewhere out of the way, say in column X, and have formulae in the target cells that refer to these updated cells. So cell A1 would have the formula "=X1", cell B23 "=X2" and so on.

Categories