I am trying to use OpenXML to programmatically set an Excel workbook to visible, but am fairly new to C# and am not sure how to do that.
We are using a 3rd party application that stores Office documents and I'm tasked with reporting. One of the many strange things it does is compresses documents before saving them and sets Excel Workbook visibility to false in the database record.
According to the documentation there is the function that should do that:
public DocumentFormat.OpenXml.EnumValue<DocumentFormat.OpenXml.Spreadsheet.VisibilityValues> Visibility { get; set; }
How do I translate that into a working C# example? I don't have much so far:
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
using (SpreadsheetDocument rptTemplate = SpreadsheetDocument.Open(mem, true))
{ // the next line doesn't work
rptTemplate.WorkbookPart.Workbook.WorkbookProperties.SetAttribute(VisibilityValues = visible)
rptTemplate.WorkbookPart.Workbook.Save();
rptTemplate.SaveAs(filePathName);
}
}
first of all you need to get WorkbookView of your workbook, so
var workBookView = workbookpart.Workbook.Descendants<WorkbookView>().FirstOrDefault();
and then you can set desired visibility :
workBookView.Visibility = VisibilityValues.Hidden;
if you are creating spreadsheet document you should create workBookView before you can use it,like below:
spreadsheetDocument.WorkbookPart.Workbook.Append(new BookViews(new WorkbookView()));
Related
I have a C# data processing application which uses EPPlus to write the final results into an excel sheet. The background color of the rows are changed based on what the data on that row signifies. Time was never an issue as I only dealt with files that were below <100MB before. However, as my requirements have changed and the files get larger, I have noticed that.. just coloring makes my application 60% slower. Removing coloring makes the application significantly faster. The snippet below is an example of the code which I use to color the data to make it visually distinguishing. I'm no expert at EPPlus but is there a way, this can be optimized to make my application faster? Or are there any better ways for me to make the rows visually distinct for the people who will end up looking at the data? Any help will be appreciated!
if (data[4] == "3")
{
// color the type 3 messages here
var fill1 = cell1.Style.Fill;
fill1.PatternType = ExcelFillStyle.Solid;
fill1.BackgroundColor.SetColor(Color.LightGray);
}
if (data[4] == "4")
{
var fill1 = cell1.Style.Fill;
fill1.PatternType = ExcelFillStyle.Solid;
fill1.BackgroundColor.SetColor(Color.BlanchedAlmond);
}
EDIT:
This is the code I use to copy the template and write the excel data into the new worksheet. p is an Excel Package which I convert to a byte Array before writing to the excel file.
Byte[] bin = p.GetAsByteArray();
File.Copy("C:\\Users\\mpas\\Desktop\\template.xlsx", "C:\\Users\\mpas\\Desktop\\result.xlsx");
using (FileStream fs = File.OpenWrite("C:\\Users\\mpas\\Desktop\\result.xlsx")) {
fs.Write(bin, 0, bin.Length);
}
Styling is much faster in EPPlus, and most Excel APIs, if you use named styles. Assign and use the style to cell in EPPlus like this ...
internal static string YourStyleName = "MyStyle";
ExcelNamedStyleXml yourStyle = excel.Workbook.Styles.CreateNamedStyle(YourStyleName);
yourStyle.Style.Font.Color.SetColor(Color.DarkRed);
yourStyle.Style.Fill.PatternType = ExcelFillStyle.Solid;
yourStyle.Style.Fill.BackgroundColor.SetColor(Color.LemonChiffon);
// ...
sheet.Cells[sourceRange].StyleName = YourStyleStyleName
Here is code to open an existing file.
FileInfo AddressList = new FileInfo("c:\test\test.xlsx");
// Open and read the XlSX file.
try
{
using (ExcelPackage package = new ExcelPackage(AddressList))
{
// Get the work book in the file
ExcelWorkbook workBook = package.Workbook;
if (workBook != null)
{
if (workBook.Worksheets.Count > 0)
{
// Get the first worksheet
//ExcelWorksheet Worksheet = workBook.Worksheets.First();
var worksheet = package.Workbook.Worksheets[1];
I'm using EPPlus in a web application with C#. I need to read an Excel file and check its format, I tried it doing the same as this article (How do I get partial cell styling in excel using EPpplus?), and actually all format properties were ok (bold, italic, color...), but the one that I really need is to check the strike
text property and it is always set to false.
Here is an answer just so the question doesnt hang out there:
[TestMethod]
public void Strike_Format_Test()
{
//http://stackoverflow.com/questions/30517646/how-to-apply-strike-formatting-using-epplus
var existingFile = new FileInfo(#"c:\temp\StrikeFormat.xlsx");
using (var pck = new ExcelPackage(existingFile))
{
var wb = pck.Workbook;
var ws = wb.Worksheets.First();
var cell = ws.Cells["A1"];
Console.WriteLine(cell.Style.Font.Strike);
}
}
I'm using CarlosAg ExcelXmlWriter library which generates XML Excel spreadsheets 2003 (*.xml)
I need to find coomercial or free tool just that converts this generated xml spreadsheet into
PDF. I tried SautinSoft library but it didn't work with my desired extension (xml) it only works with xlsx or xls extesnions
thanks guys in advance
Try Aspose.
http://www.aspose.com/categories/.net-components/aspose.cells-for-.net/default.aspx
You might also need the PDF component, not sure how they do it now.
Can you simply use some pdf printer to do it?
Try to use a free solution (EpPlus): https://github.com/EPPlusSoftware/EPPlus
Or SpreadSheet https://spreadsheetlight.com/
An another way:
static void ConvertFromStream()
{
// The conversion process will be done completely in memory.
string inpFile = #"..\..\..\example.xml";
string outFile = #"ResultStream.pdf";
byte[] inpData = File.ReadAllBytes(inpFile);
byte[] outData = null;
using (MemoryStream msInp = new MemoryStream(inpData))
{
// Load a document.
DocumentCore dc = DocumentCore.Load(msInp, new XMLLoadOptions());
// Save the document to PDF format.
using (MemoryStream outMs = new MemoryStream())
{
dc.Save(outMs, new PdfSaveOptions() );
outData = outMs.ToArray();
}
// Show the result for demonstration purposes.
if (outData != null)
{
File.WriteAllBytes(outFile, outData);
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(outFile) { UseShellExecute = true });
}
}
I can successfully inject a piece of VBA code into a generated excel workbook, but what I am trying to do is use the Workbook_Open() event so the VBA code executes when the file opens. I am adding the sub to the "ThisWorkbook" object in my xlsm template file. I then use the openxml productivity tool to reflect the code and get the encoded VBA data.
When the file is generated and I view the VBA, I see "ThisWorkbook" and "ThisWorkbook1" objects. My VBA is in "ThisWorkbook" object but the code never executes on open. If I move my VBA code to "ThisWorkbook1" and re-open the file, it works fine. Why is an extra "ThisWorkbook" created? Is it not possible to inject an excel spreadsheet with a Workbook_Open() sub? Here is a snippet of the C# code I am using:
private string partData = "..."; //base 64 encoded data from reflection code
//open workbook, myWorkbook
VbaProjectPart newPart = myWorkbook.WorkbookPart.AddNewPart<VbaProjectPart>("rId1");
System.IO.Stream data = GetBinaryDataStream(partData);
newPart.FeedData(data);
data.Close();
//save and close workbook
Anyone have ideas?
Based on my research there isn't a way to insert the project part data in a format that you can manipulate in C#. In the OpenXML format, the VBA project is still stored in a binary format. However, copying the VbaProjectPart from one Excel document into another should work. As a result, you'd have to determine what you wanted the project part to say in advance.
If you are OK with this, then you can add the following code to a template Excel file in the 'ThisWorkbook' Microsoft Excel Object, along with the appropriate Macro code:
Private Sub Workbook_Open()
Run "Module1.SomeMacroName()"
End Sub
To copy the VbaProjectPart object from one file to the other, you would use code like this:
public static void InsertVbaPart()
{
using(SpreadsheetDocument ssDoc = SpreadsheetDocument.Open("file1.xlsm", false))
{
WorkbookPart wbPart = ssDoc.WorkbookPart;
MemoryStream ms;
CopyStream(ssDoc.WorkbookPart.VbaProjectPart.GetStream(), ms);
using(SpreadsheetDocument ssDoc2 = SpreadsheetDocument.Open("file2.xlsm", true))
{
Stream stream = ssDoc2.WorkbookPart.VbaProjectPart.GetStream();
ms.WriteTo(stream);
}
}
}
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[short.MaxValue + 1];
while (true)
{
int read = input.Read(buffer, 0, buffer.Length);
if (read <= 0)
return;
output.Write(buffer, 0, read);
}
}
Hope that helps.
I found that the other answers still resulted in the duplicate "Worksheet" object. I used a similar solution to what #ZlotaMoneta said, but with a different syntax found here:
List<VbaProjectPart> newParts = new List<VbaProjectPart>();
using (var originalDocument = SpreadsheetDocument.Open("file1.xlsm"), false))
{
newParts = originalDocument.WorkbookPart.GetPartsOfType<VbaProjectPart>().ToList();
using (var document = SpreadsheetDocument.Open("file2.xlsm", true))
{
document.WorkbookPart.DeleteParts(document.WorkbookPart.GetPartsOfType<VbaProjectPart>());
foreach (var part in newParts)
{
VbaProjectPart vbaProjectPart = document.WorkbookPart.AddNewPart<VbaProjectPart>();
using (Stream data = part.GetStream())
{
vbaProjectPart.FeedData(data);
}
}
//Note this prevents the duplicate worksheet issue
spreadsheetDocument.WorkbookPart.Workbook.WorkbookProperties.CodeName = "ThisWorkbook";
}
}
You need to specify "codeName" attribute in the "xl/workbook..xml" object
After feeding the VbaProjectPart with macro. Add this code:
var workbookPr = spreadsheetDocument.WorkbookPart.Workbook.Descendants<WorkbookProperties>().FirstOrDefault();
workbookPr.CodeName = "ThisWorkBook";
After opening the file everything should work now.
So, to add macro you need to:
Change document type to macro enabled
Add VbaProjectPart and feed it with earlier created macro
Add workbookPr codeName attr in xl/workbook..xml with value "ThisWorkBook"
Save as with .xlsm ext.
I have a datatable with larger than 300.000 row, i want export this datatable to excel 2003 but don't use Excel COM. I have used NPOI but it raise error OutOfMemory. I'm finding a thirt party component (free) can export for each row and write directly to file to avoid out of memory. Who can help me? thanks.
How about a csv file ? It is readable from Excel.
Try this Libs. I am not sure about the capacity
http://ehl.codeplex.com/
http://excelexportlib.codeplex.com/
You can export to an excel-file with the use of a webgrid (only in code). Under the assumption that you're creating a WinForm-application, you will have to make a reference to the System.Web library.
// Create the DataGrid and perform the databinding
var myDataGrid = new System.Web.UI.WebControls.DataGrid();
myDataGrid.HeaderStyle.Font.Bold = true;
myDataGrid.DataSource = myDataTable;
myDataGrid.DataBind();
string myFile = "put the name here with full path.xls"
var myFileStream = new FileStream( myFile, FileMode.Create, FileAccess.ReadWrite );
// Render the DataGrid control to a file
using ( var myStreamWriter = new StreamWriter( myFileStream ) )
{
using (var myHtmlTextWriter = new System.Web.UI.HtmlTextWriter(myStreamWriter ))
{
myDataGrid .RenderControl( myHtmlTextWriter );
}
}
I haven't tested it with so many records, so I don't know the impact of this memory-wise.
Edit:
Take note of the comment of #Charles Williams that excel 2003 has a row limitation