Delete non-empty worksheets from excel workbook - c#

I want to delete some worksheets from an Excel workbook. When my program is loaded, it reads the sheets in the workbook, lists them in a gridview where the user can select which sheets should be in the output file. When the user hits the save button, I delete worksheets based on the selection and save the workbook. All that works. EXCEPT for when there is actually content in the worksheet. This will delete empty worksheets, but not worksheets with content.
foreach (var item in _view.Sheets)
{
Exc.Worksheet ws = wb.Worksheets[item.Name];
if (!item.Include)
{
ws.Delete();
}
}
Any clues?

try to turn off alerts:
app.DisplayAlerts = false;
foreach (var item in _view.Sheets)
{
Exc.Worksheet ws = wb.Worksheets[item.Name];
if (!item.Include)
{
ws.Delete();
}
}
app.DisplayAlerts = true;

Related

Update already opened Excel workbook C#

I have excel document that gets updated frequently from sensors and external data sources.
In C# i am opening the file and doing work with the data. What do i need to do to update my sheet variable without trying to reopen the file again.
void test(){
Application xlApp = new Application();
excelBook = xlApp.Workbooks.Open(filePath);
Worksheet sheet = excelBook.Worksheets[sheetName];
if (sheet != null){
var r = sheet.UsedRange.Value2;
while(true){
string json = JsonConvert.SerializeObject(r, Formatting.Indented);
//do work with the json data here
//........
//make sure we have latest updated sheet
//these are not working.
excelBook.RefreshAll();
excelBook.AutoUpdateSaveChanges = true;
}
}
}

C# Excel: Issue in copying worksheet from different workbook

I'm currently trying to copy a worksheet from a different workbook which i succeed by using Copy() and PasteSpecial(). However, I would like to know why the following code does not work even though many solutions online seems to use this approach.
Workbook currBook = Globals.ThisAddIn.GetActiveWorkbook();
Workbook copyBook = Globals.ThisAddIn.OpenWorkbook(Globals.ThisAddIn.Application.ActiveWorkbook.Path + #"\copyFile.xlsm", true, true);
//required worksheet
Worksheet copySheet = Globals.ThisAddIn.GetWorksheet(copyBook, "ToCopy");
copySheet.Copy(currBook.Worksheets[1]);
//close workbook
copyBook.Close();
Function used to get specific sheet:
public Excel.Worksheet GetWorksheet(Excel.Workbook book, string sheetName, bool create = false)
{
foreach (Excel.Worksheet sheet in book.Worksheets)
{
//worksheet with name found
if (sheet.Name == sheetName)
{
sheet.Activate();
return sheet;
}
}
//worksheet can't be found
if (create)
{
Excel.Worksheet sheet = book.Worksheets.Add();
sheet.Name = sheetName;
sheet.Activate();
return sheet;
}
return null;
}
There is no error from the stated code and the worksheet has been tested to exist. The program simply does not create a copy into currBook
Interestingly, I was just working on something else where this came up...
In order to specify a destination it's necessary to use Range.Copy, not Sheet.Copy:
copySheet.UsedRange.Copy(currBook.Worksheets[1].Range["A1"]);
If a destination can't be specified, then Excel puts the data in a new workbook.

Writing to Excel file when it's opened by another process

I'm wondering if it is possible to write to an Excel file using C#/EPPlus while I have the file open. I continue to get exceptions while trying to write using my program and I can't find anything online.
Here is the code I have to append to an existing worksheet which works fine when the spreadsheet isn't opened
public static void AppendExistingMailingWorkbook(string workSheet, string filePath, IList<MailingReportItem> reportData)
{
//create a fileinfo object of an excel file on the disk
FileInfo file = new FileInfo(filePath);
Object thisLock = new Object();
lock (thisLock)
{
//create a new Excel package from the file
using (ExcelPackage excelPackage = new ExcelPackage(file))
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[workSheet];
var rowToAppend = worksheet.Dimension.End.Row + 1;
for (int i = 0; i < reportData.Count; i++, rowToAppend++)
{
worksheet.Cells[rowToAppend, 1].Value = reportData[i].BatchDate.Date.ToString("MM/dd/yyyy");
worksheet.Cells[rowToAppend, 2].Value = reportData[i].BatchId;
worksheet.Cells[rowToAppend, 3].Value = reportData[i].FileName;
worksheet.Cells[rowToAppend, 4].Value = reportData[i].PageCount;
worksheet.Cells[rowToAppend, 5].Value = reportData[i].MailDate;
}
//save the changes
excelPackage.Save();
}
}
}
In Excel, set the workbook to be shared. From the office help:
Open workbook in Excel
Click Review > Share Workbook
On the Editing tab, select the Allow changes by more than one user ... check box.
On the Advanced tab, select the options that you want to use for tracking and updating changes, and then click OK.
If this is a new workbook, type a name in the File name box. Or, if this is an existing workbook, click OK to save the workbook.
If the workbook contains links to other workbooks or documents, verify the links and update any links that are broken.
Click File > Save.
When you're done, - Shared will appear at the top of the Excel window, next to the filename.
The file will then be opened non-exclusively, allowing others to edit it while Excel has it open.

