Unable to Save Changes to Excel File (C# /) - c#

I've been trying to modify an Excel file called "Hoja de Resultados" adding three different variables each time (Puntuacion = Score, Fecha = Date, Hora = Time) inside of Visual Studio (C#). The excel sheet is linked to a DataGridView window which updates everytime the values are changed. The problem is, when there are less than 3 rows on the excel sheet, the program stops modifying the XLSX and sometimes it throws a seemingly random exception on "Program.cs".
string Direccion = "G:\\Archivos\\ProgramaciĆ³n\\Visual Basic\\UVG\\Microcontroladores - Proyecto Final ComunicaciĆ³n Serial\\Hoja de Resultados.xlsx";
Microsoft.Office.Interop.Excel.Application ExcelApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook Hoja = ExcelApp.Workbooks.Open(Direccion);
Microsoft.Office.Interop.Excel.Worksheet HojaActiva = ExcelApp.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;
Excel.Range Rango = HojaActiva.UsedRange;
int Posicion = Rango.Rows.Count;
int NuevaPosicion = Posicion + 1;
HojaActiva.Cells[NuevaPosicion, 1] = Puntuacion;
HojaActiva.Cells[NuevaPosicion, 2] = Fecha;
HojaActiva.Cells[NuevaPosicion, 3] = Hora;
Hoja.Saved = true;
ExcelApp.DisplayAlerts = false;
ExcelApp.ScreenUpdating = false;
ExcelApp.Visible = false;
ExcelApp.UserControl = false;
ExcelApp.Interactive = false;
Hoja.Close(true, Type.Missing, Type.Missing);
Marshal.ReleaseComObject(Hoja);
Marshal.ReleaseComObject(HojaActiva);
ExcelApp.Quit();
Sorry if it's partially in Spanish. But for some guidance:
Hoja = Workbook
HojaActiva = ActiveWorkbook

The reason that your Excel file is not updating is because you're not saving it anywhere in code. To solve your saving problem,
Change this line:
Hoja.Saved = true;
To
Hoja.Save();
When you write Hoja.Saved = true;, it tells Excel that there are no changes to be saved, whether that is the case or not, and that it can safely exit. Thus it does not actually save anything. You have to call the Save() method explicitly.
Another possible issue in your code is if the active range does not begin at the very top. The line int NuevaPosicion = Posicion + 1; assumes that your input starts on row one, which may not be the case. I would recommend changing that line to this:
//This way it doesn't matter where the range starts.
// Rows.Row returns the row on which the active range begins.
int NuevaPosicion = Posicion + Rango.Rows.Row;
Now new data will always be appended to the end of the sheet.
Other than the two points above, I cannot replicate your problem with less than 3 rows on the excel sheet, the code you posted seems to run with no issues under all conditions I could think of. Try making the change above and see if that solves your other issues. If not, please update your question to describe the seemingly random exception on "Program.cs" a bit more explicitly.

Related

How do I assign a 2 dimensional object[,] array the value of an Excel Interop Range.Value?

I am trying to assign a 2 dimensional object array to the value of an Excel interop Range.Value
I pull the 2 object dimensional array directly from the Range.Value of the UsedRange then do some changes to the data and put it back in as a 2 dimensional object[,].
When I save the workbook either as a copy or the original the new values I assign to the Range do not persist.
Here is my code:
/// <summary>
/// Writes the data from this excel document to <see cref="ExcelDoc.FilePath"/>
/// </summary>
/// <param name="newFilePath">used to specify a new file path</param>
/// <remarks>This will overwrite any data in the file</remarks>
public void WriteDataToExcelInterop(string newFilePath = "")
{
Excel.Application exApp = new();
GetWindowThreadProcessId(exApp.Hwnd, out int excelProcessId);
Process excelProcess = Process.GetProcessById(excelProcessId);
try
{
exApp.DisplayAlerts = false;
exApp.AutomationSecurity = MsoAutomationSecurity.msoAutomationSecurityForceDisable;
Excel.Workbook workbook = exApp.Workbooks.Open(FilePath);
for (int i = 1; i <= workbook.Worksheets.Count; i++)
{
Excel.Worksheet worksheet = workbook.Worksheets[i];
string name = worksheet.Name;
Excel.Range range = worksheet.UsedRange;
for (int j = 0; j < Worksheets.Count; j++)
{
if (Worksheets[j].Name == name)
{
range.Value = Worksheets[j].DataObjects; //this does not work
range.Formula = Worksheets[j].FormulaObjects; //this does work
}
}
}
if (newFilePath == "")
{
workbook.Close(true, FilePath);
exApp.Quit();
excelProcess.Kill();
}
else
{
workbook.SaveCopyAs(newFilePath);
workbook.Close(false, FilePath);
exApp.Quit();
excelProcess.Kill();
}
}
catch (Exception ex)
{
exApp.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
excelProcess.Kill();
ExceptionHandling.ExceptionData exData = new(ex);
MessageBox.Show(exData.FormatedMessage);
}
}
I thought maybe it had something to do with the fact that the UsedRange is ReadOnly according to documentation but the fact that I can change the formulas just fine says otherwise.
So I thought maybe it had to do with the datatype going into the variable so I tried changing the datatype of DataObjects but that did also not work.
I also thought maybe the line exApp.AutomationSecurity = MsoAutomationSecurity.msoAutomationSecurityForceDisable; was causing an issue with saving since this particular excel file is macro enabled. I commented out the line but this did nothing.
I'm thinking maybe the issue is Range.Value is not changing after save because some flag somewhere has not been raised properly.
I could really use a fresh set of eyes on this to see if I'm missing something.
I found what the issue was.
I assumed wrongly that Range.Value and Range.Formula were considered separate but when I make changes to Range.Formula it overwrites any changes I made to Range.Value since I am modifying Value BEFORE Formula. I will now have to change my code a little to account for this but my code was working as intended I just didn't realize why I was getting the results I was. Hopefully this can be helpful to anyone confused by this interaction in the future.

Changing color of Excel cell throws "'System.__ComObject' does not contain a definition for 'Interior'"

I am currently facing when I want to change the color of my excel sheet.
With my code I am already inserting entries into my excel file. Some specific cells should receive a special color.
When I run my code, I always get the same error.
Analyzing the Google/Stackoverflow results, I did not find a solution, even though there have been some complaints about this.
Workbooks wbs = excel.Workbooks;
Workbook sheet = wbs.Open(fileName);
excel.DisplayAlerts = false;
Worksheet y = sheet.ActiveSheet;
y.Copy(y, Type.Missing);
int index = y.Index;
int addRow = 2;
Worksheet x = (Worksheet)excel.Worksheets[index];
//...
//this line throws the error
x.Cells[addRow++, 1].Interior.Color = System.Drawing.Color.Blue;
//...
I am using Microsoft Office Interop which was very useful and did its job...until now.
You have to use the XlRgbColor to implement Color (for Excel interop 14.0):
x.Cells[addRow++, 1].Interior.Color = XlRgbColor.xlBlue;
if you have older version : you have to use translator
x.Cells[addRow++, 1].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Silver

Paste Values in Excel C#

Hello I have been trying to use the PasteSpecial paste values on another sheet but every thing I've tried I keep receiving the same error.
PasteSpecial method of Range class failed
xl.Application xlApp = Globals.ThisWorkbook.Application;
xl.Workbook wb = xlApp.ActiveWorkbook;
xl.Worksheet data = wb.Sheets["Data"];
xl.Worksheet datasheet = wb.Sheets["DataSheet"];
xl.Worksheet pivot = wb.Sheets["Pivot"];
xl.Range dataRng = data.Range["A1:C" +
xlApp.WorksheetFunction.CountA(data.Range["A:A"])];
xl.Range pivotRng = pivot.Range["A1"];
addPivot ap = new addPivot();
ap.DataRange = dataRng;
ap.DestRng = pivotRng;
ap.PivotSheet = pivot;
ap.createPivot("FullName","Date");
pivot.PivotTables("PivotTable1").NullString = "0";
pivot.UsedRange.Copy();
datasheet.Range["A2:GT999"].ClearContents();
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone,
System.Type.Missing, System.Type.Missing);
datasheet.Range["2:2"].ClearContents();
I have Tried
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone)
and what you see above.
thanks!
Do the ClearContents before the Copy. Excel is removing your copied range (same as hitting ESC in the program) when you clear those cells out. Then you are trying to PasteSpecial but nothing is in the clipboard any longer.
//...code above
datasheet.Range["A2:GT999"].ClearContents();
pivot.UsedRange.Copy();
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone,
System.Type.Missing, System.Type.Missing);
//code below...
If you are debugging this step-by-step, you would see the dashed border around the copied range disappear after the ClearContents call. Excel is a bit odd (but predictable) about when the copied range is forgotten. Editing cells anywhere in the Workbook is one sure way to make it forget.

