I know that this problem may be addressed in another asked question, is not cause I lost 2 days trying them all.
Lets start the topic:
-I have an desktop app made in Visual Studio Express 2017, C# code used.
-I take 3 variables that I want to store in an excel document that has multiple sheets: Overtime nr. of hours, Day, comment.
Every sheet belongs to an individual that is based on pc username.
string datforOV = monthCalendar1.SelectionRange.Start.ToShortDateString(); //data string
double hours = decimal.ToDouble(numericUpDown1.Value) + decimal.ToDouble(numericUpDown2.Value) * 0.1; //number of hours
string box = richTextBox1.Text; //content of text box
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Worksheet xlsht = new Microsoft.Office.Interop.Excel.Worksheet();
string path = #"Z\OLP.xlsx";
xlsht = xlApp.Application.Workbooks.Open(path).Worksheets[Environment.UserName];
I need to add a new row, in the sheet named already after the Username, and fill the first column with nr of hours, second column with the day, and the next one with the comment.
Please help me cause I started losing my minds. Thank in advance.
You can try the following code to add a new row to your named sheet excel.
using Excel = Microsoft.Office.Interop.Excel;
private void button1_Click(object sender, EventArgs e)
{
string datforOV = monthCalendar1.SelectionRange.Start.ToShortDateString(); //data string
double hours = decimal.ToDouble(numericUpDown1.Value) + decimal.ToDouble(numericUpDown2.Value) * 0.1; //number of hours
string box = richTextBox1.Text; //content of text box
Excel.Application xlApp = new Excel.Application();
string path = #"E:\1.xlsx";
Excel.Workbook workbook = xlApp.Application.Workbooks.Open(path);
Excel.Worksheet xlsht =workbook.Worksheets[Environment.UserName];
Excel.Range range = xlsht.UsedRange;
int rowcount = range.Rows.Count;
Excel.Range next= (Excel.Range)xlsht.Rows[rowcount+1];
next.Insert(); //Another method to add new row
xlsht.Cells[rowcount + 1, 1] = datforOV;
xlsht.Cells[rowcount + 1, 2] = hours;
xlsht.Cells[rowcount + 1, 3] = box;
workbook.Save();
workbook.Close();
xlApp.Quit();
MessageBox.Show("sucess");
}
Result:
Related
When I try and call Cells[fullRow, 1].get_end I get an error, object does not contain a definition for get_end and no extension method could be found.
I never saw anyone complain about this or describe this in comments of old stack overflow threads re the subject of finding last row in an excel column using C#
string path = #"Z:\New folder\Test123.xlsx";
MyApp = new Excel.Application();
MyApp.Visible = true;
MyBook = MyApp.Workbooks.Open(path);
MySheet = (Excel.Worksheet)MyBook.Sheets[1];
int fullRow = MySheet.Rows.Count;
int LastRow = MySheet.Cells[1,1].get_end(Excel.XlDirection.xlUp).Row;
If you assign the return from MySheet.Cells[1, 1] in to a Range object or call the function End, you should find that it works as expected.
Also, note the case of the function, it is meant to be: get_End.
// Working
Excel.Range firstCell1 = MySheet.Cells[1, 1];
int LastRow1 = firstCell1.get_End(Excel.XlDirection.xlUp).Row;
// Working
Excel.Range firstCell2 = MySheet.Cells[fullRow, 1];
int LastRow2 = firstCell2.get_End(Excel.XlDirection.xlUp).Row;
// Working
int LastRow3 = MySheet.Cells[fullRow, 1].End(Excel.XlDirection.xlUp).Row;
// Does not work
int LastRow4 = MySheet.Cells[1, 1].get_End(Excel.XlDirection.xlUp).Row;
I believe the problem is down to the Dynamic Binding done at runtime for COM Interop, but, I am not 100% certain of the reason.
You can use something like this
void Main()
{
Microsoft.Office.Interop.Excel.Application app;
Microsoft.Office.Interop.Excel.Workbook wkb;
app = new Microsoft.Office.Interop.Excel.Application();
wkb = app.Workbooks.Open(#"e:\temp\test.xlsx");
int row = GetLastRow(wkb, "sheet1", "A");
Console.WriteLine(row);
app.Quit();
}
int GetLastRow(Workbook wkb, string sheet, string column)
{
Microsoft.Office.Interop.Excel.Worksheet sht = wkb.Worksheets[sheet] as Worksheet;
Microsoft.Office.Interop.Excel.Range range = sht.Range[column + ":" + column];
range = range.Cells[range.Rows.Count, range.Column] as Range;
return range.End[XlDirection.xlUp].Row;
}
You can remove all that Microsoft.Office.Interop.Excel stuff adding the appropriate using directive.
I have a C# console application which needs a large Excel to be split into multiple Excel files based on the row count. The code below shows a source file with only 51 rows (including the header column rows) but the final source file will have 100,000+ rows.
The code is trying to skip the very first (header) row and then should copy from rows 2 through 11 and so on--I have the target files set to only 10 rows per file, to make developing faster.
Question So how do I copy rows 2 through 11 and subsequent 10 rows from the source Excel file and paste to multiple target Excel files so that the target files each will have 10 rows?
Here is the almost newly written code. It is loosely based on copying of specific range of excel cells from one worksheet to another worksheet and https://social.msdn.microsoft.com/Forums/vstudio/en-US/afd01976-63d0-4f96-9ba4-e3e2b6cf8d55/excel-with-c-how-to-specify-a-range-?forum=vsto
Now I am able to write 5 Excel files. But the first file has 9 rows (starting from row 2) while 2nd file has only 3 rows, starting with row 10, the 3rd has 13 rows starting, again, with row 10; the last two files have incrementally more rows, both starting with row 10.
So something wrong with my For Loop? Or the way I am selecting the ranges?
string startPath = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
string filePath_source = Path.Combine(startPath, #"Source_Files\Offers_Source_Temp.xlsx");
string filePath_copiedinto = Path.Combine(startPath, #"Source_Files\ToBeCopiedInto.xlsx");
app = new Excel.Application();
app.DisplayAlerts = false;
book = app.Workbooks.Open(filePath_source);
sheet = (Excel.Worksheet)book.Worksheets.get_Item((1));
int iRowCount = sheet.UsedRange.Rows.Count;
int maxrows = 10;//change this to something like 50,000 later. 01/16/18
int maxloops = iRowCount / maxrows;
int beginrow = 2; //skipping the header row.
Excel.Application destxlApp;
Excel.Workbook destworkBook;
Excel.Worksheet destworkSheet;
Excel.Range destrange;
string srcPath;
string destPath;
//Opening of first worksheet and copying
srcPath = filePath_source;
for (int i = 1; i <= maxloops; i++) {
Excel.Range rng = (Excel.Range)sheet.Range[sheet.Cells[beginrow, 1], sheet.Cells[maxrows, 3]];
rng.Copy(Type.Missing);
//opening of the second worksheet and pasting
destPath = filePath_copiedinto;
destxlApp = new Excel.Application();
destxlApp.DisplayAlerts = false;
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(startPath + "\\Output_Files\\" + beginrow + ".xlsx");
destworkBook.Close(true, null, null);
destxlApp.Quit();
beginrow = beginrow + maxrows;
string blah = null;
}
I would suggest to use OpenXml library to do that task. It is dependency free and supports the whole OpenXml structure.
Here a starting point how to read/write the rows:
using System;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
// Open the document for editing.
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
foreach (Row r in sheetData.Elements<Row>())
{
}
}
Now, writing is very similar:
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Create(fileName),
SpreadsheetDocumentType.Workbook))
{
// create the workbook
spreadSheet.AddWorkbookPart();
spreadSheet.WorkbookPart.Workbook = new Workbook (); // create the worksheet
spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet = new Worksheet();
// create sheet data
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet.AppendChild(new SheetData());
// create row
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet.First().AppendChild(new Row());
}
Got it! In my revised code in the Question, I came close but had some problem in the For Loop; fixed it per the code below. So here is the almost complete code. Thanks everyone for your help!!
try
{
string startPath = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
string filePath_source = Path.Combine(startPath, #"Source_Files\Offers_Source_Temp.xlsx");
string filePath_copiedinto = Path.Combine(startPath, #"Source_Files\ToBeCopiedInto.xlsx");
app = new Excel.Application();
app.DisplayAlerts = false;
book = app.Workbooks.Open(filePath_source);
sheet = (Excel.Worksheet)book.Worksheets.get_Item((1));
int iRowCount = sheet.UsedRange.Rows.Count;
int countColumns = sheet.UsedRange.Columns.Count;
int maxrows = 10;//change this to something like 50,000 later. 01/16/18
int maxloops = iRowCount / maxrows;
int beginrow = 2; //skipping the header row.
Excel.Application destxlApp;
Excel.Workbook destworkBook;
Excel.Worksheet destworkSheet;
Excel.Range destrange;
string srcPath;
string destPath;
//Opening of first worksheet and copying
srcPath = filePath_source;
for (int i = 1; i <= maxloops; i++) {
/// Excel.Range rng = (Excel.Range)sheet.Range[sheet.Cells[beginrow, 1], sheet.Cells[maxrows, 3]];
Excel.Range startCell = sheet.Cells[beginrow, 1];//not sure the second parameter needed?
Excel.Range endCell = sheet.Cells[beginrow+maxrows-1, 3];//not sure the second parameter needed?
Excel.Range rng = sheet.Range[startCell, endCell];
rng = rng.EntireRow;//so second parameters above should not be needed. But doesn't work without it!
rng.Copy(Type.Missing);
//opening of the second worksheet and pasting
destPath = filePath_copiedinto;
destxlApp = new Excel.Application();
destxlApp.DisplayAlerts = false;
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(startPath + "\\Output_Files\\" + beginrow + ".xlsx");
destworkBook.Close(true, null, null);
destxlApp.Quit();
beginrow = beginrow + maxrows;
}//for loop
}
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 in the middle of simple method, that saves my DataGridView into an Excel document (1 sheet only) and also adds VBA code and a button to run the VBA code.
public void SaveFile(string filePath)
{
Microsoft.Office.Interop.Excel.ApplicationClass ExcelApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
ExcelApp.Application.Workbooks.Add(Type.Missing);
//Change Workbook-properties.
ExcelApp.Columns.ColumnWidth = 20;
// Storing header part in Excel.
for (int i = 1; i < gridData.Columns.Count + 1; i++)
{
ExcelApp.Cells[1, i] = gridData.Columns[i - 1].HeaderText;
}
//Storing Each row and column value to excel sheet
for (int row = 0; row < gridData.Rows.Count; row++)
{
gridData.Rows[row].Cells[0].Value = "Makro";
for (int column = 0; column < gridData.Columns.Count; column++)
{
ExcelApp.Cells[row + 2, column + 1] = gridData.Rows[row].Cells[column].Value.ToString();
}
}
ExcelApp.ActiveWorkbook.SaveCopyAs(filePath);
ExcelApp.ActiveWorkbook.Saved = true;
ExcelApp.Quit();
}
I only implemented DataGridView export.
EDIT: Thanks to Joel I could, with proper words, search again for the solution. I think that this may be helpful. Would you correct me or give a tip or two about what I should look for.
I just wrote a small example which adds a new button to an existing workbook and afterwards add a macro which will be called when the button is clicked.
using Excel = Microsoft.Office.Interop.Excel;
using VBIDE = Microsoft.Vbe.Interop;
...
private static void excelAddButtonWithVBA()
{
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlBook = xlApp.Workbooks.Open(#"PATH_TO_EXCEL_FILE");
Excel.Worksheet wrkSheet = xlBook.Worksheets[1];
Excel.Range range;
try
{
//set range for insert cell
range = wrkSheet.get_Range("A1:A1");
//insert the dropdown into the cell
Excel.Buttons xlButtons = wrkSheet.Buttons();
Excel.Button xlButton = xlButtons.Add((double)range.Left, (double)range.Top, (double)range.Width, (double)range.Height);
//set the name of the new button
xlButton.Name = "btnDoSomething";
xlButton.Text = "Click me!";
xlButton.OnAction = "btnDoSomething_Click";
buttonMacro(xlButton.Name, xlApp, xlBook, wrkSheet);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
xlApp.Visible = true;
}
And here we got the buttonMacro(..) method
private static void buttonMacro(string buttonName, Excel.Application xlApp, Excel.Workbook wrkBook, Excel.Worksheet wrkSheet)
{
StringBuilder sb;
VBIDE.VBComponent xlModule;
VBIDE.VBProject prj;
prj = wrkBook.VBProject;
sb = new StringBuilder();
// build string with module code
sb.Append("Sub " + buttonName + "_Click()" + "\n");
sb.Append("\t" + "msgbox \"" + buttonName + "\"\n"); // add your custom vba code here
sb.Append("End Sub");
// set an object for the new module to create
xlModule = wrkBook.VBProject.VBComponents.Add(VBIDE.vbext_ComponentType.vbext_ct_StdModule);
// add the macro to the spreadsheet
xlModule.CodeModule.AddFromString(sb.ToString());
}
Found this information within an KB article How To Create an Excel Macro by Using Automation from Visual C# .NET
I am currently working in a C# application which has a class which will generate an excel file. Everything went smooth. The data populated on the excel sheet has 'Times New Roman' has font. I would like to change it to some other fonts (Calibari). How can I do that programmatically.
From what I tried, simply changing font name, size etc... on range changes font for that range:
range.Font.Name = "Arial"
range.Font.Size = 10
range.Font.Bold = true
Here is how:
//Declare Excel Interop variables
Microsoft.Office.Interop.Excel.Application xlApp;
Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
//Initialize variables
xlApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
//Set global attributes
xlApp.StandardFont = "Arial Narrow";
xlApp.StandardFontSize = 10;
Focus on the 2nd line from the bottom. That sets the default font type, but I wanted to show you where xlApp came from, even if it's self explanatory.
the following worked for me, when I tried setting the default application font it did nothing so I was able to set the font name of the active sheet rows and it worked. Also worth noting I used and tested this using Excel Interop version 12
Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
//Create\Add workbook object
Excel.Workbooks workBooks = excelApp.Workbooks;
//Excel.Workbook
Excel.Workbook workBook = workBooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);
//use worksheet object
Excel.Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;
//set default font
workSheet.Rows.Font.Name = "Arial";
Have you tried something like this:
new Font("Arial", 10, FontStyle.Bold);
var range = worksheet.get_Range(string.Format("{0}:{0}", startRowIndex, Type.Missing));
range = range.EntireRow;
range.Style.Font.Name = "Arial";
range.Style.Font.Bold = false;
range.Style.Font.Size = 12;
Hey Do not upset I do it and works for me .
Just define Font.Name and excell sheet fill all sheet use everywhere .
Any Way Code is :
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount-1]].Merge();
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount - 1]].Interior.Color = ColorTranslator.ToOle(Color.FromArgb(23,65,59));
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount - 1]].Borders.LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlContinuous;
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount - 1]].Style.Font.Name = "Arial Narrow";
((Excel.Range)WorksheetResult.UsedRange).Font.Name = "Avant Garde";
WorksheetResult is just a sheet reference.
Found this thread by my own similar problem. I had a little picker box that, when a cell was clicked, needed to paste a unique font's symbol into the selected excel cell. Here's how i did that:
string selectedItem = arrayOfSymbols[tableLayoutPanel1.GetRow((Control)sender), tableLayoutPanel1.GetColumn((Control)sender)];
Excel.Worksheet ws = Globals.ThisAddIn.Application.ActiveSheet;
Excel.Range cell = Globals.ThisAddIn.Application.ActiveCell;
ws.Cells[cell.Row, cell.Column].Font.Name = "My Custom Font";
ws.Cells[cell.Row, cell.Column] = selectedItem;