ClosedXML save workbook to prompt for readonly open - c#

Using ClosedXML (v0.93.1) on .Net Core 2.0, attempting to save a workbook so that when opened it prompts the user to open as "Read Only" or enter the protected password. (See linked question below).
I have seen IXLWorksheet.Protect() and XLWorkbook.Protect() - these correctly protect the workbook but still default the user to open with R/W access. This is a problem because it write locks the file and my process can't overwrite it until the user closes excel.
This question indicates OpenXML (the underlying API for ClosedXML) has the ability to set this when saving a workbook, however ClosedXML XLWorkbook.SaveAs(SaveOptions) doesn't include this. Perhaps I can't find the closedxml documentation on this option, but I believe this is the option I need.
According to this answer and the attached github/MSDN links OpenXML doesn't support password protecting but I don't see any info about readonly open other than the first linked question saying it should be possible.
Code example:
XLWorkbook wb = new XLWorkbook();
var ws = wb.AddWorksheet("My Sheet");
// ... add to worksheet
ws.Protect("my-worksheet-password");
wb.SaveAs("C:\\my-workbook.xlsx", new SaveOptions { /* In OpenXML the option would exist during this save step. */ });

This feature is not supported by ClosedXML yet, but you can use OpenXml directly to set the require property. Refer to https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.filesharing.readonlyrecommended?view=openxml-2.8.1
private void GeneratePartContent(WorkbookPart part)
{
FileSharing fileSharing1 = new FileSharing(){ ReadOnlyRecommended = true };
workbook1.Append(fileSharing1);
part.Workbook = workbook1;
}

Related

C# Word Interop - Open a .pdf without the conversion prompt