Release excel instance created

I`m trying to get copy of one worksheet to another worhsheet,below is my code..I tried to release instances created of excel but still I see one instace of it in TaskManager.
C# Code:
try
{
wBook = xCel.Workbooks.Open(filePath);
xCel.Visible = false;
this.xCel.DisplayAlerts = false;
wBook = (Excel.Worksheet)wBook.Worksheets.get_Item(1);
wBook.Copy(Type.Missing, Type.Missing);
wBook = (Excel.Worksheet)wBook.Sheets[1];
wBook.SaveAs(strFileCopyPath);
}
finally
{
if (wBook != null)
{ wBook.Close();
Thread.Sleep(500);
}
Marshal.ReleaseComObject(wBook);
Marshal.ReleaseComObject(wBook);
}
Please some one tell what wrong i`m doing here?
thanks
You are doing nothing SANE wrong - but the concept of closing exel is very harebrained.
Thats because:
1: Excel creates sometimes intermediate objects, which are NOT visible for you, especially if you do something like this: DIm bla = excel.worksheet.range("A1").value. Then there are intermediate objects for excel.
2: The excel objects have to be closed in a very specific order.
This code SHOULD help:
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
this has to be double due to the fact that pass 1 only marks intermediate objects, but does not destroy them. It is garbage collection.
Also destroy your objects in this order:
Ranges etc.
Worksheets
Workbook
APP
like this:
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlSheet)
xlsheet = nothing
xlBook .Close()
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlBook )
xlbook = nothing
xlapp.quit
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlapp)
xlapp = nothing
use try catch to catch errors in this section, if an object is nothing..
You forgot to call Quit() method of Excel.Application object. Ususally I use the following code to close Excel (VB.Net code snippet):
xlApp.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
xlApp = Nothing
xlBook = Nothing
xlSheet = Nothing

