I'm making an extension method to simplify copying range data from on worksheet to another.
I want to specify a rectangular range and the top left cell of the target range. Keeping formating is important for Dates and Currencies so I want to Use Value over Value2
//Extension Helper method
public static void CopyRangeTo(this Excel.Range from, Excel.Range to){
//resize to to match the rows and columns of from
to = to.Resize[from.Rows.Count, from.Columns.Count];
//copy the contents of the range from to range to
to.Value = from.Value;
}
//how its intended to be called
ws.Range["A1:C5"].CopyRangeTo(ws2.Range["B3"]);
//note this call doesn't work because Cells cannot be used statically
ws.Range[ws.Cells[1, 1], ws.Cells[5, 3]].CopyRangeTo(ws2.Cells[2,3]);
Currently The program works well the inital time a call the method. However if I change the worksheet at all the method seems to fail (Even if I didn't affect the ranges being used). However I have noticed that if I use Value2 instead then it is much more robust, however I lose formatting.
I may try to iterate through the cells and see if that helps, if any one has other ideas or insight between moving cells around I'd appreciated. I'd like to stay away from paste special.
Related
I have a column and i'm looking to cut rows 3-37 and move it to the column directly to the right of it.
I tried the following and it moves the values correctly to the right but the cell styles don't copy over.
Range copyRange = ws1.Range["L5:L37"];
Range insertRange = ws1.Range["M5:M37"];
insertRange = copyRange;
I've also tried solution from here Cut and Paste Columns in Excel Range with c# but nothing seem to copy
Range copyRange = ws1.Range["L5:L37"];
Range insertRange = ws1.Range["M5:M37"];
//insertRange.Insert(copyRange.Cut());
insertRange.Insert(XlInsertShiftDirection.xlShiftToRight, copyRange.Cut());
If you want to just replace the contents of the destination cells with the contents of the source cells, and delete the contents of the source cells, then the Range.Cut method ought to do the trick if you call it with the destination range as an argument:
Range sourceRange = ws1.Range["L5:L37"];
Range destinationRange = ws1.Range["M5:M37"];
sourceRange.Cut(destinationRange);
This will not copy sourceRange to the clipboard, but it will clear the clipboard if you already have a range on the clipboard.
You can also specify only the upper left cell of the destination range, and it will do the same thing:
Range sourceRange = ws1.Range["L5:L37"];
Range destinationRange = ws1.Range["M5"];
sourceRange.Cut(destinationRange);
If you want to keep the contents of the source cells, use Copy instead of Cut.
(Note that the statement insertRange = copyRange; definitely should never copy any cells anywhere if insertRange is a local variable. That could only happen if C# supported overloaded assignment (which, to my knowledge, it does not) or if some other code somehow "sensed" the assignment.)
I'm currently evaluating VSTO to replace the VBA we use with Excel 2010, so I'm absolutely new to this Excel interop thing.
While testing, I sometimes encountered issues while using ranges and cells, sometimes the arrays seem to be 1-based and sometimes they seem to be 0-based, and I didn't find any information on that.
Here is a small example :
var ws = ((Excel.Worksheet)Globals.ThisWorkbook.Application.ActiveWorkbook.Sheets[1]);
var range = ws.Range["B2:D6"];
AddName("testname", range);
range.BorderAround2(Excel.XlLineStyle.xlContinuous, Excel.XlBorderWeight.xlThick);
//Code 1
((Excel.Range)range.Cells[2, 1]).BorderAround2(Excel.XlLineStyle.xlDouble, Excel.XlBorderWeight.xlThick);
((Excel.Range)range.Cells[4, 1]).BorderAround2(Excel.XlLineStyle.xlDouble, Excel.XlBorderWeight.xlThick);
//Code 2
range.Range[range.Cells[2, 1], range.Cells[4, 1]].Interior.Color = ColorTranslator.ToOle(Color.Green);
//Code 3
range.Range[range.Cells[1, 0], range.Cells[3, 0]].Interior.Color = ColorTranslator.ToOle(Color.Red);
The code creates a named range and adds a thick border around, then adds a double border to the cells that represent the bounds of the range I want.
It sets the background to green for the range that uses the same cells, and in red for the range I want.
Here is the result :
What I wanted to do is to select the cells of the first column in the range except the first and last (so red is what I want).
But it seems that if I select the cells individually (code 1), it doesn't behave the same as if I create a range from the same cells (code 2).
To get the range I want, I have to shift all indexes by one (0-based) (code 3).
Is there a reason for that, did I miss something ? I don't see anything about that in the MSDN.
Check out the Remarks here : https://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.range.range.aspx. With the Range property the cells are relative to the defined range, hence the shift by 1.
I need to parse an Excel file. First I wrote an extension in Visual Basic inside the Excel file, all worked good. Now I need to port it to C# so it can be a separate application. While the functions I use are the same, the result is not the same...
When I choose from the GUI which Worksheet to parse, I do something like:
range = (workbook.Worksheets.get_Item(itemIndex) as Excel.Worksheet).UsedRange;
Then, for the first row I need to parse I do something like:
range.get_Range(range.Cells.get_Item(6, 2),
range.Cells.get_Offset(6,2).get_End(Excel.XlDirection.xlToRight)))
And I get the right result with all the fields I need.
The second time when I need to get another row, I do:
range.get_Range(range.Cells.get_Item(13, 3),
range.Cells.get_Offset(13, 3).get_End(Excel.XlDirection.xlToRight)))
This time it gives me all the elements except the last one. And I have more functions like this, some with XlDirection.xlDown and all of them return me the range without the last element.
I tried to swap the functions, thinking may be I need to release range and then acquire it again or something(wanted to check if it's always working only for the first function being executed) but it is always working only for the first example, whenever the function is being executed...
This is even stranger because it worked in VBA Excel.
I also tired with Excel.Application get_Range and Excel.Worksheet get_Range...
Anyone knows why this happens?
I managed to solve this strange behavior. It's not the correct way of getting out the data.
The correct way would be: range.get_Range(range.Cells[6, 2], (range.Cells[6, 2] as Excel.Range).get_End(Excel.XlDirection.xlToRight)) - for the first example.
Hope it helps somebody...
My C# code manipulates Excel Ranges using Microsoft.Office.Interop.Excel library. I need to assign a Formula Array to a selected Range. I've tried a variety of methods recommended online, including Microsoft recommendations, but so far was unable to make it work properly.
I observe 2 issues:
Issue 1.
Assignment looks fine on surface: it does not fail, cell objects in the range show .ArrayFormula property assigned, on the spreadsheet formula in every cell appears in curly brackets. However, the Formula Array is actually disjointed: each cell in the range can be changed separately, which normal Formula Array would not permit. It behaves as if every cell had its own, single-cell Formula Array, independent from others. Regardless of my best efforts, this is ALWAYS the case.
Is there actually a properly working solution for this issue?
Issue 2.
My Array Formula contains a reference to another Range (Range A), which I need to refer to in R1C1 style. I need Array Formula in every cell in the target Range point to the same Range A. Somehow I always end up with every cell in target Range having its own version of the formula, referring to shifted "Range A" area. How do I make the reference stay in place, regardless of a cell?
N.B. You may assume that Issue 2 is causing Issue 1, but this is not the case: for example, when array formula is simple, like "=SIN(1)", the Issue 1 still occurs.
I would really appreciate any WORKING suggestions. Thanks a lot in advance.
No one seemed interested, however I found a solution and will answer to my own question.
Apparently, assignment of an Excel Array Formula within C# code works only if the formula is in A1 style, not in R1C1 style. In my case, I was starting with a R1C1-style formula, so it required conversion to A1 style. This is achieved by assigning the original R1C1-style formula to the top left cell of the target range:
topLeftCell.Formula = myR1C1Formula;
// topLeftCell.FormulaR1C1 = myR1C1Formula also works
Assignment to that particular cell will ensure that A1-style formula contains correct references. Get back the converted formula as a string:
string formulaA1 = topLeftCell.Formula;
Get reference to the whole target range by rezising the top left cell:
Excel.Range newArrayRange = topLeftCell.Resize[height, width];
Resize operation must precede the following assignment. Finally, assign the A1-style formula to the FormulaArray property of the whole target range:
newArrayRange.FormulaArray = formulaA1;
This works perfectly without issues or side-effects.
Which would be potentially a best way to enumerate or iterate or simply look for empty cells or cells with specific data structure in Excel, and later once you find it do some processing on it.
I tired Range, Value, Value2, etc but it takes fairly long time when Excel Sheet is considerably larger. I believe there must be some other efficient way.
It would be nice, if you can show some example snippet.
The answer is relativley simple: get the array in one batch from excel (search SO for a how to) - test the values of the erray for empty cells and then acess only the empty cells in excel.
It is somewhat cumbersome, but the fastes way because iterating each cell is vastly slower than simply getting all data in a batch.
To find blank cells, use the .SpecialCells method of a range object.
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.range.specialcells(v=office.11).aspx
The .specialCells method returns a range object of the matching criteria (i.e., xlCellTypeVisible, xlCellTypeBlanks, etc.). You can then iterate of this range to perform your formatting, etc.
Update I'm not a C# programmer, but I can show you how I would do this in VBA. Assuming interop exposes most/all of the same methods and functionality, you should hopefully be able to translate this for your purposes.
Sub ColorVisibles()
Dim rng As Range
Dim rngBlanks As Range
Dim blanksExist As Boolean
'define your range
Set rng = Range("A1:AA300")
'check to make sure there are blank cells in the range:
blanksExist = Application.WorksheetFunction.CountBlank(rng) > 0
If blanksExist Then
Set rngBlanks = rng.SpecialCells(xlCellTypeBlanks)
rngBlanks.Interior.Color = vbYellow
Else:
MsgBox "No blank cells exist in the specified range.", vbInformation
End If
End Sub