I am creating an Excel spreadsheet (XLSX) from an existing template and adding a dataset to a table on one of the worksheets.
I have been able to programmatically set the active sheet, but I can't set where the scroll position of the sheet is so that the last rows of data are displayed when someone opens the spreadsheet in Excel.
I'm adding 5000+ rows but when someone opens the file in Excel, the scroll position is where it was when the original template was saved. The cell I set to selected is indeed selected but it's off screen.
Here's my code:
using (var workbook = new XLWorkbook(fileInfo.FullName))
{
workbook.Properties.Author = _configuration.GetSection("ExcelAuthor").Value;
var workSheet = workbook.Worksheet(_configuration.GetSection("ExcelWorkSheetName").Value);
var table = workSheet.Table(_configuration.GetSection("ExcelTableName").Value);
var range = table.ReplaceData(data.Tables[0]);
//Set the sheet as the active sheet
foreach (var sheet in workbook.Worksheets)
{
var isCurrentSheet = sheet.Equals(workSheet);
sheet.SetTabActive(isCurrentSheet);
sheet.SetTabSelected(isCurrentSheet);
}
//workSheet.ActiveCell = range.LastCell();
workSheet.Cell(range.LastCell().Address).Select();
//range.LastCell().Select();
//range.LastCell().SetActive();
path = string.Format("{0} - {1}.xlsx", _configuration.GetSection("FileNamePrefix").Value, DateTime.Now.ToString("yyyy-MM-dd"));
path = string.Format("{0}\\{1}", _configuration.GetSection("OutputFolder").Value.TrimEnd(new[] { '/', '\\' }), path);
workbook.SaveAs(path);
};
I submitted a pull request and this is fixed in https://github.com/ClosedXML/ClosedXML/pull/1561
I'm not sure that it's possible to set the exact scroll position, but you can set the currently selected cell.
var wb = new XLWorkbook();
var wsActiveCell = wb.AddWorksheet("Set Active Cell");
wsActiveCell.Cell("B2000").SetActive();
Reference: https://github.com/ClosedXML/ClosedXML/wiki/Selecting-Cells-and-Ranges
Related
I'm using NPOI and C# and cannot seem to get AutoSizeColumn to size the columns appropriately. I've followed the suggestions I can find, set the font up front in the styles, added content after the styles are applied, auto-sized the columns before write (after all data is added) and still the columns are too narrow. Below is a demonstration of the problem:
public void ShowBug()
{
IWorkbook workbook;
workbook = new XSSFWorkbook();
// Create a base font
IFont boldFont = workbook.CreateFont();
boldFont.FontHeightInPoints = 11;
boldFont.FontName = "Calibri";
boldFont.Boldweight = (short)FontBoldWeight.Bold;
// Create a base style
ICellStyle boldStyle = workbook.CreateCellStyle();
boldStyle.SetFont(boldFont);
// Create a simple cell style using the base style
ICellStyle simpleStyle = workbook.CreateCellStyle();
simpleStyle.CloneStyleFrom(boldStyle);
// Create a sheet in the workbook
ISheet excelSheet = workbook.CreateSheet("Demo");
// Create a single row
IRow row = excelSheet.CreateRow(0);
// Create a single cell inside the row
ICell cell = row.CreateCell(0);
cell.SetCellType(CellType.String);
cell.CellStyle = simpleStyle;
cell.SetCellValue("This is an icredibly long text value for this column - and apparently too long");
// Autosize the column and create the file, after the output is produced
using (var fs = new FileStream("BugReport.xlsx", FileMode.Create, FileAccess.Write))
{
excelSheet.AutoSizeColumn(0, true);
workbook.Write(fs);
}
}
This code shows up in Excel like this:
Result of Code
So, what am I missing? What am I doing wrong?
I am using this approach to auto size the column header
XSSFSheet sheet = (XSSFSheet)workbook.CreateSheet("Demo");
sheet.AutoSizeColumn(“header1”); // Remove Boolean value which you are passing
I'm trying to verify that a cell in a row is not null. If it is null, I want to change the background colour of the cell to red. After reading how to do that, I have come up with the following code:
public int verifyImportFile(FileUpload fup)
{
int status = 0;
//check if there is actually a file being uploaded
if (fup.HasFile)
{
//load the uploaded file into the memorystream
using (MemoryStream stream = new MemoryStream(fup.FileBytes))
//Lets the server know to use the excel package
using (ExcelPackage xlPackage = new ExcelPackage(stream))
{
//Gets the first worksheet in the workbook
ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets[1];
//Gets the row count
var rowCnt = worksheet.Dimension.End.Row;
//Gets the column count
var colCnt = worksheet.Dimension.End.Column;
//Beginning the loop for data gathering
for (int i = 2; i < rowCnt; i++) //Starts on 2 because excel starts at 1, and line 1 is headers
{
//If there is no value in column 3, proceed
if (worksheet.Cells[i, 3].Value == null)
{
worksheet.Cells[i, 3].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[i,3].Style.Fill.BackgroundColor.SetColor(Color.Red);
status = 1;
}
}
xlPackage.Save();
}
}
return status;
}
What I do know from testing is that if a null value is found, it enters the if statement that checks for nulls. It seems to be running the code to change the background colour. After it loops through the entire excel sheet, the variable status does change to 1 and is displayed in a popup.
From my understanding of how to do this, it is running properly but the background colour stays white.
Your code is correct as far as setting the background color assuming it is being hit which have confirmed.
But how are you actually saving out the file? Once you load to a MemoryStream the connection to the original byte array is severed. You need to do a SaveAs() or GetAsByteArray() call like this:
xlPackage.SaveAs(new FileInfo(#"c:\temp\myFile.xls"));
Calling Save() just writes to the MemoryStream.
Hopefully this works.
worksheet.Cells[i, 3].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red)
Alrighty guys I have rather a brain mangler for you; I'm trying develop a relatively simple add-in for excel that should read in data from Sheet A in a workbook, create a new or update sheet B to contain a simplified version of said data in sheet A at the press of a button. Below is some example code I'm working on:
Application.SendKeys("{ENTER}"); // Exit edit mode
Excel.Workbook wb = this.Application.ActiveWorkbook;
Excel.Worksheet sheetA = null;
Excel.Worksheet sheetB = null;
foreach (Excel.Worksheet sheet in wb.Worksheets) {
// Assume origin sheet we want to move from is same name as book name
if (sheet.Name == wb.Name)
sheetA = sheet;
// Sheet to move to will be called review. Clean if it exists.
else if (sheet.Name == "Review")
{
sheetB = sheet;
sheetB.Cells.ClearContents();
}
}
// If origin sheet cannot be found, assume it's the first sheet
if (sheetA == null)
sheetA = (Excel.Worksheet)wb.Worksheets[1];
// Add the review sheet after the origin sheet if it doesn't exist
if (sheetB == null)
{
sheetB = wb.Worksheets.Add(After: sheetA);
sheetB.Name = "Review";
}
// Simply copy across the value of the first cell
sheetB.Range["A1"].Value2 = sheetA.Range["A1"].Value2;
Now the outcomes of this code seem to be radically different depending on whether anything is in "edit mode" (cells are being edited) or not. If not, all is well as you'd expect, a new sheet is created in the correct position and the cell populated.
If a cell is being edited though, the edited cell is moved to another worksheet in the workbook. If there is no other sheet to move to a COMException with HRESULT: 0x800A03EC is thrown (error unknown).
This error shows up A LOT and it's really frustrating with it essentially telling you "be damned if I know", so any ideas would be appreciated. The most common thing seems to be "worksheet doesn't exist" which would be the case here, but I can't tell why it wants to move the edited cell in the first place?
Solution found. Simulating a return key stroke (first line of my example) does exit edit mode, but a delay is required for the application to process it. My implementation worked out as:
Application.SendKeys("{ENTER}"); // Exit edit mode
Timer timer = new Timer(100); // 100ms delay
timer.AutoReset = false; // Stop the timer looping and re-executing
timer.Elapsed += new ElapsedEventHandler((Sender, ent) =>
{
// Code you want to execute outside of edit mode
});
timer.Start(); // Start her up!
Hope that helps some lost wandering soul!
I'm trying to create a simple example with #officewriter #excelwriter and having trouble using the styles. Not sure why.
ExcelApplication XLAPP = new ExcelApplication();
Workbook WB = XLAPP.Create();
Worksheet WKST = WB.Worksheets[0];
DataSet ds = <...some valid dataset with 2 columns....>
DataView dv = ds.Tables[0].DefaultView;
DataImportProperties props = WB.CreateDataImportProperties();
SoftArtisans.OfficeWriter.ExcelWriter.Style dataStyle = WB.CreateStyle();
props.UseColumnNames = true;
dataStyle.BackgroundColor = Color.SystemColor.Red;
dataStyle.CellLocked = true;
Area importArea = WKST.ImportData(dv, WKST.Cells[0, 0],props);
importArea.ApplyStyle(dataStyle);
XLAPP.Save(WB, Page.Response, "Output.xls", false);
Here's the problem: the color style works in the output but the CellLocked style does not. Why?
Thanks for any help. Frustrated - I thought this was a simple example!
ExcelWriter is designed to mimic Excel, so usually the steps that you need to take in Excel to active certain properties are the same steps that you need to take in ExcelWriter.
In Excel, all the cells in a worksheet have the locked style property set, but that property will not take effect until the worksheet is protected. (Right click on a cell and go to Format Cells > Protection - the Locked property is checked, but the cells are not locked until you go to Review > Protect Sheet).
Similarly, in ExcelWriter, all the cells in Worksheet.Cells have the Style.CellLocked set to true by default, but that property will not take effect until Worksheet.Protect() is called. Once you protect the sheet, the cells should be locked.
ExcelApplication xlapp = new ExcelApplication();
Workbook wb = xlapp.Create(ExcelApplication.FileFormat.Xlsx);
Worksheet ws = wb.Worksheets["Sheet1"];
//Currently, all the cells have default "locked" style set to true
//Protecting the worksheet will activate this property
ws.Protect("MyPassword");
xlapp.Save(wb, "ProtectedWorksheet.xlsx");
Since all the cells default to Locked, if you want to lock only an area of cells and not the entire worksheet, you will need to set Style.CellLocked to false on any cells that you want to leave unlocked. When the worksheet is protected, these cells will remain editable.
ExcelApplication xlapp = new ExcelApplication();
Workbook wb = xlapp.Open("MyWorkbook.xlsx");
Worksheet ws = wb.Worksheets[0];
//Create a style that will leave certain cells unlocked
//when the worksheet is protected
Style unlockStyle = wb.CreateStyle();
unlockStyle.CellLocked = false;
//Select the area that will be left unprotected
//Apply the style to that area
Area unlockedArea = ws.PopulatedCells;
unlockedArea.ApplyStyle(unlockStyle);
//Protect the worksheet
ws.Protect("MyPassword");
xlapp.Save(wb, "MyNewWorkbook.xlsx");
For more about protecting worksheets, we have a guide in our documentation: Protecting Your Worksheet.
Note: I work for SoftArtisans, makers of OfficeWriter.
I wanted to add formulas to an Excel workSheet.
I managed to do so with the Formula property.
The problem is that when I open the worksheet in Excel, I can see that the formula works - but I can only see the result in the cell. I can't see the formula that was calculated in the Formula Bar at the top of Excel.
Obviously if I enter a formula in Excel itself I can see the result in the cell and the formula in the Formula Bar.
Some of my code:
for (int i = 0; i < nOfColumns / 3; i++)
{
Range cells = workSheet.Range[workSheet.Cells[2, i * 3 + 3], workSheet.Cells[lastRowNumber, i * 3 + 3]];
cells.FormulaR1C1 = "=IF(EXACT(RC[-2],RC[-1]),TRUE,ABS(RC[-2]/RC[-1]-1))";
}
below is a test code. even after I save the workbook - the FormulaHidden is false and I can successfully retrieve the formula insterted. really frustrated
Microsoft.Office.Interop.Excel.Application excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Worksheet workSheet;
try
{
excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.DisplayAlerts = false;
workBooks = excelApp.Workbooks;
workBook = workBooks.Open(filePath, AddToMru: false);
workSheet = workBook.Worksheets.get_Item(1);
int nOfColumns = workSheet.UsedRange.Columns.Count;
int lastRowNumber = workSheet.UsedRange.Rows.Count;
Range rng = workSheet.Range["C1"];
rng.Formula = "=SUM(B2:B4)";
String formula = rng.Formula; //retrieve the formula successfully
rng.FormulaHidden = false;
workSheet.Unprotect();
workBook.SaveAs(filePath, AccessMode: XlSaveAsAccessMode.xlExclusive);
formula = rng.Formula; //retrieve the formula successfully
bool hidden = rng.FormulaHidden;
}
catch (Exception e)
{
throw;
}
finally
{
if (workBook != null)
{
workBook.Close();
workBook = null;
}
if (workBooks != null)
{
workBooks.Close();
workBooks = null;
}
if (excelApp != null)
{
excelApp.Quit();
excelApp = null;
}
}
}
Anyone know how to make the formula shown, when adding the formulas programatically ?
finally !!! figured it out. this behavior is caused by the SaveAs flags.
changed
workBook.SaveAs(filePath, AccessMode: XlSaveAsAccessMode.xlExclusive);
to
workBook.SaveAs(filePath, AccessMode: XlSaveAsAccessMode.xlShared);
now the only thing left is to understand what exactly is the different between the two flags. :)
Hiding the formula (by checking Hidden checkbox on Format Cells dialog) & protecting the worksheet (thereafter) will cause the formula to not show in the formula bar.
Example VBA code
Range("C1").FormulaHidden = True 'set this property to false to make formula visible.
Sheet1.Protect
EDIT: In order to see the formula in the formula bar
Range("C1").FormulaHidden = False
Sheet1.Unprotect
Go to the Formula tab on the tool bar, and click "Show Formulas".
I think localization could be involved in this weird behaviour.
Some time ago, working in Excel, I had the impression that formulas got stored in localized language (I was using italian), then undergo a conversion when compiled. This could make sense, because localized constants are an essential part of the spreadsheet data.
I'm sorry I haven't now Excel available, so I can't be more precise, but I think you could try to localize to english your spreadsheet, or set the formula text in your local language.