I am using the Word Interop and wish to open a .pdf file and let word automatically do the conversion.
I have the following properties set.
var wordApp = new Word.Application();
wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
wordApp.Options.DoNotPromptForConvert = true;
wordApp.Options.ConfirmConversions = false;
var doc = wordApp.Documents.OpenNoRepairDialog(ExternalFilePath, false, true);
However when it does the Open I still get the following prompt:
Word version 2016.
Does anyone know how to by-pass this? If I click OK the rest of the program executes as I'd expect successfully.
I am aware of and do not wish to use any other third-party tools.
Thanks to BugFinder for the suggestion of using the Format parameter, but still no luck with this.
Here are the additions to the code:
Word.FileConverters converters = wordApp.FileConverters;
var wordPdfConverter = converters.OfType<Word.FileConverter>().Where(c => c.CanOpen == true && c.Extensions == "pdf").First();
var doc = wordApp.Documents.OpenNoRepairDialog(ExternalFilePath, false, true, false, Format: wordPdfConverter.OpenFormat, NoEncodingDialog: true);
This gets a converter but still displays the prompt. :(
Further edit thanks to Cidney's comments. It appear this prompt is a user registry setting as that to bypass it in the interop is impossible. Seems a like a flaw with the Interop to me. The Open XML SDK does not support saving as XPS so this is unavailable too. Appears I will have to manipulate the users registry first to achieve this (brings write permission concerns) or check if Word is waiting for user input and send keys.

Adding a table to an existing excel file with spreadsheetgear

I use Spreadsheetgear to export the results of custom SQL queries as excel files.
Now I want to improve this system: The user will be able to upload an excel template file into the database (currently as varbinary). For example, it could have one worksheet with calculations, then when exporting data into that template it'll fill a different worksheet with the datatable from the query.
Can spreadsheetgear do this? If so, how does it work - mainly how can I load an existing excel file as a Spreadsheetgear workbook/workbookset? I could not find anything in their documentation (though I am still looking).
Edit: Solved.
I create the workbook manually, load the template from the database as a byte[], then open said template with the OpenFromMemory function:
// Create workbookSet
SpreadsheetGear.IWorkbookSet workbookSet = SpreadsheetGear.Factory.GetWorkbookSet();
// Create a new empty workbook in the workbookSet.
SpreadsheetGear.IWorkbook workbook = workbookSet.Workbooks.Add();
if(TemplateID != -1) // If this case requires a template
{
// Get template from SQL database (.xlsx stored as varbinary(max))
byte[] template = GetTemplateByID(VorlagenID);
workbook = workbookSet.Workbooks.OpenFromMemory(template);
}
// Create export worksheet
SpreadsheetGear.IWorksheet worksheet = workbook.Worksheets[0];
worksheet.Name = "Export";
[...]
Templates always use the Worksheet[1] in my case, but it should be easy to create a Worksheet[1] for the export.
Yes it is possible to set up pre-constructed template files using SpreadsheetGear. We use this extensively using .NET / C# / MSSSQL. The method allows you to create quite sophisticated templates and then simply add the required data. This of course includes any calculations you build into the template.
Method 1 - Store the template on a webserver, extract and write the created user spreadsheet to a folder on the web server. Return the filename to allow extraction by code or by the user from the server.
public static String SaveTemplateSpreadsheetToServer()
{
// Open the workbook.
var templatename = HostingEnvironment.MapPath("~/Files/MyTemplate.xlsx");
var workbook = Factory.GetWorkbook(templatename );
// Read and write to the spreadsheet
// Save a copy to disk and return filename
var filename = "The_exported_file.xlsx";
var filePath = HostingEnvironment.MapPath("~/FilesTemp/" + filename);
workbook.SaveAs(filePath, FileFormat.OpenXMLWorkbook);
// close workbook
workbook.Close();
// Return the filename
return fileName;
}
Method 2: Store the template on a webserver, extract and save modified spreadsheet as a byte array. Download directly an attachment
public static byte[] SaveTemplateSpreadsheetToServer()
{
// Open the workbook.
var templatename = HostingEnvironment.MapPath("~/Files/MyTemplate.xlsx");
var workbook = Factory.GetWorkbook(templatename );
// Read and write to the spreadsheet
// Save as byte array and send to user
var byteArray = workbook.SaveToMemory(FileFormat.OpenXMLWorkbook);
// close workbook
workbook.Close();
// Return the byte array
return byteArray;
}
We have done some work with binary template files saved in a database but find it more convenient to work with physical template files on a web server. It is easier to manage changes to the template.
My only caution is to avoid working with very big templates that have lots of "junk" in them (e.g. images). The process becomes affected by the time it takes to load the file into memory prior to the read / write / export activity. Less than 1MB is ideal and less than 2MB is manageable.

ClosedXML Add WorkSheet with Conditional Formatting

I am using ClosedXML to add new worksheet to existing Excel document.
It works fine for normal Excel document.
But if a excel document sheet contains conditional formatting on some cell then it throws error
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at ClosedXML.Excel.XLCFConverters.Convert(IXLConditionalFormat conditionalFormat, Int32 priority, SaveContext context)
at ClosedXML.Excel.XLWorkbook.GenerateWorksheetPartContent(WorksheetPart worksheetPart, XLWorksheet xlWorksheet, SaveContext context)
at ClosedXML.Excel.XLWorkbook.CreateParts(SpreadsheetDocument document)
at ClosedXML.Excel.XLWorkbook.CreatePackage(String filePath, SpreadsheetDocumentType spreadsheetDocumentType)
at ClosedXML.Excel.XLWorkbook.SaveAs(String file)
Below is the sample code
using (var excelDoc = new ClosedXML.Excel.XLWorkbook(strFilePath))
{
excelDoc.Worksheets.Add("New Result Sheet");
excelDoc.SaveAs(strFilePathSave);
}
Please help how to fix this issue.
XlsIO is a .NET library that reads and writes Excel 2003/2007/2010/2013/2016 files. Using XlsIO, you can add/modify a worksheet with conditional formatting very easily without issues. The whole suite of controls is available for free (commercial applications also) through the community license program if you qualify. The community license is the full product with no limitations or watermarks.
Step 1: Create a console application
Step 2: Add reference to Syncfusion.XlsIO.Base and Syncfusion.Compression.Base, you can add these reference to your project using NuGet also.
Step 3: Copy & paste the following code snippet.
The following code snippet illustrates how to add a worksheet with conditional formatting using XlsIO
using (ExcelEngine excelEngine = new ExcelEngine())
{
//Instantiate the excel application object.
IApplication application = excelEngine.Excel;
application.DefaultVersion = ExcelVersion.Excel2013;
//Open the workbook
IWorkbook workbook = application.Workbooks.Open("Input.xlsx");
(workbook.Worksheets as WorksheetsCollection).Add("NewSheet");
IWorksheet worksheet = workbook.Worksheets[1];
IConditionalFormats condition = worksheet.Range["A1"].ConditionalFormats;
IConditionalFormat condition1 = condition.AddCondition();
condition1.FormatType = ExcelCFType.CellValue;
condition1.Operator = ExcelComparisonOperator.Between;
condition1.FirstFormula = "10";
condition1.SecondFormula = "20";
condition1.BackColor = ExcelKnownColors.Red;
worksheet.Range["A1"].Number = 13;
//Save the workbook
workbook.SaveAs("AddedWorkbook.xlsx");
}
Kindly refer the sample to achieve this scenario and the sample can be downloaded from following location.
Download Demo
For further information about XlsIO, please refer our help documentation
Note: I work for Syncfusion

C# Excel Interop - Suppress 'Publishing' dialog when invoking Worksheet.ExportAsFixedFormat

I am using Excel Interop to open an xlsx file and save that as a pdf document. Upon invoking the 'ExportAsFixedFileFormat' method a dialog titled "Publishing" is displayed to indicate the progress. How can I suppress or hide this dialog? I have seen a few similar questions on other forums without a satisfying solution, but hopefully someone has solved this since then.
Code:
Application application = new Application();
application.DisplayAlerts = false; // <- No effect
application.Visible = false; // <- No effect
application.ScreenUpdating = false; // <- No effect
application.UserControl = false; // <- No effect
application.Workbooks.Open(path, Type.Missing, true);
application.DisplayDocumentActionTaskPane = false; // <- No effect
application.Worksheets[1].ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, path);
Perhaps I am not answering your question, but I'll try to be helpful. As far as I know, it's not recommended to use Excel Interop in back-end processing. You encountered one of its issues, but there are some others. There are 3rd party alternatives for interacting with Excel formats, some of them are open source and some are commercial. I would strongly recommend taking a look at one of them:
ClosedXML and NPOI. Both are open source and free.
How about processing it on a BackgroundWorker. This will not throw COMEXCEPTION.
I tried this code on a button click and it works without displaying the progress dialog.
var worker = new BackgroundWorker();
worker.DoWork += (o, args) =>
{
var path = #"D:\sample.xlsx";
var application = new Microsoft.Office.Interop.Excel.Application();
application.Workbooks.Open(path,
Type.Missing, true);
application.Worksheets[1].ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, path);
};
worker.RunWorkerAsync();
You can use pedamorf library ,pedamorf is a server that converts documents, images, urls, html and text to PDF. It relies heavily upon the excellent open-source programs wkhtml2pdf, Libre Office and iTextSharp. A client library is provided and can be used from a .NET application to utilize the conversion services provided by pedamorf.
More info is here
By far the better API to handle (create, read, export to PDF/html, etc...) XLS/XLSX files that I know is FlexCel. It's really simple, incredibly fast and full writen in C#. Don't require Excel instalation.
You'll find it here : http://www.tmssoftware.com/site/flexcelnet.asp (get the trial version with a lot of examples with source code)
If you have budget in your project to acquire the licence, I realy reccomend.

