Update Excel cells with a textbox via MVC4 - c#

I started to learn programming again yesterday. As an assignment i made an API where it is possible to create Excel files, where it is possible to fill in a textbox and the text from this textbox will fill a cell in excel. Here is what i made for the assignment to create a excel file(The question comes after the code).
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DownloadExcel(string field, int id = 0)
{
var bla = field;
List<Record> obj = new List<Record>();
obj = RecordInfo(field);
StringBuilder str = new StringBuilder();
str.Append("<table border=`" + "1px" + "`b>");
str.Append("<tr>");
str.Append("<td><b><font face=Arial Narrow size=3>Fieldname</font></b></td>");
str.Append("</tr>");
foreach (Record val in obj)
{
str.Append("<tr>");
str.Append("<td><font face=Arial Narrow size=" + "14px" + ">" + val.Fieldname.ToString() + "</font></td>");
str.Append("</tr>");
}
str.Append("</table>");
HttpContext.Response.AddHeader("content-disposition", "attachment; filename=Information" + DateTime.Now.Year.ToString() + ".xls");
this.Response.ContentType = "application/vnd.ms-excel";
byte[] temp = System.Text.Encoding.UTF8.GetBytes(str.ToString());
return File(temp, "application/vnd.ms-excel");
}
public List<Record> RecordInfo(string fieldname = "test")
{
List<Record> recordobj = new List<Record>();
recordobj.Add(new Record { Fieldname = fieldname });
return recordobj;
}
}
Now my question is. Is it possible to modify fields in Excel via C# and is it possible to modify a specific cell with a textbox and that the data will be changed on click (like for example, I want to adjust cell D7 from 1 to 2, so I go to my form and fill in 2 in the textbox and press submit and the specific cell will show the new number when i open Excel). I searched but couldn't find the answer. A link to how it can be made is appreciated.
Thanks in advance!

I recommend that you switch to another approach and use a "grown-up" library for the excel file creation. EPPlus is free, offers great features and is very easy to use.
Have a look at the examples: http://epplus.codeplex.com/wikipage?title=WebapplicationExample

