I am trying to truncate the cells in a column to a fixed number of characters using the DevExpress SpreadsheetControl. I have it working by just iterating through each cell and manually truncating it using the code below:
for (int i = 0; i <= rowCount; ++i)
{
worksheet.Columns[0][i].Value = worksheet.Columns[0][i].Value.ToString().Substring(0, 15);
}
But this is extremely slow. I was wondering if there is a way in the control to tell it to truncate the cells automatically?
EDIT
I found where I can export the sheet to a datatable and do the substring on it. This is much faster:
var dataTable = worksheet.CreateDataTable(worksheet.GetUsedRange(), true);
var exporter = worksheet.CreateDataTableExporter(worksheet.GetUsedRange(), dataTable, true);
exporter.Export();
for (int i = 0; i < dataTable.Rows.Count; ++i)
{
dataTable.Rows[i][0] = dataTable.Rows[i][0].ToString().Substring(0, 15);
}
worksheet.Import(dataTable, true, 0, 0);
I would still like to see if there is a better way built into the control or someplace else in the API.
Your code is extremely slow because SpreadsheetControl is trying to update its content with any change in any cell. So, you just need to lock updates before running your code and unlock its after. For lock/unlock updates you can use SpreadsheetControl.BeginUpdate and SpreadsheetControl.EndUpdate methods.
Here is example:
spreadsheetControl1.BeginUpdate();
var cells = spreadsheetControl1.ActiveWorksheet.GetUsedRange();
foreach (var cell in cells)
cell.Value = cell.Value.ToString().Substring(0, 15);
spreadsheetControl1.EndUpdate();
Related
Using C# .net core I am updating existing excel template with Data and formulas using EPPlus lib 4.5.3.3.
If you see the below screen shots all formula cells has '#value!' even after using calculate method in C# code (Just for reference attached xml screen short just after downloading excel before opening it). Auto calculation is also enabled in Excel.
In one of the blog mentioned to check the xml info,
My requirement is to upload this excel through code to sharepoint site and read the excel formula cells for other operations with out opening the excel manually.
is there any other way to calculate the formula cells form code and update the cell values?
I went through the Why won't this formula calculate unless i double click a cell? as well, but no luck.
using (ExcelPackage p = new ExcelPackage())
{
MemoryStream stream = new MemoryStream(byteArray);
p.Load(stream);
ExcelWorksheet worksheet = p.Workbook.Worksheets.FirstOrDefault(a => a.Name == "InputTemplate");
worksheet.Calculate();
if (worksheet != null)
{
worksheet.Cells["A3"].Value = company.CompanyName;//// Company Name
worksheet.Cells["B3"].Value = product.Name;////peoduct name
worksheet.Cells["C3"].Value = product.NetWeight;
worksheet.Cells["D3"].Value = product.ServingSize;
worksheet.Cells["E3"].Value = 0;
var produceAndIngredientDetailsForExcelList = await GetProduceAndIngredientDetails(companyId, productId);
////rowIndex will be 3
WriteProduceAndIngredientDetailsInExcel(worksheet, produceAndIngredientDetailsForExcelList);
///rowIndex will update based on no. of produce and then Agregates.
StageWiseAggregate(worksheet, produceAndIngredientDetailsForExcelList);
////Write Total Impacts Row
TotalImpactsFormulaSection(worksheet);
worksheet.Calculate();
}
Byte[] bin = p.GetAsByteArray();
return bin;
}
Formula Code
var columnIndex = 22;///"V" Column
for (; columnIndex <= 27; columnIndex++)
{
var columnName = GetExcelColumnName(columnIndex);
worksheet.Cells[currentRowIndex, columnIndex].Formula = $"=SUBTOTAL(109,{columnName}{firstRowIndex}:{columnName}{currentRowIndex - 1})";
}
Found the solution for this issue from my Architect (kudos to him).
I was writing formulas in wrong way by blindly fallowing tutorials like
https://riptutorial.com/epplus/example/26433/add-formulas-to-a-cell
Note: don't follow link shown above.
We should not use "=" for formulas. I just removed it worked like charm
var columnIndex = 22;///"V" Column
for (; columnIndex <= 27; columnIndex++)
{
var columnName = GetExcelColumnName(columnIndex);
worksheet.Cells[currentRowIndex, columnIndex].Formula = $"SUBTOTAL(109,{columnName}{firstRowIndex}:{columnName}{currentRowIndex - 1})";
}
Here is the official tutorial which mentioned correctly.
https://www.epplussoftware.com/en/Developers/ (check the second slide)
Working result:
I want to check an excel worksheet if any of the cells have a conditional format that true. I have multiple formats that format cells different colors, but I just want to be able to see if any of the conditions are true (don't care which condition). This example isn't working, but I want to know if I'm on the right track or even possible to do what I'm asking.
var lastCell = Globals.ThisAddIn.Application.ActiveCell.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);
var firstCell = ((Worksheet)Globals.ThisAddIn.Application.ActiveSheet).Cells[2,1];
var range = ((Worksheet)Globals.ThisAddIn.Application.ActiveSheet).Range[firstCell, lastCell]
var r = range.SpecialCells(XlCellType.xlCellTypeAllFormatConditions, true);
As the FormatConditions collection of a range applies to an entire range, I don't believe there's a mechanism to determine the value for each cell in the range. I think your best bet would be to test the Formula in the cell of interest. I'm sure this is not what you wanted to hear.
Get the used range with this answer. Of course, replacing xlWorkSheet with ((Worksheet)Globals.ThisAddIn.Application.ActiveSheet).
Then, assuming you set it to range, loop through the range...
for (int r = 1; r <= range.Rows.Count; r++)
{
for (int c = 1; c <= range.Columns.Count; c++)
{
...replace the formula/value temporarily with something like (preface: I almost guarantee this code will not work)...
string formula = range[r, c].Formula; //or Value, check HasFormula
for (int i = 1; i <= range.FormatConditions.Count; i++)
{
range[r, c].Formula = range.FormatConditions[i].Formula1; // or Formula2 (?)
}
bool condition = range[r, c].Value;
// do work based on the condition
range[r, c].Formula = formula;
... and close the loops.
}
}
While creating and customizing Excel file using Interop and Office 2013 installed, gives me somehow extremely slow results (more than 5 minutes).
In fact, the same thing works very well on Excel 2010 interop (just 50 seconds, exactly same process). (Code snippet below)
It would be nice to know if there is a faster way to do this. I know there are different libraries to do this but I would like to stick to Interop since everything is already in the same.
I am creating Excel file first then check if there are any empty cells or cells containing a specific string and change color of those cells.
To create Excel, I used Object array and parse it that is really faster. Main thing which is pulling it down is to search and change cell color.
// Check for empty cell and make interior silver color
for (int row = 0; row < rowNo; row++)
{
for (int col = 0; col < columnNo; col++)
{
if (string.IsNullOrEmpty(objData[row, col].ToString()))
{
// Access that cell in Excel now and change interior color
Range cell = (Range)activeSheet.Cells[row + 2, col + 1];
cell.Interior.Color = System.Drawing.Color.Silver;
}
}
}
// Check for cells contains "column header string#"
for (int col = 16; col < columnNo; col++)
{
// Get column header - only once and use it for all rows in the same column
string cellValue = activeSheet.Cells[1, col + 1].Value2.ToString();
for (int row = 0; row < rowNo; row++)
{
string value = objData[row, col].ToString();
if (string.IsNullOrEmpty(value) || value.Contains(cellValue+"#"))
{
Range cell = (Range)activeSheet.Cells[row + 2, col + 1];
cell.Interior.Color = System.Drawing.Color.Silver;
cell.Font.Color = System.Drawing.Color.Red;
}
}
}
I'd look into selecting the whole range and setting conditional formatting so blank cells are the color you want. Sorry don't have any code for you, it's been awhile since I've played in that realm.
I wanted to ask if there is some practical way of adding multiple hyperlinks in excel worksheet with C# ..? I want to generate a list of websites and anchor hyperlinks to them, so the user could click such hyperlink and get to that website.
So far I have come with simple nested for statement, which loops through every cell in a given excel range and adds hyperlink to that cell:
for (int i = 0; i < _range.Rows.Count; i++)
{
Microsoft.Office.Interop.Excel.Range row = _range.Rows[i];
for (int j = 0; j < row.Cells.Count; j++)
{
Microsoft.Office.Interop.Excel.Range cell = row.Cells[j];
cell.Hyperlinks.Add(cell, adresses[i, j], _optionalValue, _optionalValue, _optionalValue);
}
}
The code is working as intended, but it is Extremely slow due to thousands of calls of the Hyperlinks.Add method.
One thing that intrigues me is that the method set_Value from Office.Interop.Excel can add thousands of strings with one simple call, but there is no similar method for adding hyperlinks (Hyperlinks.Add can add just one hyperlink).
So my question is, is there some way to optimize adding hyperlinks to excel file in C# when you need to add a large number of hyperlinks...?
Any help would be apreciated.
I am using VS2010 and MS Excel 2010.
I have the very same problems (adding 300 hyperlinks via Range.Hyperlinks.Add takes approx. 2 min).
The runtime issue is because of the many Range-Instances.
Solution:
Use a single range instance and add Hyperlinks with the "=HYPERLINK(target, [friendlyName])" Excel-Formula.
Example:
List<string> urlsList = new List<string>();
urlsList.Add("http://www.gin.de");
// ^^ n times ...
// create shaped array with content
object[,] content = new object [urlsList.Count, 1];
foreach(string url in urlsList)
{
content[i, 1] = string.Format("=HYPERLINK(\"{0}\")", url);
}
// get Range
string rangeDescription = string.Format("A1:A{0}", urlsList.Count+1) // excel indexes start by 1
Xl.Range xlRange = worksheet.Range[rangeDescription, XlTools.missing];
// set value finally
xlRange.Value2 = content;
... takes just 1 sec ...
In C# I have a DataTable which has valid values, some of which are in exponential format. I export this DataTable to an xlsx file using ExtremeML (which I believe is based on OpenXML) and the following code:
if (!Directory.Exists(savePath.Text))
Directory.CreateDirectory(savePath.Text);
using (var package = SpreadsheetDocumentWrapper.Create(savePath.Text + fileName + ".xlsx"))
{
for (int i = 0; i < dataset.Tables.Count; i++)
{
// declares worksheet
var part = package.WorkbookPart.WorksheetParts.Add(dataset.Tables[i].TableName.ToString()); // second worksheet filled by second collection
bool first = true;
int position = 0;
for (int row = 0; row < dataset.Tables[i].Rows.Count; row++)
{
for (int col = 0; col < dataset.Tables[i].Columns.Count; col++)
{
// adds to file
if (first)
part.Worksheet.SetCellValue(new GridReference(row, col), dataset.Tables[i].Columns[col].ColumnName.ToString());
else
part.Worksheet.SetCellValue(new GridReference(row, col), dataset.Tables[i].Rows[position][col]);
}
if (first)
{
first = false;
}
else
position++;
}
}
}
The resulting excel file is almost acceptable, except that negative exponential negative numbers
(for example -7.45E-05) treat the negative sign as a digit (previous example becomes 0.000-745). The columns are all set to be doubles in the DataTable, so the excel file should be formatting them as such.
Any ideas as to the cause of this problem?
(Also, I know the code is a touch painful to grok- the code is not mine, and I haven't gotten around to cleaning up the program.)
Your sample code seems fine.
This looks like a bug in the way way ExtremeML converts doubles. As you say, it only appears to affect negative exponential negative numbers.
I am currently debugging it and creating additional unit tests to cover these scenarios, after which I will commit the updated code to CodePlex and post an update here.
Edit: This bug has now been fixed. You'll need to download and build the ExtremeML source code from CodePlex, as the runtime release is not currently up-to-date.