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

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

Related

Getting Excel sheet cell value next to a merged column

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.

Setting colors of an entire Excel range to array of colors

I have an Excel.Range with 10 cells and I want to change its color by using a 0 based 2d array with 10 elements of Excel.XlRgbColor.
object[,] colorsTest = new object[10, 1]
{
{ Excel.XlRgbColor.rgbAliceBlue, Excel.XlRgbColor.rgbAliceBlue},
{ Excel.XlRgbColor.rgbAliceBlue, Excel.XlRgbColor.rgbAliceBlue}...
(8 more)
};
Excel.Range range = activeSheet.Range[activeSheet.Cells[1, 1], activeSheet.Cells[10, 1]];
But now when I try to set
range.Interior.Color = colorsTest;
I get a Type mistmatch error for some reason. When I try to set the range's value with the array, everything works like intended - I get the indexes of my colors pasted into the cells as values, like expected.
Any idea what could be causing this? I think the type is what it should be.
I have about 5000 rows this has to apply to so looping through them separately results in tragic performance.
Interesting idea but unfortunately it's not possible to set different color for each cell in a range they way you wanted to do that. I'm actually surprised you didn't get an error.
I also agree with "tragic performance" I guess we're talking about minutes here.
I see just a few options here
Copy format - if the colors are stable and you will always apply the same or just a few variation of them. I'd prepare the colored cells somewhere, like in range AA1:AA10 and when you want to apply the colors I'd just copy & paste format to your range
OpenXML - I believe it would be a way faster than accessing cells one by one but I assume you will code it for at least one day. I don't have too much experience with OpenXML SDK but from what I have done I can tell you it's pretty verbose

C# Interop Excel format like Excel's format as table

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.

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