Read the calculated values from Excel using AddIn Formulas and Microsoft Object Library

we are trying to retrieve a calculated value from a cell which has add-In formulas in it.
The sample add-in "myUtilityl.xla" is working properly in excel. It retrieves value for the addin function =ISOWEEKNUM(F9). But we are unable to retrieve the value programatically using C# & Microsoft Object Library. The add-In "myUtilityl.xla" is attached to Excel. Environment is VS2010
I am providing the sample code here.
string path = #"C:\Test.xls";
Workbook theWorkbook;
Worksheet theWorksheet;
Range readRange;
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
theWorkbook = app.Workbooks.Open(path);
Sheets theSheets = (Sheets)theWorkbook.Worksheets;
theWorksheet = (Worksheet)theWorkbook.Worksheets.get_Item("Sheet1");
readRange = theWorksheet.get_Range("B1");
MessageBox.Show(Convert.ToString(readRange.Value));
//theWorkbook.Save();
app.Workbooks.Close();
I am new to Microsoft Object library. Any help or clue will be very helpful.
Well Brijesh its working now. The only thing that was missing was that we have to open the xla.
app.Workbooks.Open(xlaFilePath);
Then it started working..
Thank you very much. i am posting the code here anyways
string path = #"C:\Test2.xls";
string xlaPath = #"C:\Test2.xla";
Workbook theWorkbook;
Worksheet theWorksheet, theWorksheet2;
Range readRange;
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
app.Workbooks.Open(xlaPath);
theWorkbook = app.Workbooks.Open(path);
theWorksheet2 = (Worksheet)theWorkbook.Worksheets.get_Item("Sheet2");
theWorksheet2.get_Range("A3").Value = 7;
theWorksheet2.get_Range("A4").Value = 7;
theWorkbook.RefreshAll();
theWorksheet = (Worksheet)theWorkbook.Worksheets.get_Item("Sheet1");
readRange = theWorksheet.get_Range("A1");
Console.WriteLine(Convert.ToString(readRange.Value));
Console.ReadLine(); //theWorkbook.Save();
theWorkbook.Close();
app.Workbooks.Close();
Above code inputs two values into cells of sheet2 and the VBA UDF calculated value is retrieved.
you may add following in your code sample
var addins = Application.AddIns.Add(xlaFilePath);
if (!addins.Installed)
{
addins.Installed = true;
}

Categories