I have written C# application using Excel Interop 12.0 that worked fine on Excel 2007 and Excel 2010. But it is working randomly on Excel 2016. It gives Activate method of Range class failed error sometimes. When I rerun the program it is ok. Again I run it throws error. The program opens about 30 workbooks and populates the workbook from C#. I use Range.get_ReSize() and Range.Activate() in many places. Why is the behavior random? Any Help is appreciated
xlWb.Activate();
xlWsSummary.Activate();
Excel.Range ra = xlWsSummary.UsedRange;//append to the last cell in the used range ra.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, System.Type.Missing).Activate();
Excel.Range Range;
string cellToPaste = "A" + (xlApp.ActiveCell.Row + 1).ToString();//go to the next row
Range = xlWsSummary.get_Range(cellToPaste, System.Type.Missing);
Range = Range.get_Resize(1, index);
Range.Value2 = data;
Range.Font.Size = 8;//set the fontsize ```
I'm not sure if this is what you're looking for exactly, but here is some sample code that works through 2 Excel workbooks, each with a single worksheet containing 6 numbers:
A B C
----------
1 | 3 4 5
2 | 6 7 8
This data is copied from each workbook and appended to data in a summary workbook, creating:
A B C
----------
1 | 3 4 5
2 | 6 7 8
3 | 3 4 5
4 | 6 7 8
Finally, the next 'A' column cell after the data is selected before saving the summary workbook:
string file = #"C:\Users\me\Desktop\Summary.xlsx";
List<string> fileData = new List<string>{#"C:\Users\me\Desktop\Book1.xlsx",
#"C:\Users\me\Desktop\Book2.xlsx"};
Excel.Application xlApp = new Excel.Application();
// Open summary workbook and init data workbook and worksheet
Excel.Workbook xlWb = xlApp.Workbooks.Open(file);
Excel.Worksheet xlWsSummary = xlWb.Sheets[1];
Excel.Workbook xlWbData = null;
Excel.Worksheet xlWsData = null;
// Work through your data workbooks
for ( int i = 0; i < fileData.Count; i++ )
{
// Open data workbook
xlWbData = xlApp.Workbooks.Open(fileData[i]);
xlWsData = xlWbData.Sheets[1];
// Get specific range of data. I didn't use UsedRange in case only a subset of data is required
string dataStart = "A1";
string dataEnd = "C2";
Excel.Range rangeSource = xlWsData.get_Range(dataStart, dataEnd);
// Determine next available 'A' cell after used range in summary worksheet
Excel.Range ra = xlWsSummary.UsedRange;
Excel.Range rangeDest = ra.get_Range("A" + (ra.Rows.Count + 1));
// Copy data from data workbook to summary workbook
rangeSource.Copy(rangeDest);
// Select the range just copied to the summary workbook and format as required
Excel.Range rangeFormat = rangeDest.Resize[rangeSource.Rows.Count, rangeSource.Columns.Count];
rangeFormat.Font.Size = 8;//set the fontsize ```
}
// Release data workbook objects and close before doing anything else
Marshal.ReleaseComObject(xlWsData);
xlWbData.Close();
Marshal.ReleaseComObject(xlWbData);
// Select next available cell in A column
xlWsSummary.Activate();
Excel.Range used = xlWsSummary.UsedRange;
Excel.Range next = xlWsSummary.get_Range("A" + (used.Rows.Count + 1));
next.Select();
// Save the file
xlWb.SaveAs(file);
// And, release!
Marshal.ReleaseComObject(xlWsSummary);
xlWb.Close();
Marshal.ReleaseComObject(xlWb);
I may have not handled the Excel object releasing properly so you'd better look into that and the Activate() call gives an ambiguity warning but otherwise, hope this helps.
Related
This is a follow-up question to an old question:
How to get the range of occupied cells in excel sheet
Quoting from that question:
Excel.Range last = sheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
Excel.Range range = sheet.get_Range("A1", last);
"range" will now be the occupied cell range
share|improve this answer answered Aug 18 '09 at 9:03
Zurb
it returns always time blank cells, which i think question author wants to "not to get" that blank cells.– JavidanApr 23 at 7:30
See the Range.SpecialCells method. For example, to get cells with constant values or formulas use:
_xlWorksheet.UsedRange.SpecialCells(
Microsoft.Office.Interop.Excel.XlCellType.xlCellTypeConstants |
Microsoft.Office.Interop.Excel.XlCellType.xlCellTypeFormulas)
share|improve this answer answered Aug 16 '09 at 14:23
Joe Erickson
Back to my question...
I tried to use this method which generates this exception:
System.Runtime.InteropServices.COMException: 'SpecialCells method of Range class failed'
Here is my code:
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
Excel.Application xlapp = new Excel.Application();
Excel.Workbook wbook = xlapp.Workbooks.Open(tbxFilename.Text);
Excel.Worksheet wsheet = wbook.Sheets[1];
Excel.Range range = wsheet.UsedRange;
tbxOutput.AppendText($"UsedRange: Rows = {range.Rows.Count}, Cols `enter code here`= {range.Columns.Count}\r\n");
range = wsheet.UsedRange.SpecialCells(Excel.XlCellType.xlCellTypeConstants | // generates Exception
Excel.XlCellType.xlCellTypeFormulas, Missing.Value);
tbxOutput.AppendText($"Rows = {range.Rows.Count}, Cols = {range.Columns.Count}\r\n");
What is wrong with my code?
Thanks!
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
I am using Excel Interop to generate Excel Table into an xlsx file. I have a very simple table with 3 columns and no header, no totals but has 2 rows.
Now when I generate the Excel file, I use the following code
...
Range rng = worksheet.Range["$A$1:$C$2"];
ListObject lo = worksheet.ListObjects.Add(xlSrcRange, rng, Type.Missing, XlYesNoGuess.xlNo);
lo.ShowHeaders = false;
...
With the above code it generates the table with cell range A2:C3 instead of A1:C2. But any values I set in the A row are set but outside of the table.
If I set the ShowHeaders to true in the 3rd line of code above, then Excel is converting the top row as the header line and still moves the table cell range to A2:C3
Am I doing something wrong? I appreciate any and all help and guidance to fix this issue.
Thanks
Jaideep
Try this
//
//~~> Rest of your code
//
Range rng = worksheet.Range["$A$1:$C$2"];
ListObject lo = worksheet.ListObjects.Add(xlSrcRange, rng, Type.Missing, XlYesNoGuess.xlNo);
lo.ShowHeaders = false;
Excel.Range rngRowOne = worksheet.get_Range("A1", "A1");
rngRowOne.EntireRow.Delete(Excel.XlDirection.xlUp);
//
//~~> Rest of your code
//
The issue is When I set up the DataSet as shown below, the default reads only 255 characters from the spreadsheet cell and places them into the table.
DataSet exData = new DataSet();
string connectionString = String.Format(ConnectionString, path);
OleDbDataAdapter ex = new OleDbDataAdapter(ExcelQuery, connectionString);
ex.Fill(exData);
I'm using Extended Properties=Excel 8.0 and Microsoft.Jet.OLEDB.4.0, there is no problem connecting to the excel sheet.
From my reading it follows that it is due to the Jet.OLEDB provider, what should I be using?
And I may be unable to update to new provider of Jet, is there any workaround? Any workaround would would be restricted on not being able to directly modify the Excel document to contain two cells or more for data over 255 chars.
Thanks.
Use ExcelDataReader and add reference in your project. and use below code to read Excel more than 255 columns...
FileStream stream = File.Open(strFileName, FileMode.Open, FileAccess.Read);
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
DataSet result = excelReader.AsDataSet();
excelReader.Close();
return result.Tables[0];
I had the same problem here and I'd found out that, by default, only the first 8 rows are used to set the type of column. If you have a larger string on any other row bellow the 8th, it will be truncated.
You just need to Run Regedit and go to :
32-bits versions:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel
Or
64-bits versions:
HKEY_LOCAL_MACHINE\SOFTWARE\wow6432node\microsoft\jet\4.0\Engines\Excel
There, change the TypeGuessRows to 0, so the Jet engine will use all the rows to set the data type.
Source: Why SSIS always gets Excel data types wrong, and how to fix it!
The standard max length of a text column in jet is 255 characters. I haven't tried this, but if you create a schema.ini file and tell it that your column is a memo type, you might be able to put more data in.
The same issue is described here. Any specific reason why you are not considering using the Excel interop?
Form the following pictures you can find how to add Excel reference
library in your project.
Select Add Reference dialogue from Project menu of your Visual Studio
Select Microsoft Excel 15.0 Object Library of COM leftside menu and
click OK button
After import the reference library, we have to initialize the Excel
application Object.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Excel.Application xlApp ;
Excel.Workbook xlWorkBook ;
Excel.Worksheet xlWorkSheet ;
Excel.Range range ;
string str;
int rCnt ;
int cCnt ;
int rw = 0;
int cl = 0;
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(#"d:\csharp-Excel.xls", 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
range = xlWorkSheet.UsedRange;
rw = range.Rows.Count;
cl = range.Columns.Count;
for (rCnt = 1; rCnt < = rw; rCnt++)
{
for (cCnt = 1; cCnt < = cl; cCnt++)
{
str = (string)(range.Cells[rCnt, cCnt] as Excel.Range).Value2;
MessageBox.Show(str);
}
}
xlWorkBook.Close(true, null, null);
xlApp.Quit();
}
}
}
Data type lenght will be limited to 255 characters as Interop uses the first eight column to set the limit.
A shortcut approach to tackle this is ,just make the longest column in as
the first column or any of hte first eight columns in excel. So that the limit will be set to that of the longest column
If you have schema.ini to work with, use Memo datatype instead of Text for the column that stores strings larger than 255.
In Excel VBA (or if you could in C#, I'm using the Excels Object Library from .NET), how to copy a worksheet from one workbook to another sheet in another workbook. Basically, what I'm doing is copying every of my sheet into a central worksheet in another workbook and then will do all the stuff I need to do there. I tried using Range.Copy method, I gave the Destination parameter as the range of the other workbook. It worked perfectly, but there is one problem, that is every time I copy it replaces the older data in that worksheet. How do I do something like so that when I paste it pastes in the end of the sheet.
EDIT: I searched and found a way, but now when I copy the cells I get a COM exception with the message "To paste all cells from an Excel worksheet into the current worksheet, you must paste into the first cell (A1 or R1C1)."
Following is the code, it is in C#
logWorksheet = logWorkbook.ActiveSheet as Excel.Worksheet;
Excel.Range tempRange = logWorksheet.Cells[logWorksheet.Rows.Count, "A"] as Excel.Range;
tempRange = tempRange.get_End(Excel.XlDirection.xlUp);
int emptyRow;
if (tempRange.Row > 1)
emptyRow = tempRange.Row + 1;
else
emptyRow = tempRange.Row;
string copyLocationAddress = Convert.ToString(emptyRow);
Excel.Range copyLocation = logWorksheet.get_Range(
"A" + copyLocationAddress, Type.Missing) as Excel.Range;
// copy whole workbook to the central workbook
tempLogSheet.Cells.Copy(copyLocation);
-- UPDATE --
This snippet copies the cells A1:A3 of Book1 to Book2. It will find the last used cell in Book2 and will append the data underneath it.
Sub CopyRange()
Dim source As Worksheet
Dim destination As Worksheet
Dim emptyRow As Long
Set source = Workbooks("Book1.xlsx").Sheets("Sheet1")
Set destination = Workbooks("Book2.xlsx").Sheets("Sheet1")
'find empty row (actually cell in Column A)'
emptyRow = destination.Cells(destination.Rows.Count, 1).End(xlUp).Row
If emptyRow > 1 Then
emptyRow = emptyRow + 1
End If
source.Range("A1:A3").Copy destination.Cells(emptyRow, 1)
End Sub
-- OLD --
This sample copies all the sheets of Book1.xlsx to Book2.xlsx:
Workbooks("Book1.xlsx").Worksheets.Copy Before:=Workbooks("Book2.xlsx").Sheets(1)