A bit late, but I answered my own question some time ago.
For the ones who are interested, here is the code for how I fixed my problem.
public class ExcelModel
{
Application excelApp;
string newValue;
public ExcelModel(string newValue)
{
excelApp = new Application();
this.newValue = newValue;
}
public void openExcelSheet(string fileName)
{
Workbook workbook = excelApp.Workbooks.Open(fileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
Worksheet sheet = (Worksheet)workbook.Worksheets.get_Item(2);
double oldValue = sheet.get_Range("D6").get_Value();
sheet.Cells[6, 4] = newValue;
workbook.SaveAs("\\Users\\user1\\Downloads\\text3.xlsx", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
workbook.Close();
excelApp.Visible = true;
Workbook newWorkbook = excelApp.Workbooks.Open("\\Users\\user1\\Downloads\\text3.xlsx");
}
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult UpdateExcel(string field, int id = 0)
{
ExcelModel model = new ExcelModel(field);
string file = "\\Users\\user1\\Downloads\\Testdocument.xlsx";
model.openExcelSheet(file);
return RedirectToAction("Index");
}
public List<Record> RecordInfo(string fieldname = "test")
{
List<Record> Recordobj = new List<Record>();
Recordobj.Add(new Record { Fieldname = fieldname });
return Recordobj;
}
}

Related

C# Excel PivotItem Visibility

I want to use C# to get the values of the checkboxes from the Excel Filter.
so ...
Proj_1 should be true
Proj_2 should be true
Proj_3 should be false
Proj_4 should be false
Below is the code I am currently using but item.Visible is ALWAYS false no matter if the UI has a check by it or not (unless I uncheck Select Multiple Items) but I need that checked
foreach (PivotField fld in pivotFields)
{
Console.WriteLine(fld.Name + " -- " + fld.Orientation + " -- " + fld.EnableItemSelection + " -- " + fld.EnableMultiplePageItems + " -- ");
foreach (PivotItem item in fld.PivotItems(Type.Missing))
{
if (item.Visible == true)
{
Console.WriteLine("Item" + item.Name + " in field " + fld.Name + "is Visible (Selected)");
}
else
{
Console.WriteLine("Item" + item.Name + " in field " + fld.Name + "is Hidden (Not Selected)");
}
}
}
This is something that works, if the Microsoft.Office.Interop.Excel library is added:
namespace ExcelAtSomething
{
using System;
using Excel = Microsoft.Office.Interop.Excel;
class Startup
{
static void Main()
{
string filePath = #"C:\Users\stackoverflow\Desktop\Sample.xlsx";
Excel.Application excel = new Excel.Application();
excel.Visible = true;
excel.EnableAnimations = true;
Excel.Workbook wkb = Open(excel, filePath);
foreach (Excel.Worksheet xlWorksheet in wkb.Worksheets)
{
Excel.PivotTables pivotTablesCollection = xlWorksheet.PivotTables();
if (pivotTablesCollection.Count > 0)
{
for (int i = 1; i <= pivotTablesCollection.Count; i++)
{
Excel.PivotTable currentPivotTable = pivotTablesCollection.Item(i);
Console.WriteLine($"Table is named -> {currentPivotTable.Name}");
foreach (Excel.PivotField pivotField in currentPivotTable.PivotFields())
{
Console.WriteLine($"\nField is named -> {pivotField.Name}");
foreach (Excel.PivotItem visibleItems in pivotField.VisibleItems)
{
Console.WriteLine($"Visible item name -> {visibleItems.Name}");
}
foreach (Excel.PivotItem PivotItem in pivotField.PivotItems())
{
Console.WriteLine($"Item is named -> {PivotItem.Name}");
Console.WriteLine(PivotItem.Visible);
}
}
}
}
}
excel.EnableAnimations = true;
wkb.Close(true);
excel.Quit();
Console.WriteLine("Finished!");
}
private static Excel.Workbook Open(Excel.Application excelInstance,
string fileName, bool readOnly = false,
bool editable = true, bool updateLinks = true)
{
Excel.Workbook book = excelInstance.Workbooks.Open(
fileName, updateLinks, readOnly,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
return book;
}
}
}
The trick is that except for PivotItem, there is a PivotField, which has a list of its visible items. If you display these, then only the visible ones are displayed:
Playing a bit and probably making custom a function, that returns the non-visible items, which are not in that list is a good option for the Excel.

Loop through Excel files and copy correct range in a separate file with C#

Intro:
Today I have decided to make an Excel automatization task with C#. This is probably the first time I am doing something like this, thus the problems are plenty.
The task:
Pretty much, the idea is the following - I have 4 excel files in folder strPath. I have to loop through all of them and make a file called Report.xlsx in the same folder, with the information from those files.
The information, that I need is anything, below row 9. Thus, the first row to copy is row number 10. That is why, the first file I loop for is saved as Report, and the bMakeOnce value is changed. After the first file is looped and saved As, I start entering into the else condition. There I locate the last used row of the XL files and I try to copy the range into the sheetReport.
The questions:
First of all - any ideas for code improvement;
Whenever I am looping through the files I get the following picture telling me that each of the looping file is opened already.
Any good idea how to do the range copy better? Currently, I simply try to put the copied range on every 200+n line, to avoid some confusion for me.
Any idea why I do not get anything in the sheetReport, except for the first file?
The code I am using (initially, for the current goto Github below):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;
class MainClass
{
static void Main()
{
string strPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), #"..\..\..\"));
string[] strFiles = Directory.GetFiles(strPath);
Excel.Application excel = null;
bool bMakeOnce = true;
int intFirstLine = 10;
int intLastColumn = 50;
int lastRow;
int lastRowReport;
Excel.Workbook wkbReport = null;
string strWkbReportPath;
int n = 0;
foreach (string strFile in strFiles)
{
Console.WriteLine(strFile);
Excel.Workbook wkb = null;
Excel.Worksheet sheet = null;
Excel.Worksheet sheetReport = null;
Excel.Range rngLast = null;
Excel.Range rngLastReport = null;
Excel.Range rngToCopy = null;
Excel.Range rngDestination = null;
excel = new Excel.Application();
excel.Visible = true;
wkb = OpenBook(excel, strFile);
if (bMakeOnce)
{
bMakeOnce = false;
strWkbReportPath = wkb.Path + "\\" + "Report.xlsx";
wkb.SaveAs(strWkbReportPath);
wkbReport = OpenBook(excel, strWkbReportPath);
}
else
{
wkb = OpenBook(excel, strFile);
sheetReport = wkbReport.Worksheets[1];
sheet = wkb.Worksheets[1];
n++;
rngLastReport = sheetReport.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
rngLast = sheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
rngToCopy = sheet.Range[sheet.Cells[intFirstLine, 1], sheet.Cells[rngLast.Row, intLastColumn]];
int size = rngToCopy.Rows.Count;
Console.WriteLine(size);
rngDestination = sheetReport.Range[sheetReport.Cells[200 * n, 1], sheetReport.Cells[200 * n + size, intLastColumn]];
rngToCopy.Copy(rngDestination);
//rngDestination.PasteSpecial(Excel.XlPasteType.xlPasteAll);
}
}
wkbReport.Close(false);
excel.Quit();
}
public static Excel.Workbook OpenBook(Excel.Application excelInstance, string fileName, bool readOnly = false, bool editable = true, bool updateLinks = true)
{
Excel.Workbook book = excelInstance.Workbooks.Open(
fileName, updateLinks, readOnly,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
return book;
}
}
Now it works somehow, producing what I want:
using System;
using System.IO;
using Excel = Microsoft.Office.Interop.Excel;
class MainClass
{
static void Main()
{
string strPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), #"..\..\..\"));
string[] strFiles = Directory.GetFiles(strPath);
Excel.Application excel = null;
bool bMakeOnce = true;
string strReportName = "Report.xlsx";
int intFirstLine = 10;
int intLastColumn = 50;
int lastRow;
int lastRowReport;
int intTotalRows;
Excel.Workbook wkbReport = null;
string strWkbReportPath;
int n = 0;
excel = new Excel.Application();
excel.Visible = true;
foreach (string strFile in strFiles)
{
if (strFile.Contains(strReportName))
{
Console.WriteLine(strReportName + " is deleted.");
File.Delete(strFile);
}
}
foreach (string strFile in strFiles)
{
if (strFile.Contains(strReportName))
{
continue;
}
Console.WriteLine(strFile);
Excel.Workbook wkb = null;
Excel.Worksheet sheet = null;
Excel.Worksheet sheetReport = null;
Excel.Range rngLastReport = null;
Excel.Range rngToCopy = null;
wkb = Open(excel, strFile);
if (bMakeOnce)
{
bMakeOnce = false;
strWkbReportPath = wkb.Path + "\\" + strReportName;
wkb.SaveAs(strWkbReportPath);
wkb.Close();
wkbReport = Open(excel, strWkbReportPath);
}
else
{
sheetReport = wkbReport.Worksheets[1];
sheet = wkb.Worksheets[1];
//lastRow = sheet.Cells[1, 3].get_End(Excel.XlDirection.xlUp).Row;
intTotalRows = sheet.Rows.Count;
lastRow = sheet.Cells[intTotalRows, 1].End(Excel.XlDirection.xlUp).Row;
lastRowReport = sheetReport.Cells[intTotalRows, 1].End(Excel.XlDirection.xlUp).Row;
//lastRowReport = sheetReport.Cells[intTotalRows, 1].get_End(Excel.XlDirection.xlUp).Row;
//lastRowReport = sheetReport.Cells[intTotalRows, intTotalRows.End[Excel.XlDirection.xlUp]].Row;
n++;
rngToCopy = sheet.Range[sheet.Cells[intFirstLine,1],sheet.Cells[lastRow, intLastColumn]];
int size = rngToCopy.Rows.Count;
rngLastReport = sheetReport.Range[sheetReport.Cells[lastRowReport+1, 1], sheetReport.Cells[lastRowReport + 1+size, intLastColumn]];
rngToCopy.Copy(rngLastReport);
wkb.Close(false);
}
}
wkbReport.Close(true);
excel.Quit();
Console.WriteLine("Finished!");
}
public static Excel.Workbook Open(Excel.Application excelInstance, string fileName, bool readOnly = false, bool editable = true, bool updateLinks = true)
{
Excel.Workbook book = excelInstance.Workbooks.Open(
fileName, updateLinks, readOnly,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
return book;
}
//public static Excel.Workbook OpenBook(Excel.Application excelInstance, string fileName, bool readOnly = false, bool editable = true, bool updateLinks = true)
//{
// Excel.Workbook book = excelInstance.Workbooks.Open(
// fileName, updateLinks, readOnly,
// Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
// Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
// Type.Missing, Type.Missing);
// return book;
//}
}
Thus, I have put it codeReview here: https://codereview.stackexchange.com/questions/153054/loop-through-excel-files-and-copy-correct-range-in-a-separate-file

How to persist Excel cell formats in C# Interop?

I am reading an Excel sheet programmatically using Microsoft.Office.Interop.Excel in C#.
I am able to read it row by row and converting each row to a string arrray. Then, I am adding these rows to a DataTable.
Every thing works fine except the one of the column in the Excel contains Date values, and when I fetch it from the Excel Range object and cast it to string array, the date values gets converted to some sort of decimal numbers.
For e.g.-
If the date value is '6/4/2016 8:14:39 PM', I get the value as '42522.5224305556'
If the date value is '5/27/2016 1:10:12 PM', I get the value as '42517.54875'
Below is my code-
private System.Data.DataTable GetTicketsFromExcel(string excelFilePath)
{
System.Data.DataTable dtblTickets = new System.Data.DataTable();
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Worksheet ws = new Worksheet();
Workbook wb = null;
try
{
wb = excelApp.Workbooks.Open(excelFilePath, Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Sheets.get_Item(1);
Range usedRange = ws.UsedRange;
Range rowRange;
string[] lsRow = null;
for (int i = 1; i <= usedRange.Columns.Count; i++)
{
dtblTickets.Columns.Add(usedRange.Cells[5, i].Value.ToString());
}
string sortColumn = "Reported On";
string sortDirection = "DESC";
dtblTickets.Columns[sortColumn].DataType = typeof(DateTime);
for (int row = 6; row <= usedRange.Rows.Count; row++)
{
//dtblTickets.Columns.Add()
rowRange = usedRange.Rows[row];
object[,] cellValues = (object[,])rowRange.Value2;
lsRow = cellValues.Cast<object>().Select(o => Convert.ToString(o)).ToArray<string>();
dtblTickets.Rows.Add(lsRow.ToArray());
}
dtblTickets.DefaultView.Sort = sortColumn + " " + sortDirection;
dtblTickets = dtblTickets.DefaultView.ToTable();
}
catch (Exception ex)
{
}
finally
{
wb.Close();
excelApp.Quit();
Marshal.ReleaseComObject(ws);
Marshal.ReleaseComObject(wb);
Marshal.ReleaseComObject(excelApp);
ws = null;
wb = null;
excelApp = null;
}
return dtblTickets;
}
Please note-
I don't want to use OLEDB to read and export this
I want to able to read the Excel row by row (without extracting each cell value and converting them)
I don't want to convert/format the original Excel document data
Can someone please help me with this?
Not quite sure, if you want to solve the problem this way, but one way is to change the property of the Cells (or the whole row or column) in Excel.
Right click on a Cell
Format Cells
Under "Number" select Category "Text" for the Cells.
I've tested it and it worked.

How to Overwrite existing Excel file by adding new sheet with Excel Interop - C#

My program, every hour, does some math calculations and saves these result into excel. When it first run(lets say 08:00 AM) it creates a excel workbook and one sheet namely "Sheet1". It saves excel and releases the COM objects. so far everything is fine.
My problem begins with second run (09:00 AM). when it tries to save new results, it overwrites existing excel file (This is OK, the way i want it) but it overwrites Sheet1 which was created in 08:00 AM. I want it to save new result in Sheet2.
In third run, i want it to save result in Sheet3
In fourth run, i want it to save result in Sheet4. so on so forth..
How can i change my code to do like above ?
thanks in advance..
My Code:
using excelApp = Microsoft.Office.Interop.Excel;
public static void Main(string[] arg)
{
while (true)
{
writeToExcel();
int wait = 3600 * 1000;
System.Threading.Thread.Sleep(Convert.ToInt32(wait));
}
}
public static void writeToExcel()
{
excelApp.Application excl = new Microsoft.Office.Interop.Excel.Application();
excl.Visible = true;
//MATH CALCULATIONS......
excelApp.Workbook wb = excl.Workbooks.Add(excelApp.XlWBATemplate.xlWBATWorksheet);
excelApp.Worksheet ws1 = (excelApp.Worksheet)wb.Worksheets[1];
excelApp.Worksheet ws2 = (excelApp.Worksheet)wb.Sheets.Add();
excelApp.Worksheet ws3 = (excelApp.Worksheet)wb.Sheets.Add();
excelApp.Worksheet ws4 = (excelApp.Worksheet)wb.Sheets.Add();
excelApp.Worksheet ws5 = (excelApp.Worksheet)wb.Sheets.Add();
excl.DisplayAlerts = false;
string fileName = string.Format(#"{0}\Data_" + DateTime.Now.Month + "-" DateTime.Now.Day + ".xlsx", Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory));
workSheet.SaveAs(fileName);
Console.WriteLine("Excel Saved Successfully!!");
excl.Quit();
// Release COM objects
if (excl != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(excl);
if (workSheet != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(workSheet);
excl = null;
workSheet = null;
GC.Collect();
}
You need to get the work book from the saved file. So at the beginning of your routine, you need a mechanism to determine if today's file already exists, if so, use the the following to get your Workbook.
Workbook WB = ExcelApp.Workbooks.Open(fileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
Hopefully this helps you see the error in your ways.

How do I get and compare the contents of a cell in an Excel spreadsheet by name?

I'm trying to compare a cell with a string to replace it if it is equal. But when I try to do the code below the 0x800A03EC error occurs.
int cont = 0;
string cell;
do
{
cont++;
cell = rCol.ToUpper() + cont.ToString(); // = "D1"
string cellData = ((Excel.Range)sheet.Cells[cell]).Value2.ToString();
if (cellData == from)
{
sheet.Cells[cell] = to;
}
} while (sheet.Cells[cell] == null);
How can I do this?
If you know the cell you want to check, for example A1, you can do it like this:
using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
namespace Test
{
class Program
{
static void Main(string[] args)
{
// create app
var excelApp = new Excel.Application();
// open workbook
var workbook = excelApp.Workbooks.Open(
#"C:\Users\Home\Documents\Book1.xlsx",
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
// open sheet
var sheet = (Excel.Worksheet)workbook.Sheets[1];
// create some variables
var from = "Pete";
var to = "Dave";
// compare cell A1 [1,1] with 'from'
if (string.Equals(sheet.Cells[1,1].Value, from))
{
sheet.Cells[1, 1].Value = to;
}
// save the workbook
workbook.Save();
// close the workbook and release resources
workbook.Close(true, workbook.Path);
Marshal.ReleaseComObject(workbook);
workbook = null;
}
}
}
Try this to get at a simple range:
int row = 1;
string col = "D";
string text = sheet.get_Range(col + row.ToString()).Value;
The 0x800A03EC error is a value returned by Excel, which means NAME_NOT_FOUND (see this SA question). Looks like you were passing a parameter that Excel could not find, probably because you were passing a string ("D1"), rather than two integer parameters (4,1).
It's impossible to tell where you are getting the rCol.ToUpper() value, without seeing more of your code. However, if you are attempting to go through a series of columns and rows to check for an equality condition (that's what it looks like you are attempting), you will quickly run into the pesky problem of how to increment the column value using capital letters (just try it; not much fun!).
One solution I did in VB recently was to use the native Excel function index, which uses numeric values to get at a particular cell. You would need to cast an object of the type Excel.WorksheetFunction to use that function. But I have since discovered there are easier solutions than using an Excel function:
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
namespace exceltest
{
class Program
{
static void Main(string[] args)
{
Microsoft.Office.Interop.Excel.Application xl = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook workbook = xl.Workbooks.Open(#"C:\test.xlsx");
Microsoft.Office.Interop.Excel.Worksheet sheet = workbook.Sheets[1];
xl.Visible = true;
//use this if you want to use native Excel functions (such as index)
Microsoft.Office.Interop.Excel.WorksheetFunction wsFunc = xl.WorksheetFunction;
int maxNum = 100; // set maximum number of rows/columns to search
string from = "blah";
string to = "blum";
//this is pretty slow, since it has to interact with 10,000 cells in Excel
// just one example of how to access and set cell values
for (int col = 1; col <= maxNum; col++)
{
for (int row = 1; row <= maxNum; row ++)
{
Range cell = (Range)sheet.Cells[row, col];
if ((string)cell.Value == from) //cast to string to avoid null reference exceptions
{
cell.Value = to;
}
}
}
}
}
}

Categories

Resources