How to write data to an existing Excel doc in C# - c#

I have a document that I need to update on a monthly basis and I'm writing an automation to do so. This is my first time attempting to update a document with C# as opposed to simply creating a new one. I have researched and tried implementing a few libraries that I've found online and here on StackOverflow, for example, ClosedXML, but so far I've had no luck. I understand this question has been asked here before, so my actual question is: Is my implementation incorrect/am I doing something wrong?
public void WriteToReport(List<BrandData> brandData, string reportFilePath)
{
using (var workbook = new XLWorkbook(reportFilePath))
{
var worksheet = workbook(1);
worksheet.Cell(26, 2).Value = "Hello World!";
workbook.SaveAs(reportFilePath);
}
}
Above is how I've tried to test ClosedXML so far. The GitHub docs imply that it should be this simple, but I don't see any changes made to the doc when the automation is finished. I've also tried using Streamwriter. If anyone can help me with ClosedXML or suggest another library that worked for them, it would be greatly appreciated.
Edit: Following explanations on other similar questions on here, I have tried this:
public void WriteToReport(List<BrandData> brandData, string reportFilePath)
{
var workbook = new XLWorkbook(reportFilePath);
var worksheet = workbook.Worksheet(1);
int numberOfLastColumn =
worksheet.LastColumnUsed().ColumnNumber();
IXLCell newCell = worksheet.Cell(numberOfLastColumn + 1, 1);
newCell.SetValue("Hello World");
workbook.SaveAs(reportFilePath);
}

Here is a simple example to write a string value to the first WorkSheet.
public void WriteToCell(string fileName, int row, int col, string value)
{
using var workbook = new XLWorkbook(fileName);
var worksheet = workbook.Worksheets.Worksheet(1);
worksheet.Cell(row, col).Value = value;
workbook.SaveAs(fileName);
}

Related

C# - Saving and Loading data to file

I decided to get into coding and am learning c#, after making a few small projects, I decided to step it up a little and make a text adventure game, with saving and loading, and if I get to feeling zany I'll try to add some multiplayer. While I haven't really hit a road block because of it, I can't help but feel that I am doing load function REALLY sub-optimally. The save is fine, I feel like it works for me, but the load I feel can be really simplified, I just don't know what to use.
I also wouldn't really mind, but with this way, if I add other attributes/skills or whatever else that needs to be saved, I will have to add everything to the load function as well, and it will be even longer.
I have tried to search around on here, the c# documentation, and other sites, but can't find a solution that works for this case. can anyone help me find a better way of doing this? Or is this the best I can really do since it's varying data types?
Edit: To simplify and clarify what answer I am searching for, I am trying to find a simpler and more scalable way to save and load the data to a file.
static void LoadGame(CharData PlayerData)
{
Console.WriteLine("Enter the name of the character to load as shown below.");
//getting current directory info, setting to di
DirectoryInfo di = new DirectoryInfo(Directory.GetCurrentDirectory());
//need to initialize these outside of a loop
int SaveFiles = 0;
string DisplayName = " ";
int DisplayNameLength = 0;
//looks through files in working directory ending in '.fasv', displays them in format '{x}. John Smith'
foreach (var fi in di.GetFiles("*.fasv"))
{
SaveFiles++;
DisplayNameLength = fi.Name.Length;
//remove .fasv from displayed name to make it look nicer
DisplayName = fi.Name.Remove(DisplayNameLength - 5, 5);
Console.WriteLine(SaveFiles.ToString() + ". " + DisplayName);
}
string toLoad = Console.ReadLine();
using StreamReader sr = new StreamReader(toLoad + ".fasv");
//the name is easy to get since it's a string. but integers...
PlayerData.Name = sr.ReadLine();
//... not so much. i hate all of this and i feel like it's gross, but i don't know how else to do it
int hp, xp, level, toughness, innovation, mind, empathy, spryness;
Int32.TryParse(sr.ReadLine(), out hp);
Int32.TryParse(sr.ReadLine(), out xp);
Int32.TryParse(sr.ReadLine(), out level);
Int32.TryParse(sr.ReadLine(), out toughness);
Int32.TryParse(sr.ReadLine(), out innovation);
Int32.TryParse(sr.ReadLine(), out mind);
Int32.TryParse(sr.ReadLine(), out empathy);
Int32.TryParse(sr.ReadLine(), out spryness);
PlayerData.Health = hp;
PlayerData.Level = level;
PlayerData.XP = xp;
PlayerData.Toughness = toughness;
PlayerData.Innovation = innovation;
PlayerData.Mind = mind;
PlayerData.Empathy = empathy;
PlayerData.Spryness = spryness;
sr.Close();
InGame(PlayerData);
}
static void SaveGame(CharData PlayerData)
{
using (StreamWriter sw = new StreamWriter(PlayerData.Name + ".fasv"))
{
foreach (System.Reflection.PropertyInfo stat in PlayerData.GetType().GetProperties())
{
//write player data properties to file line by line, using stat to iterate through the player data properties
sw.WriteLine(stat.GetValue(PlayerData));
}
sw.Close();
}
}
If you aren't set on a particular data format for the file data, I would recommend using a serializer such as JSON.NET. You can use NuGet to add newtonsoft.json to your project, and that would allow you to just do something similar to:
using (StreamWriter file = File.CreateText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
serializer.Serialize(file, playerData);
}
And then your code to read from the file would be pretty similar:
using (var file = File.OpenText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
return (CharData)serializer.Deserialize(file, typeof(CharData));
}
I borrowed those code snippets from newtonsoft.com. CreateText will create (or overwrite) the file and write the object as a JSON object.