Does ClosedXML support setting a worksheet's zoom level?

I am using closedXML to generate an Excel file from C# and I am trying to see if there is anyway to set the zoom level of a worksheet. I can't find this in any of the documentation or from googling?
Does anybody know how to do this?
It's now possible, starting with ClosedXML version 0.87.0, via the IXLSheetView.ZoomScale property.
using Excel = ClosedXML.Excel;
var wb = new Excel.XLWorkbook();
Excel.IXLWorksheet ws = wb.AddWorksheet("zoom");
Excel.IXLSheetView view = ws.SheetView;
/* Value can be set between 10 and 400 */
view.ZoomScale = 85;
You can check the IXLSheetView source code for more information.
Update for version 0.87+: https://stackoverflow.com/a/52755386/2610249
No, ClosedXML does not support setting the zoom. The option that johny links to is only for scaling of the pages when printing.
There is a feature request on the ClosedXML page, but no answer from the developer.
As previously answered, you can't, but I've found a way around it which works well:
Create a template Excel file in advance, with all sheets' zoom levels set to how you want them.
When you create your workbook, instead of:
public XLWorkbook CreateWorkbook()
{
XLWorkbook workbook = new XLWorkbook();
IXLWorksheet worksheet = workbook.AddWorksheet("First sheet");
// ...
return workbook;
}
do this:
public XLWorkbook CreateWorkbookWithZoom()
{
XLWorkbook workbook = new XLWorkbook(#"C:\your template file.xlsx");
IXLWorksheet worksheet = workbook.Worksheet(1);
worksheet.Name = "First sheet";
// ...
return workbook;
}
where C:\your template file.xlsx is the path of your template file.
I think you can also handle having a variable number of sheets, by copying existing (blank) worksheets instead of creating new ones. You can get creative with having different blank template worksheets to choose from, if you need to set the zoom level dynamically.
A pull request for this has been logged at https://github.com/ClosedXML/ClosedXML/pull/180

Categories

Resources