I've got some problems adding a excel document as attachment on a custom list. I've made an eventreceiver which collects data from other lists and puts the data into an excel document.
What i've tried to do to correct the problem:
tested CreateContentRow(index, item) in a separate console application (works)
checked if theres something wrong with fetching information from other lists (works)
tried saving to a document library (the file gets saved without content)
tried to open the xlsx document to se if there's something wrong with the xml (no custom xml added).
The code works just fine, but the document is saved but it's identical to the template, no content added.
using (var memory = new MemoryStream())
{
var binary = template.OpenBinary();
memory.Write(binary, 0, binary.Length);
using (var document = SpreadsheetDocument.Open(memory, true))
{
var workbookPart = document.WorkbookPart;
var worksheetparts = workbookPart.WorksheetParts;
var worksheetpart = worksheetparts.FirstOrDefault();
var sheetData = worksheetpart.Worksheet.GetFirstChild<SheetData>();
var index = 2;
foreach (var item in items)
{
var row = CreateContentRow(index, item);
index++;
sheetData.AppendChild(row);
}
properties.ListItem.Attachments.Add("name" + string.Format("{0:yyyy-MM-dd_HHmmss}", DateTime.Now) + ".xlsx", memory.ToArray());
properties.ListItem.Update();
}
}
I'm using the same approach on another list where i generate .docx documents there it works just fine. Is there a big difference in how i should save the document depending on if its .xlsx or .docx ?
When i debug the cells and rows are added to the sheetdata, but it does not get saved. Any ideas on how to fix this ?
In order to get the content saved i needed to add save statements.
wspart.Worksheet.Save();
document.WorkbookPart.Workbook.Save();
When i added the lines above before adding the attachemnt everything worked as it should.
Related
Following is the ActionResult Method created to return EXCEl File, which works fine. However, there is a problem that the content for each column is too large that the rows are mal-formed, which means there are wide blank-spaces between rows.
Following is the ActionResult code.
public ActionResult ImportExecelFile(int appNo)
{
List<PendingApproval> pendings = objPA.GetPendingApprovals(appNo.ToString());
string xml = String.Empty;
XmlDocument xmlDoc = new XmlDocument();
XmlSerializer xmlSerializer = new XmlSerializer(pendings.GetType());
using (MemoryStream xmlStream = new MemoryStream())
{
xmlSerializer.Serialize(xmlStream, pendings);
xmlStream.Position = 0;
xmlDoc.Load(xmlStream);
xml = xmlDoc.InnerXml;
}
//Create file
string fileName = "Pending_Approvals";
fileName += string.Format("-{0}", DateTime.Now.ToString("yyyy-MM-dd"));
fileName += ".xls";
byte[] fileContents = Encoding.UTF8.GetBytes(xml);
return File(fileContents, "application/vnd.ms-excel", fileName);
}
You're not creating an Excel file - you're creating XML and saving it with an .xls extension. Excel will try its best to open it, although it's probably going to raise a warning to the user because it's not really an Excel file.
Excel files can contain formatting, including column widths. Data serialized as XML will not have that. So there's really no way to control how Excel displays the XML.
You can accomplish what you're trying to do using a library like EPPlus that creates Excel workbooks using OpenXML. But if you're just saving XML as .xls then you won't have any control over formatting. (I'm amazed it opens at all, but I tested saving an XML file as .xls and it worked except for the warning message.)
I try to merge two Excel templates together in C#. Everything works fine except for the pictures in the second template that aren't merged.
I use the following steps to achieve my objective :
Load the first template
Add content to the first template
Load the second template
Copy the template's content in the clipboard
Paste the clipboard's content in the current Worksheet
Sadly, I have no control over the templates themselves as they are provided by the client and I can't change them.
Here's the code :
public static void MergeTemplates()
{
ExcelApp app = new ExcelApp();
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
app.Workbooks.Open($"{path}\\Excel1.xltx", true);
var workbook = app.Workbooks[1];
var sheet = workbook.Worksheets[1];
sheet.Range["A1"].Value = "Sometext";
//Loading the second template, which has images.
var xlTemp = app.Workbooks.Open($"{path}\\Excel2.xltx", true);
var xlSheetTemp = xlTemp.Worksheets[1];
xlSheetTemp.Range["A1", "C10"].Copy();
//Add the second template at the end of the first
sheet.Range["A2","C11"].PasteSpecial();
xlTemp.Close();
workbook.SaveAs($"{path}\\Test.xlsx");
workbook.Close();
app.Quit();
}
At the end, the images of Excel2.xltx aren't imported in the saved Test.xlsx document.
How can I merge the two templates so that the images will be included?
I am working on a Windows Phone 8 app to READ/WRITE Excel files. I asked a question here about this and the comment provided and many other links led me to OpenXml.
All of this got me good on how to create an Excel file and how to launch it. But now I am stuck at very basic of these all i.e. How to read an existing Excel file (probably created outside using MS Excel) cell-by-cell i.e. I want to access each cells and their values through my code. In the openXML thing I did this:
Stream localFile = App.GetResourceStream(new Uri("/ReadExcel;component/jai.xlsx"
,UriKind.Relative)).Stream;
MemoryStream ms = new MemoryStream();
localFile.CopyTo(ms);
DocumentFormat.OpenXml.Packaging.SpreadsheetDocument spreadsheetDoc =
DocumentFormat.OpenXml.Packaging.SpreadsheetDocument.Open(localFile, true);
{
var a = spreadsheetDoc.Package;
// Do work here
}
But it gives me error:
The type 'System.IO.Packaging.Package' is defined in an assembly that is not
referenced. You must add a reference to assembly 'WindowsBase, Version=4.0.0.0
So basically I am stuck at this WindowsBase.dll. I tried all various ways to import an assembly i.e. unblock and all, but nothing works.
So all I want to do is to programmatically access the content of an existing Excel file in my code cell-by-cell.
Please help or suggest whether it is even possible as of now in WP8.
I used the following method to read cells from an xlsx Excel file on Windows Phone 8:
Add the Microsoft Compression library to your project using NuGet
Adapt the code sample from the developer network to your needs - it shows how to read cells from an Excel file (and it needs the Compression lib)
Since I already extended the code a bit to handle empty columns and empty files properly you can also use my code:
public class ExcelReader
{
List<string> _sharedStrings;
List<Dictionary<string, string>> _derivedData;
public List<Dictionary<string, string>> DerivedData
{
get
{
return _derivedData;
}
}
List<string> _header;
public List<string> Headers { get { return _header; } }
// e.g. cellID = H2 - only works with up to 26 cells
private int GetColumnIndex(string cellID)
{
return cellID[0] - 'A';
}
public void StartReadFile(Stream input)
{
ZipArchive z = new ZipArchive(input, ZipArchiveMode.Read);
var worksheet = z.GetEntry("xl/worksheets/sheet1.xml");
var sharedString = z.GetEntry("xl/sharedStrings.xml");
// get shared string
_sharedStrings = new List<string>();
// if there is no content the sharedStrings will be null
if (sharedString != null)
{
using (var sr = sharedString.Open())
{
XDocument xdoc = XDocument.Load(sr);
_sharedStrings =
(
from e in xdoc.Root.Elements()
select e.Elements().First().Value
).ToList();
}
}
// get header
using (var sr = worksheet.Open())
{
XDocument xdoc = XDocument.Load(sr);
// get element to first sheet data
XNamespace xmlns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
XElement sheetData = xdoc.Root.Element(xmlns + "sheetData");
_header = new List<string>();
_derivedData = new List<Dictionary<string, string>>();
// worksheet empty?
if (!sheetData.Elements().Any())
return;
// build header first
var firstRow = sheetData.Elements().First();
// full of c
foreach (var c in firstRow.Elements())
{
// the c element, if have attribute t, will need to consult sharedStrings
string val = c.Elements().First().Value;
if (c.Attribute("t") != null)
{
_header.Add(_sharedStrings[Convert.ToInt32(val)]);
} else
{
_header.Add(val);
}
}
// build content now
foreach (var row in sheetData.Elements())
{
// skip row 1
if (row.Attribute("r").Value == "1")
continue;
Dictionary<string, string> rowData = new Dictionary<string, string>();
// the "c" elements each represent a column
foreach (var c in row.Elements())
{
var cellID = c.Attribute("r").Value; // e.g. H2
// each "c" element has a "v" element representing the value
string val = c.Elements().First().Value;
// a string? look up in shared string file
if (c.Attribute("t") != null)
{
rowData.Add(_header[GetColumnIndex(cellID)], _sharedStrings[Convert.ToInt32(val)]);
} else
{
// number
rowData.Add(_header[GetColumnIndex(cellID)], val);
}
}
_derivedData.Add(rowData);
}
}
}
}
This works for simple Excel files having one work sheet and some text and number cells. It assumes there is a header row.
Usage is as follows:
var excelReader = new ExcelReader();
excelReader.StartReadFile(excelStream);
After reading excelReader.Headers contains the header names, excelReader.DerivedData contains the rows. Each row is a Dictionary having the header as key and the data as value. Empty cells won't be in there.
Hope this gets you started.
Unfortunately, it is not possible to use the official OpenXML SDK by Microsoft. The reason is exactly the exception you already ran into. WP8 does not have the System.IO.Packaging namespace available which is required to extract/compress the zip-based xlsx file format. Adding WindowsBase.dll won't work either because it is not compiled for WP8.
After googling for quite some time in the last two years about this the only 3 solutions that I know are (despite developing Excel support from zero by your own :) ):
Use the Ag.OpenXML open source project which you can find on http://agopenxml.codeplex.com/ . The source repository contains an implementation to write an Excel file (the downloadable package only contains Word export). I use this in my WP8 app for quite some time and it works well despite the lack of a lot of features. Unfortunately, this package is not maintained anymore since 2011. However, it might be a good start for you.
Use the commercial libraries of ComponentOne https://www.componentone.com/SuperProducts/StudioWindowsPhone/
Use the commercial libraries of Syncfusion http://www.syncfusion.com/products/windows-phone
I want to create an excel document based on a template using Open XML Format SDK 2.0.
I have followed this tutorial Creating Documents by Using the Open XML Format SDK 2.0 CT.
My problem is that the rows and cells i put in to the document doesn't get saved. When I open the document it looks just like the template.
There is no exceptions thrown when I run my code. I figure I have to force the changes to be saved in the document, but I cant figure out how.
Here's some of my code:
public static void GenerateExcelReportToDisk()
{
var factory = new Factory();
var generated = "result.xlsx";
var newFile = Util.GetReportTargetPath() + generated;
var templateFile = Util.GetReportTemplatePath() + #"template.xlsx";
File.Copy(templateFile, newFile, true);
using (var myWorkbook = SpreadsheetDocument.Open(newFile, true))
{
var workbookPart = myWorkbook.WorkbookPart;
var worksheetPart = workbookPart.WorksheetParts.First();
var sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
//Get data
var data = factory.GetAllFixtures().Take(20);
int rowIndex = 3;
foreach (var fixture in data)
{
var pcRate = fixture.PCRate;
var account = fixture.Charter != null ? fixture.Charter.Shortname : null;
var region = fixture.Region != null ? fixture.Region.GroupName : null;
//CreateContentRow is exactly like the tutorial linked above.
var row = CreateContetRow(rowIndex, region, pcRate, account);
rowIndex++;
sheetData.AppendChild(row);
}
//Tried to add myWorkbook.WorkbookPart.Workbook.Save(); here, but it doesn't do anything
myWorkbook.Close();
}
Well, I managed to figure this out by myself after a short while.
Posting the answer here in case it will help someone (including myself):
In the line above myWorkbook.Close(); add worksheetPart.Worksheet.Save();
As simple as that...
I'm trying to add comments to cells in an Excel 2007 spreadsheet. I'm using the OpenXml SDK 2.0 to do so.
My use case is this:
I've created a template Excel file that I copy and use that as my starting point, rather than create an OpenXML document from scratch. My template file has a comment in cell A1 so that Excel has already created a WorksheetCommentPart for me.
Now my problem is that when I add Comment nodes to the Comments part the spreadsheet doesn't load and Excel asks if I want to recover.
What really bothers me is that my original comment in A1 is still there, but any comments I added programmatically are gone!
Here's the code I'm working with:
using (MemoryStream spreadsheetStream = new MemoryStream())
{
GetGradebookSpreadsheetTemplate(spreadsheetStream);
using (SpreadsheetDocument spDoc = SpreadsheetDocument.Open(spreadsheetStream, true))
{
WorkbookPart wbPart = spDoc.WorkbookPart;
WorksheetPart wsPart = wbPart.WorksheetParts.First();
SheetData sheet = wsPart.Worksheet.GetFirstChild<SheetData>();
Comments comments = wsPart.WorksheetCommentsPart.Comments;
comments.Descendants<Author>().First().Text = string.Format("{0}, {1}", instructor.LastName, instructor.FirstName);
comments.Descendants<Text>().First().Text = string.Format("{0}, {1}", instructor.LastName, instructor.FirstName);
List<DefinedName> definedNames = new List<DefinedName>();
definedNames.Add(CreateDefinedName("COLWeb_Gradebook", sheet.NamespaceURI, "Gradebook", "1", "A"));
uint index = 4;
foreach (User u in users)
CreateUserDataRow(index++, definedNames, comments.CommentList, sheet, u, coursesForUsers[u], assignments, submissions[u]);
Cell lastCell = sheet.Descendants<Cell>().Last();
OpenXmlElement dimensionsElement = wsPart.Worksheet.Elements().Where(x => x.LocalName == "dimension").First();
dimensionsElement.SetAttribute(new OpenXmlAttribute("ref", null, "A1:" + lastCell.CellReference));
comments.Save();
wsPart.Worksheet.Save();
wbPart.Workbook.Save();
}
return spreadsheetStream.ToArray();
}
And "CreateUserDataRow" creates a new row, but the relevant part is (where "comment" is my comment string and "c" is my Cell that I want to create the comment about):
if (!string.IsNullOrEmpty(comment))
{
List<OpenXmlElement> runs = new List<OpenXmlElement>();
foreach (string row in comment.Split(new string[] { "<p>", "</p>" }, StringSplitOptions.RemoveEmptyEntries))
{
string trimmed = row.Trim();
if (!string.IsNullOrEmpty(trimmed))
{
string escaped = System.Security.SecurityElement.Escape(trimmed);
runs.Add(new Run(new RunProperties(), new Text(escaped)));
}
}
Comment commentCell = new Comment();
commentCell.Reference = c.CellReference;
commentCell.AuthorId = 0;
commentCell.AppendChild(new CommentText(runs));
comments.AppendChild(commentCell);
}
Now as far as my eye can see, and KDiff3 for that matter, my files are pretty much identical to the files that would be output if I were to open Excel and put the comments into the cells by hand in Excel.
Does anyone have a good example of attaching a comment to a cell with OpenXml? Is there something I should know about maybe a relationship? Does it have something to do with using an Excel file that I created and then I'm using as a template (maybe some dimensions aren't set)?
Thanks for any help I can get.
Unfortunately, it is not that simple.
Cell comments also have a graphics object which is in a VML drawing part. VML is a cryptic legacy specification and is not in the approved ECMA standard. You can find documentation on it in Microsoft's Open XML documents, but it is not pretty. Hopefully Microsoft will address this in Excel 14 by adding full cell comment support as well as support for controls which are also written to VML.
Having said that, I have not used the Open XML SDK and I cannot say whether or not it is possible to add comments with it. I just thought this might help get you pointed in the right direction.
There appears to be a code sample at http://openxmldeveloper.org/forums/thread/7396.aspx which shows how to create the requisite VML Drawing. I'm trying this code now, and seem to have progressed, though I'm still getting some sort of unspecified error in which Excel decides to drop the comments before opening...