Set html in Comment in excel using Aspose.Cells

I am working n exporting excel using Aspose.Cells. In that i need to show some HTML formatted comment in specific column. But when i set note in comment using HTML string then it automatically modify that HTML. I am usiing below code
Workbook workbook = GetExcelWorkbook<AuditLogExport>(auditLogExportData, templatePath);
CommentCollection comments = workbook.Worksheets[0].Comments;
for (int i = 0; i < exportData.Count; i++)
{
if (exportData[i].IsDetailedChange)
{
int commentIndex = comments.Add(string.Format("E{0}", (i + 2)));
Aspose.Cells.Comment comment = comments[commentIndex];
**comment.HtmlNote = GetAuditLogCommentNote(exportData[i]);**
comment.WidthCM = 8.0;
comment.HeightCM = 20.0;
}
}
public static string GetAuditLogCommentNote(AuditLog auditLog)
{
string note = string.Empty;
note = "<table width='400px' style='border:solid 1px black'><tr><th>Changed Field</th><th>Previous</th><th>Current</th></tr>";
foreach (var history in auditLog.DetailChanges)
{
note += string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>", history.FieldName, history.FirstChange, history.LastChange);
}
note += "</table>";
return note;
}
When i am assigning value to HtmlNote property of comment it automatically change html with font tags and strip all table tags from string. Can someone please help on this?
Update-1
Well, we think, your requirement is not achievable using Microsoft Excel. If something is not possible with Microsoft Excel, then it will also not be possible with Aspose.Cells or any other API automatically.
Please see the following screenshot. It shows how your HTML looks like when it is viewed in Web Browser and when it is copied/pasted to Excel comment.
If you think, your requirement is doable using Microsoft Excel, please provide us your sample Excel file that you have created manually using Microsoft Excel. We will check it and investigate this issue further.
Update-2
We tested this issue with the following code and found that if we try to create comment with HtmlNote property, it does not work.
We have logged this issue in our database for investigation and for a fix. Once, the issue is resolved, we will update you in this post.
This issue has been logged as
CELLSNET-46210 - Creating Comment using HtmlNote property does not work
C#
Workbook wb = new Workbook();
Worksheet ws = wb.Worksheets[0];
int idx = ws.Comments.Add("E4");
Comment cm = ws.Comments[idx];
//cm.HtmlNote = "<Font Style=\"FONT-WEIGHT: bold;FONT-FAMILY: Tahoma;FONT-SIZE: 9pt;COLOR: #000000;TEXT-ALIGN: left;\">Heading: </Font><Font Style=\"FONT-FAMILY: Tahoma;FONT-SIZE: 9pt;COLOR: #000000;TEXT-ALIGN: left;\">This is some para. </Font><Font Style=\"FONT-WEIGHT: bold;FONT-FAMILY: Tahoma;FONT-SIZE: 9pt;COLOR: #000000;TEXT-ALIGN: left;\">Heading2:</Font><Font Style=\"FONT-FAMILY: Tahoma;FONT-SIZE: 9pt;COLOR: #000000;TEXT-ALIGN: left;\"> This is some para2.</Font>";
cm.HtmlNote = new Workbook("input.xlsx").Worksheets[0].Comments[0].HtmlNote;
cm.IsVisible = true;
wb.Save("output.xlsx");
Update-3
Your issue logged as CELLSNET-46210 has been fixed in Aspose.Cells for .NET v18.7. Please download it from this link.
https://www.nuget.org/packages/Aspose.Cells/18.7.0
Note: I am working as Developer Advocate at Aspose

