Hey all, I'm experiencing transposed behavior when working the Excel.Worksheet.Cells array.
My first cell needs to be at [row = 10, column = 3]
My second cell needs to be at [row = 11, column = 17]
Then using these two cells as boundaries, I create a range and merge it.
As you can tell from the values noted above, the range should be mostly horizontal.
So, to help me out, I created a simple helper function that merges cells:
public static void MergeRange(Excel.Worksheet worksheet, int startRowIdx,
int startColIdx, int endRowIdx, int endColIdx, bool across = false)
{ //Get range boundaries.
Excel.Range cells = worksheet.Cells;
//commented out, resolved code is directly below - N. Miller, 9-24-2013
//Excel.Range topleftCell = cells[startColIdx][startRowIdx];
//Excel.Range bottomrightCell = cells[endColIdx][endRowIdx];
Excel.Range topleftCell = cells[startRowIdx, startColIdx];
Excel.Range bottomrightCell = cells[endRowIdx, endColIdx];
//Merge range.
Debug.WriteLine(String.Format("({0}, {1}) to ({2}, {3}).", startRowIdx, startColIdx, endRowIdx, endColIdx));
Excel.Range range = worksheet.get_Range(topleftCell, bottomrightCell);
Excel.Interior interior = range.Interior;
interior.ColorIndex = XLColor.PERIWINKLE; //JUST HIGHLIGHTS RANGE FOR NOW.
//range.Merge(across);
//Cleanup
Marshal.ReleaseComObject(interior);
Marshal.ReleaseComObject(range);
Marshal.ReleaseComObject(bottomrightCell);
Marshal.ReleaseComObject(topleftCell);
Marshal.ReleaseComObject(cells);
}
In the code above, I select the cells I want by swapping the columns and rows. This, contrary to what I expected, results in the desired behavior, but it contradicts the MSDN documentation:
MSDN Worksheet.Cells
In the MSDN documentation they give an example where the row is listed first, then the column follows after.
So my question is this... which is correct?
Should the row be first, or should the column be first?
When I modify my code to be consisten with the MSDN documentation, the highlighted cells are transposed.
If I understand you correctly then when you use say cells[1][2] then it means A2 In such a case, Column comes first and then the row. If you write it as cells[1,2] then you will get B2. In this case, Row comes first and then the Column.
It's the same in Excel-VBA. ?Cells(1)(2).address in immediate window gives $A$2 and ?Cells(1,2).address gives $B$1 If you feel that I have not understood your query then please re-phrase it for me...
Here is the complete code to test it.
NOTE: Tested using VS 2010 Ultimate + Excel 2010
using System;
using System.Windows.Forms;
using System.IO;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
Namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Excel.Application xlexcel;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
object misValue = Missing.Value;
xlexcel = new Excel.Application();
xlexcel.Visible = true;
//~~> Open a File (Change filename as applicable)
xlWorkBook = xlexcel.Workbooks.Open("C:\\SomeFile.xlsx",
0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows,
"\t", false, false, 0, true, 1, 0);
//~~> Set Sheet 1 as the sheet you want to work with
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
Excel.Range cells = xlWorkSheet.Cells;
Excel.Range topleftCell = cells[1][2];
MessageBox.Show(topleftCell.Address);
topleftCell = cells[1,2];
MessageBox.Show(topleftCell.Address);
//~~> Once done close and quit Excel
xlWorkBook.Close(false, misValue, misValue);
xlexcel.Quit();
//~~> CleanUp
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlexcel);
}
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Unable to release the Object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
}
}
ScreenShot
Related
I’m working on a project and I’ve ran into an exception that I don’t know how to fix. I’ve searched everywhere and can’t find a solution that helps.
I’m trying to cut a range on a spreadsheet that has found a specific value in a cell in Column A and paste the entire row of that specific value into a new spreadsheet starting from A2 and until the value is no longer found in the original spreadsheet.
My code currently pastes one row in the new spreadsheet then gives me this exception “The information cannot be pasted because the Cut area and the paste area are not the same size and shape.” The exception is happening at this point in the code;
Excel.Range from = currentFind.EntireRow;
Excel.Range to = oSheet.get_Range("A2:A2500");
I think I need to use the active cell and active sheet properties.
Please help me!
public void btnLoad_Click(object sender, EventArgs e)
{
Excel.Application oXL;
Excel._Workbook oWB;
Excel._Worksheet oSheet;
if (dmCheck.IsChecked == true && fldCheck.IsChecked == true)
{
oXL = new Excel.Application();
oXL.Visible = true;
oWB = (Excel._Workbook)(oXL.Workbooks.Add());
oSheet = (Excel._Worksheet)oWB.ActiveSheet;
string dXLPath = #"N:/DAILY2.xlsx";
Excel.Workbook dWB = oXL.Workbooks.Open(dXLPath);
Excel.Worksheet dSheet = dWB.Worksheets.get_Item(1);
Excel.Range dRng = dSheet.get_Range("B1");
dRng.EntireColumn.Hidden = true;
Excel.Range currentFind = null;
Excel.Range firstFind = null;
Excel.Range taskHazpoi = dSheet.get_Range("A2", "A2500");
currentFind = taskHazpoi.Find("HAZPOI", Type.Missing, Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, false, Type.Missing, Type.Missing);
while(currentFind != null)
{
if (firstFind == null)
{
firstFind = currentFind;
}
else if (currentFind.get_Address(Excel.XlReferenceStyle.xlA1) == firstFind.get_Address(Excel.XlReferenceStyle.xlA1))
{
break;
}
Excel.Range from = currentFind.EntireRow;
Excel.Range to = oSheet.get_Range("A2:A2500");
from.Cut(to);
currentFind = taskHazpoi.FindNext(currentFind);
}
else if (dmCheck.IsChecked == false && fldCheck.IsChecked == false)
{
MessageBox.Show("Please check the DM and Flood box", "Report Loader");
}
I recommend you use Epplus instead of the interop Excel (i used it).
Advantages:
No requires office package installed in system.
No instance of Excel in memory.
Methods more clear.
Example of use:
http://zeeshanumardotnet.blogspot.com.es/2011/06/creating-reports-in-excel-2007-using.html?m=1
You found it in Nuget.
Regards,
You try to copy an entire row into the cell area A2:A2500, that's what triggers the exception.
For the 'to' range, take oSheet.get_Range("A2").EntireRow or oSheet.get_Range("A:A").
I basically need my code to read one by one all cells in an excel file and then upload then to a database.
I've read on several answers to this question I should use the UsedRange but everytime I do I get an error saying there is no definition for UsedRange.
I added a reference to the excel interop but no dice.
Any advice would be appreciated.
And I know the code looks terrible now but I just wanted to test if I could read data from an excel file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;
namespace ConsoleApplication28
{
class Program
{
static void Main()
{
Excel.Application excelApp = new Excel.Application();
excelApp.Visible = true;
string workbookPath = "C:/Users/Sidney/Desktop/CrystalViewer-11.xls";
Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(workbookPath,
0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "",
true, false, 0, true, false, false);
Excel.Sheets excelSheets = excelWorkbook.Worksheets;
string currentSheet = "Sheet1";
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(currentSheet);
Excel.Range range;
range = excelSheets.UsedRange;
int rows_count = range.Rows.Count;
string output = null;
}
}
}
You're trying to access UsedRange on the wrong Object. UsedRange is a property of Worksheet, so your code should be:
range = excelWorksheet.UsedRange;
Excel.Range xlRange = excelWorkbook.ActiveSheet.UsedRange;
I am writing a C# program which copies a range of cells from a worksheet of one workbook to a worksheet of an other workbook. But the problem I am facing is I am only able to copy and paste the whole worksheet of first workbook. I want to know how to select only a specific range(from row 5 [column 1 to column 10] to row 100 [column 1 to column 10]) and paste it in second workbook worksheet starting from row 2 column 8.
Also i want to know how a fill a column say from C1 to C100 with some value in a direct way instead of using the loop like below
for(i=1;i<2;i++)
{
for(j=1;j<101;i++)
{
worksheet.cells[i,j]="Fixed";
}
}
Here is the code that i have written so far
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Excel.Application srcxlApp;
Excel.Workbook srcworkBook;
Excel.Worksheet srcworkSheet;
Excel.Range srcrange;
Excel.Application destxlApp;
Excel.Workbook destworkBook;
Excel.Worksheet destworkSheet;
Excel.Range destrange;
string srcPath;
string destPath;
//Opening of first worksheet and copying
srcPath="C:\\Documents and Settings\\HARRY\\Desktop\\incident.csv";
srcxlApp = new Excel.Application();
srcworkBook = srcxlApp.Workbooks.Open(srcPath);
srcworkSheet = srcworkBook.Worksheets.get_Item(1);
srcrange = srcworkSheet.UsedRange;
srcrange.Copy(Type.Missing);
//opening of the second worksheet and pasting
destPath = "C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report.xls";
destxlApp = new Excel.Application();
destworkBook = destxlApp.Workbooks.Open(destPath,0,false);
destworkSheet = destworkBook.Worksheets.get_Item(1);
destrange = destworkSheet.Cells[1, 1];
destrange.Select();
destworkSheet.Paste(Type.Missing, Type.Missing);
destworkBook.SaveAs("C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report " + DateTime.Now.ToString("MM_dd_yyyy") + ".xls");
srcxlApp.Application.DisplayAlerts = false;
destxlApp.Application.DisplayAlerts = false;
destworkBook.Close(true, null, null);
destxlApp.Quit();
srcworkBook.Close(false, null, null);
srcxlApp.Quit();
}
}
}
You should be able to do this:
Excel.Range from = srcworkSheet.Range("C1:C100");
Excel.Range to = destworkSheet.Range("C1:C100");
from.Copy(to);
mrtig has a very elegant solution. But it won't work if you have the workbooks in separate instances of excel. So, the key is to open them in just one instance. I've modified your example to show using this approach:
public void CopyRanges()
{
// only one instance of excel
Excel.Application excelApplication = new Excel.Application();
srcPath="C:\\Documents and Settings\\HARRY\\Desktop\\incident.csv";
Excel.Workbook srcworkBook = excelApplication.Workbooks.Open(srcPath);
Excel.Worksheet srcworkSheet = srcworkBook.Worksheets.get_Item(1);
destPath = "C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report.xls";
Excel.Workbook destworkBook = excelApplication.Workbooks.Open(destPath,0,false);
Excel.Worksheet destworkSheet = destworkBook.Worksheets.get_Item(1);
Excel.Range from = srcworkSheet.Range("C1:C100");
Excel.Range to = destworkSheet.Range("C1:C100");
// if you use 2 instances of excel, this will not work
from.Copy(to);
destworkBook.SaveAs("C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report " + DateTime.Now.ToString("MM_dd_yyyy") + ".xls");
srcxlApp.Application.DisplayAlerts = false;
destxlApp.Application.DisplayAlerts = false;
destworkBook.Close(true, null, null);
srcworkBook.Close(false, null, null);
excelApplication.Quit();
}
For the First part of setting the same value for the entire range, instead of looping following will work out
range1 = workSheet.get_Range("A1:B100");
range1.Value = "Fixed";
And for copying you can try what #mrtig has suggested.
I am using Excel Interop to work with some excel sheets. In a worksheet, I need to iterate through the rows, and if the first cell in the row is empty then I need to delete the entire row iteself. I tried the following-
Excel.Range excelRange = sheet.UsedRange;
foreach (Excel.Range row in excelRange.Rows)
{
String a = ((Excel.Range)row.Cells[Type.Missing, 1]).Value2 as String;
if (a == null || a == "")
{
((Excel.Range)row).Delete(Type.Missing);
}
}
This was not working at all. Is there any different way to do this?
And, is there any quick way to find and remove all formula in a Worksheet?
Let's say your Excel file looks like this.
The best way to delete rows would be to use Excel's inbuilt feature called Autofilter which will filter Col A for blank values and then deleting the entire rows in one go.
TRIED AND TESTED (In VS 2010 Ultimate)
Note: I have changed few lines which I feel could error out in VS 2008. Since I don't have VS 2008, I couldn't test it there. If you get any errors do let me know and I will rectify it.
//~~> Open File
private void button1_Click(object sender, EventArgs e)
{
Microsoft.Office.Interop.Excel.Application xlexcel;
Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
Microsoft.Office.Interop.Excel.Range xlRange;
Microsoft.Office.Interop.Excel.Range xlFilteredRange;
xlexcel = new Excel.Application();
xlexcel.Visible = true;
//~~> Open a File
xlWorkBook = xlexcel.Workbooks.Open("C:\\MyFile.xlsx", 0, false, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
//~~> Work with Sheet1
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
//~~> Get last row in Col A
int _lastRow = xlWorkSheet.Cells.Find(
"*",
xlWorkSheet.Cells[1,1],
Excel.XlFindLookIn.xlFormulas,
Excel.XlLookAt.xlPart,
Excel.XlSearchOrder.xlByRows,
Excel.XlSearchDirection.xlPrevious,
misValue,
misValue,
misValue
).Row ;
//~~> Set your range
xlRange = xlWorkSheet.Range["A1:A" + _lastRow];
//~~> Remove any filters if there are
xlWorkSheet.AutoFilterMode=false;
//~~> Filter Col A for blank values
xlRange.AutoFilter(1, "=", Excel.XlAutoFilterOperator.xlAnd, misValue, true);
//~~> Identigy the range
xlFilteredRange = xlRange.Offset[1,0].SpecialCells(Excel.XlCellType.xlCellTypeVisible,misValue);
//~~> Delete the range in one go
xlFilteredRange.EntireRow.Delete(Excel.XlDirection.xlUp);
//~~> Remove filters
xlWorkSheet.AutoFilterMode = false;
//~~> Close and cleanup
xlWorkBook.Close(true, misValue, misValue);
xlexcel.Quit();
releaseObject(xlRange);
releaseObject(xlFilteredRange);
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlexcel);
}
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Unable to release the Object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
Output
Imagine, that u have the Column A.
There are 100 Rows and in the Cells are Numbers, like 1, 2, 3 until 100..
How can I programmaticaly (C#) Delete a specific Row, by Example: Deleting the Row which
Value in Column A is 5..
I'm working with the Microsoft.Office.Interop.Excel and thats the related code:
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(#"C:\Users\fre\Desktop\TestDatei.xls");
Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
xlWorksheet.Select(Type.Missing);
Microsoft.Office.Interop.Excel.Range range = xlWorksheet.get_Range("B1:B5", Type.Missing);
range.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftUp);
because i was interested, too, i did some investigation on the web and created some sample code. maybee this can help you:
private void DeleteCells(object sender, EventArgs e)
{
// create excel-instance:
Excel.Application excel = new Excel.Application();
// open the concrete file:
Excel.Workbook excelWorkbook = excel.Workbooks.Open(#"D:\test.xls");
// select worksheet. NOT zero-based!!:
Excel._Worksheet excelWorkbookWorksheet = excelWorkbook.Sheets[1];
// create a range:
Excel.Range usedRange = excelWorkbookWorksheet.UsedRange;
// iterate range
foreach (Excel.Range r in usedRange)
{
// check condition:
if (r.Value2 == 5.0F)
// if match, delete and shift remaining cells up:
r.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
}
// save changes (!!):
excelWorkbook.Save();
// cleanup:
if (excel != null)
{
Process[] pProcess;
pProcess = System.Diagnostics.Process.GetProcessesByName("Excel");
pProcess[0].Kill();
}
}
greetings!
jens
If you want to delete the entire row, try this it works
Range usedRanage=sheet.UsedRange;
foreach (Range r in usedRanage)
{
if (Convert.ToString(r.Value2)=="RETRIEVE")
{
r.EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);
}
}