I've defined an Excel template using Xml mappings that will generate the Excel report based on the Xml that I import.
I need to generate this report on the server so I can't use Microsoft Interopt. How can I do the following (C#) with an open source library?
Application excel = new Application();
Workbook workbook = excel.Workbooks.Open(Path.Combine(Directory.GetCurrentDirectory(), "TestTemplate.xlsx"));
var result = workbook.XmlMaps[1].Import(Path.Combine(Directory.GetCurrentDirectory(), "TestData.xml"), true);
workbook.Save();
workbook.Close();
excel.Workbooks.Close();
This allows me to do formatting of the Excel sheet on my own PC (with Office 365) and then save the template and publish with the project and just update the XML data and save as a new report.
I ended up going with ClosedXML's reporting plugin that has a variable replacement function that ended up working just as well.
The example from their website:
protected void Report()
{
const string outputFile = #".\Output\report.xlsx";
var template = new XLTemplate(#".\Templates\report.xlsx");
using (var db = new DbDemos())
{
var cust = db.customers.LoadWith(c => c.Orders).First();
template.AddVariable(cust);
template.Generate();
}
template.SaveAs(outputFile);
//Show report
Process.Start(new ProcessStartInfo(outputFile) { UseShellExecute = true });
}
Related
Okay, so i have an excel file (.xlsx) which will be downloaded when user clicks a button. The file is stored in a folder and will be processed by adding data validation and such before sending it to user. In my case i'm only adding dropdowns which will be filled from another sheet.
Here is my code that generates the file:
public IActionResult DownloadTemplateExcelFile(string type)
{
string fullFilePath = "../TemplateFiles/";
string fileName = "";
if (type != "")
{
fileName = type + ".xlsx";
fullFilePath += fileName;
}
var fileData = ExcelHelper.CreateExcelFile(type, fullFilePath);
return this.File(fileData, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",fileName);
}
And this code below is the file processing:
public static class ExcelHelper{
public static byte[] CreateExcelFile(string type, string fullFilePath){
using (var package = new ExcelPackage(new FileInfo(fullFilePath))){
// Getting list of values and set it for dropdown values
// List<string> dropdown1 = .. list string is loaded from database
ExcelWorksheet sheetDropdown1 = package.Workbook.Worksheets.Add("Dropdowns1");
var mainSheet= package.Workbook.Worksheets["Sheet1"];
sheetDropdown1.Cells["A1"].LoadFromCollection(dropdown1);
var dropdown1Addr = mainSheet.Cells[3,5,300,5].Address;
var dropdown1Formula = "='Dropdowns1'!$A:$A";
var validation = mainSheet.DataValidations.AddListValidation(dropdown1Addr);
validation.ShowErrorMessage = true;
validation.ErrorStyle = OfficeOpenXml.DataValidation.ExcelDataValidationWarningStyle.stop;
validation.ErrorTitle = "An invalid value was entered";
validation.Error = "Select a value from the list";
validation.AllowBlank = true;
validation.Formula.ExcelFormula = dropdown1Formula;
validation.Validate();
var excelFile = package.GetAsByteArray();
package.Dispose();
return excelFile;
}
}
}
When i opened the file in Excel 2010+ it worked just fine, the dropdown is loaded nicely and the cells in which the data validation is applied is working. However in excel 2007 when i tried to open it showed an error need to repair if i want to open it in 2007. If i do so however, the dropdown function is lost and unusable.
I've racked my brain but still haven't found any solution yet. How can i "Fix" this? I'm using EPPlus 6 for reference.
I'm moving some legacy code using Office Interop libraries to epplus, one thing I can't figure out is how to set a Workbook to ask the user on open the file to open it read only. Like if the user clicks on File -> Info -> Protect Workbook -> Always Open Read-Only
I've tried to set the DocSecurity property stated here (https://sno.phy.queensu.ca/~phil/exiftool/TagNames/OOXML.html), but to no success. excelWorkBook.Properties.SetExtendedPropertyValue("DocSecurity", "2");
I also tried to add the following node to the workbookxml <fileSharing readOnlyRecommended="1"/>
I even tried to compare the unzipped excel files protected, non protected, but there were too many changes.
It can be done but it is not really straightforward. Setting DocSecurity can be done by generating the Workbook.Properties object. But that is only half of it. You also need to set the flag inside the Workbook itself which can only be done via XML manipulation.
[TestMethod]
public void DocSecurity_Test()
{
//https://stackoverflow.com/questions/58335624/is-there-a-programmatically-way-for-excels-protect-workbook
var fi = new FileInfo(#"c:\temp\DocSecurity_Test.xlsx");
if (fi.Exists)
fi.Delete();
using (var package = new ExcelPackage(fi))
{
//Create a simple workbook
var workbook = package.Workbook;
var worksheet = workbook.Worksheets.Add("Sheet1");
worksheet.Cells["A1"].Value = "Test";
//Extended properties is a singleton so reference it to generate the app.xml file
//needed and add the security setting
var extProps = workbook.Properties;
extProps.SetExtendedPropertyValue("DocSecurity", "2");
//Also need to tell the workbook itself but can only do it via XML
var xml = workbook.WorkbookXml;
var att = xml.CreateAttribute("readOnlyRecommended");
att.Value = "1";
const string mainNs = #"http://schemas.openxmlformats.org/spreadsheetml/2006/main";
var fileSharing = xml.CreateElement("fileSharing", mainNs);
fileSharing.Attributes.Append(att);
//Element must be at the beginning of the tree
xml.DocumentElement.PrependChild(fileSharing);
package.Save();
}
}
Which will look like this:
I am using Microsoft.Office.Interop.Excel dll, and I want to create an excel sheet which gets data from database using a query, either in the form of pivot tables or in the form of Data Table.
I don't want to get data using my C# code and then insert into Excel Sheet. I want my Excel sheet that has been generated to have a connection directly to database using that query. Is it possible and how to do it?
Please help.
Okay, how about this?
using Microsoft.Office.Interop.Excel;
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var connection = #"OLEDB;Provider=SQLOLEDB.1;Integrated Security=SSPI;Data Source=MYDATASOURCE;Initial Catalog=AdventureWorks2017";
var command = "SELECT * FROM [Sales].[vSalesPersonSalesByFiscalYears]";
var xl = new Application
{
Visible = true
};
xl.Workbooks.Add();
var pivotCache = xl.ActiveWorkbook.PivotCaches().Add(XlPivotTableSourceType.xlExternal, null);
pivotCache.Connection = connection;
pivotCache.MaintainConnection = true;
pivotCache.CommandText = command;
pivotCache.CommandType = XlCmdType.xlCmdSql;
var sheet = (Worksheet)xl.ActiveSheet;
var pivotTables = (PivotTables)sheet.PivotTables();
var pivotTable = pivotTables.Add(pivotCache, xl.ActiveCell, "PivotTable1");
pivotTable.SmallGrid = false;
pivotTable.ShowTableStyleRowStripes = true;
pivotTable.TableStyle2 = "PivotStyleLight1";
PivotField pageField = (PivotField)pivotTable.PivotFields("SalesTerritory");
pageField.Orientation = XlPivotFieldOrientation.xlPageField;
PivotField rowField = (PivotField)pivotTable.PivotFields("FullName");
rowField.Orientation = XlPivotFieldOrientation.xlRowField;
pivotTable.AddDataField(pivotTable.PivotFields("2004"), "Sum of 2004", XlConsolidationFunction.xlSum);
}
}
}
For demonstration purposes, I used a console app, but adapt it to fit whatever way you're working with the code.
I don't pretend to have originated all this code, I adapted it from:
Creating a PivotTable programmatically
updating it for AdventureWorks2017 and Excel Interop.
I am using C# and Visual Studio 2017 and I am a bit new to it. I created a datagridview that was populated with a CSV file and I can create and import data from such files to my datagridview but I want to add a search option, either using the project number of project name. The way I currently have it set up by watching tutorial and reading is like this to import and populate the table:
using (OpenFileDialog importData = new OpenFileDialog() { Filter="CSV|*.csv", ValidateNames = true })
{
if (importData.ShowDialog() == DialogResult.OK)
{
var import = new StreamReader(new FileStream(importData.FileName, FileMode.Open));
var csv = new CsvReader(import);
cCIProjectsBindingSource.DataSource =csv.GetRecords<CCI_Projects>();
The reason I am doing it like this without using SQL is because it is for a small company that is currently working only with excel and dont want to pay for a database service just yet. If this is an inefficient way to do this please do let me know.
You must conduct the search line-by-line and using the Column you expect to have the desired value.
Something like:
try
{
foreach (MyLine as DataGridViewRow in __YourDataGridView.Rows)
{
CurrentValueN = MyLine.Cells(1).Value
// (or you can utilize named cells like)
CurrentValueN = MyLine.Cells("FirstColumn").Value
}
catch (exception ex)
{ }
It's important to mention that FIRSTCOLUMN is NOT the field name but the ColumnName in the Datagridview properties.
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