OpenXML writer usage makes graphics unreadable

As I'm writing quite large xlsx files, I'm using OpenXmlReader and OpenXmlWriter as recommended on this page:
https://blogs.msdn.microsoft.com/brian_jones/2010/06/22/writing-large-excel-files-with-the-open-xml-sdk/
What I only do is change formulas inside existing cells and making sure that their value is discarded so that it is recalculated when Excel opens the file.
Here is the function that I'm using:
public void Save(Stream Input, Stream Output)
{
Input.Position = 0;
if (Input != Output)
Input.CopyTo(Output);
using (SpreadsheetDocument document = SpreadsheetDocument.Open(Output, true))
{
WorkbookPart wbPart = document.WorkbookPart;
// force recalculation as we change formulas
wbPart.Workbook.CalculationProperties.ForceFullCalculation = true;
wbPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
// store the worksheet parts in a separate list because the loop below
// adds and removes elements inside wbPart.WorksheetParts
List<WorksheetPart> originalWsParts = new List<WorksheetPart>();
foreach (WorksheetPart inputWsPart in wbPart.WorksheetParts)
originalWsParts.Add(inputWsPart);
// process all worksheets in the workbook
foreach (WorksheetPart inputWsPart in originalWsParts)
{
string origninalSheetId = wbPart.GetIdOfPart(inputWsPart);
WorksheetPart replacementWsPart = wbPart.AddNewPart<WorksheetPart>();
string replacementWsPartId = wbPart.GetIdOfPart(replacementWsPart);
OpenXmlReader reader = OpenXmlReader.Create(inputWsPart);
OpenXmlWriter writer = OpenXmlWriter.Create(replacementWsPart);
while (reader.Read())
{
logger.Debug(reader.ElementType.Name);
if (reader.ElementType == typeof(Cell) && reader.IsStartElement)
{
writer.WriteStartElement(reader);
// write the cell content, changing the formula and skipping the value
while (reader.Read() && !(reader.ElementType == typeof(Cell) && reader.IsEndElement))
{
if (reader.IsStartElement)
{
if (reader.ElementType == typeof(CellFormula))
{
CellFormula element = reader.LoadCurrentElement() as CellFormula;
element.Text = "SUM(1,2)";
element.CalculateCell = true;
writer.WriteElement(element);
}
else if (reader.ElementType != typeof(CellValue))
{
writer.WriteStartElement(reader);
string elementText = reader.GetText();
if (!String.IsNullOrEmpty(elementText))
writer.WriteString(elementText);
}
}
else if (reader.IsEndElement)
{
if (reader.ElementType != typeof(CellValue))
writer.WriteEndElement();
}
}
writer.WriteEndElement();
}
else
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
string elementText = reader.GetText();
if (!String.IsNullOrEmpty(elementText))
writer.WriteString(elementText);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
reader.Close();
writer.Close();
Sheet sheet = wbPart.Workbook.Descendants<Sheet>()
.Where(s => s.Id.Value.Equals(origninalSheetId)).First();
sheet.Id.Value = replacementWsPartId;
wbPart.DeletePart(inputWsPart);
}
}
}
It works quite well on the simplest workbooks, but it creates unreadable files when there are drawings inside sheets in the file.
For instance, if I have a drawings on Sheet1, when Excel opens the saved file, it complains that the file has unreadable parts and shows me the drawings in the list of things it has deleted.
I unzipped the xlsx file and compared the sheetX.xml files, and apart from the added x: prefix, they are the same.
Obviously, I'm missing something but reading the various docs I could find, nothing came to me. I believe there is a reference to the original worksheet part that has not been updated but I don't see any drawings descendant in the workbook.
Any help is most welcome.
Update
I looked more closely at the files content and there are two folders missing inside the xl folder: charts and drawings
So clearly, I'm missing code that would add those into the final archive, but I can't (yet) figure out what code this is.
The above code creates a new WorksheetPart inside the loop, when the intention was always to clone the input part.
I thus went looking for a way to clone a worksheet, but there is no ClonePart() method on WorkbookPart.
Fortunately for me, someone else already had this issue and Brian Jones did a part on his blog about this:
https://blogs.msdn.microsoft.com/brian_jones/2009/02/19/how-to-copy-a-worksheet-within-a-workbook/
So, I changed the start of the using statement to this:
using (SpreadsheetDocument document = SpreadsheetDocument.Open(Output, true), tmpDocument = SpreadsheetDocument.Create(new MemoryStream(), document.DocumentType))
{
WorkbookPart wbPart = document.WorkbookPart;
WorkbookPart tmpWbPart = tmpDocument.AddWorkbookPart();
Then instead of calling AddNewPart<WorksheetPart>, I now have the following code:
// can't directly clone, so add to the temporary workbook part and then back
// into the working workbook part
WorksheetPart tmpWsPart = tmpWbPart.AddPart(inputWsPart);
WorksheetPart replacementWsPart = wbPart.AddPart(tmpWsPart);
tmpWbPart.DeletePart(tmpWsPart);
tmpWsPart = null;
With those changes, I now have my Drawing part in the worksheet, the associated folders in the xlsx file.
As a result, Excel no longer complains when opening the file and the graphs are all there and updated.

How to set active sheet with Open XML SDK 2.5

using the example here How to Copy a Worksheet within a Workbook
I have successfully been able to clone/copy sheets in my excel file, however when I open the excel the 2nd sheet is the active(visible) sheet. I haven't been able to locate a property that could do thins.....Is there any way to specify what sheet is active?
I've tried to force it by opening and editing the first sheet in the file thinking it was the last edited sheet that was active but that didn't work either.
any help would be great. TIA
update: looking at the workbook.xml created when renaming the .xlsx to .zip I came accross the 'activeTab' property. made a quick change to my code and seems to work just fine
public void SetFirstSheetInFocus(String xlsxFile)
{
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(xlsxFile, true))
{
//Get a reference to access the main Workbook part, which contains all references
WorkbookPart _workbookPart = spreadSheet.WorkbookPart;
if (_workbookPart != null)
{
WorkbookView _workbookView = spreadSheet.WorkbookPart.Workbook.BookViews.ChildElements.First<WorkbookView>();
if (_workbookView != null)
{
_workbookView.ActiveTab = 0; // 0 for first or whatever tab you want to use
}
// Save the workbook.
_workbookPart.Workbook.Save();
}
}
}
If the name of your sheet is in the variable
sheetName
you can set the sheet with that name active like this:
using (var spreadsheetDoc = SpreadsheetDocument.Open(emptyHIPTemplatePath, true /* isEditable */, new OpenSettings { AutoSave = false }))
{
var workbookPart = spreadsheetDoc.WorkbookPart;
var workBook = spreadsheetDoc.WorkbookPart.Workbook;
var sheet = workBook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sheetName);
var sheetIndex = workBook.Descendants<Sheet>().ToList().IndexOf(sheet);
var workBookView = workBook.Descendants<WorkbookView>().First();
workBookView.ActiveTab = Convert.ToUInt32(sheetIndex);
...
workBook.Save();
}
From Vincent Tan's book:
The SheetId property doesn't determine the order. The order of
appending the Sheet classes to the Sheets class, does.
When you add a sheet, it gets the next index, but a single sheet does not have an index. OpenXML gives it an index when you are done adding sheets. Again, from Vincent Tan's book:
Let's say you have 3 worksheets named Sheet1, Sheet2 and Sheet3.
However, when you appended the corresponding Sheet classes, you did it
as Sheet2, Sheet3 and Sheet1, in that order.

Categories