How to allow user to edit ranges in Protected Excel using EPPlus and c#?

I am exporting protected excel sheet using EPPlus in my winform(C#) project. Now I want functionality to allow user to edit ranges in that protected excel sheet using same plugin.
It would be great if you provide Code snippet.
Thanks in advance.
var fileName = "sample.xlsx";
var fileInfo = new FileInfo(fileName);
using (var excel = new ExcelPackage(fileInfo))
{
var ws = excel.Workbook.Worksheets.Add("sheet1");
ws.Protection.IsProtected = true;
ws.ProtectedRanges.Add("editable", new ExcelAddress("C:N"));
excel.Save();
}
I know its very late to reply but may help others. I had a similar issue and was struggling to get sorting and auto-filtering in protected worksheet.
After protecting the worksheet, I have added below two settings that allow sorting and auto-filtering.
ws.Protection.IsProtected = True
ws.Protection.AllowSort = True
ws.Protection.AllowAutoFilter = True
In my case however the next requirement was to unlock some columns to allow editing, I achieved that using:
ws.Column(12).Style.Locked = False
If you have a range however you can try something like this:
For Each cell In ws.Cells("B1:C8")
cell.Style.Locked = False
Next

openxml sdk excel how to parse and calculate formula

I have formula cell in excel file that has the formula =SUM(C2:C3).
From the web application hosted on remote webserver in the cloud, that does not have Excel installed, I would pass in values for C2 and C3.
I can also determine the exact formula in excel. How do I parse this formula programmatically in c# so that I could get the result of 6 if the input values of C2 and C3 were 2 and 4 respectively?
What if the formula is very complex, what is the best way to parse the formula and calculate it in C# on the server side in asp.net mvc application?
Code sample would really benefit me in this case.
If you provide a tool to open excel file and translate it's content to html you must deal with calculation.
If the file is "well created", for example manually with Excel you can be sure you don't need to manage computation of the formulas cause excel does the trick and stores both the formula in CellFormula's child element and result in CellValue's child element (See the method GetValue_D11()). So basically you just need to show the result.. which always will be a String.
Unfortunately you have to deal with styles and dataTypes, if you want to mantain behaviour.
Actually you have to build a complex web based spreadsheet viewer/editor.
Here is a sample "fixed" (totally not dynamic for all) for retrieving String values and formula values. if you wanna run the test be sure to download that file (http://www.devnmore.com/share/Test.xlsx) otherwise it can't works.
ShowValuesSample svs = new ShowValuesSample("yourPath\\Test.xlsx");
String[] test = svs.GetDescriptions_A2A10();
Double grandTotal = svs.GetValue_D11();
ShowValuesSample class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DocumentFormat.OpenXml.Packaging;
using Ap = DocumentFormat.OpenXml.ExtendedProperties;
using Vt = DocumentFormat.OpenXml.VariantTypes;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using A = DocumentFormat.OpenXml.Drawing;
using System.Globalization;
namespace TesterApp
{
public class ShowValuesSample
{
public String FileName { get; private set; }
private SpreadsheetDocument _ExcelDocument = null;
public SpreadsheetDocument ExcelDocument
{
get
{
if (_ExcelDocument == null)
{
_ExcelDocument = SpreadsheetDocument.Open(FileName, true);
}
return _ExcelDocument;
}
}
private SheetData _SheetDataOfTheFirstSheet = null;
public SheetData SheetDataOfTheFirstSheet
{
get
{
if (_SheetDataOfTheFirstSheet == null)
{
WorksheetPart shPart = ExcelDocument.WorkbookPart.WorksheetParts.ElementAt(0);
Worksheet wsh = shPart.Worksheet;
_SheetDataOfTheFirstSheet = wsh.Elements<SheetData>().ElementAt(0);
}
return _SheetDataOfTheFirstSheet;
}
}
private SharedStringTable _SharedStrings = null;
public SharedStringTable SharedStrings
{
get
{
if (_SharedStrings == null)
{
SharedStringTablePart shsPart = ExcelDocument.WorkbookPart.SharedStringTablePart;
_SharedStrings = shsPart.SharedStringTable;
}
return _SharedStrings;
}
}
public ShowValuesSample(String fileName)
{
FileName = fileName;
}
//In the file descriptions are stored as sharedString
//so cellValue it's the zeroBased index of the sharedStringTable
//in my example i saved 9 different values
//sharedstring it's a trick to reduce size of a file obiouvsly writing
//repetitive string just once
public String[] GetDescriptions_A2A10()
{
String[] retVal = new String[9];
for (int i = 0; i < retVal.Length; i++)
{
Row r = SheetDataOfTheFirstSheet.Elements<Row>().ElementAt(i + 1);
Cell c = r.Elements<Cell>().ElementAt(0);
Int32 shsIndex = Convert.ToInt32(c.CellValue.Text);
SharedStringItem shsItem = SharedStrings.Elements<SharedStringItem>().ElementAt(shsIndex);
retVal[i] = shsItem.Text.Text;
}
return retVal;
}
//The value it's stored beacause excel does
//To be sure it's correct you should perform all calculations
//In this case i'm sure Excel didn't stored the wrong value so..
public Double GetValue_D11()
{
Double retVal = 0.0d;
Int32 cellIndex = 0;
//cellIndex it's 0 and not 3, cause A11, B11, C11 are empty cells
//Another issue to deal with ;-)
Cell c = SheetDataOfTheFirstSheet.Elements<Row>().ElementAt(10).Elements<Cell>().ElementAt(cellIndex);
//as example take a look at the value of storedFormula
String storedFormula = c.CellFormula.Text;
String storedValue = c.CellValue.Text;
NumberFormatInfo provider = new NumberFormatInfo();
provider.NumberDecimalSeparator = ".";
provider.NumberGroupSeparator = ",";
provider.NumberGroupSizes = new Int32[] { 3 };
retVal = Convert.ToDouble(storedValue, provider);
return retVal;
}
}
}
spreadSheet.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
spreadSheet.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
worked for me.
I'm afraid its not possible. In Open XML you can read or change the formula. But you process the formula and get results through open xml.
Change the values for C2 and C3 for the formula and then save it in open xml, now open the document through Excel App. The values will be calculated and displayed.
Refer this SO Post, related to this issue open xml sdk excel formula recalculate cache issue
Refer this post too http://openxmldeveloper.org/discussions/formats/f/14/p/1806/158153.aspx
Hope this helps!

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