I have an Excel Add-in having custom functions and taskpane. My client created a workbook having multiple sheets using my custom functions approximately 3500+ function calls in current workbook. When a user opens this workbook, I need to recalculate sheet so that only my functions are recalculated. To achieve this task, I have performed following steps.
Loop on sheets in workbook.
Search each sheet for my formula using worksheet.findAllOrNullObject() function.
if search result is not NullObject, then call ranges.calculate(). Which should trigger function calls.
var sheets = context.workbook.worksheets;
sheets.load("items/name");
await context.sync();
for (var i = 0; i < sheets.items.length; i++) {
var sheet = sheets.items[i];
const foundRanges = sheet.findAllOrNullObject(FORMULA_DATA[formula], {
completeMatch: false,
matchCase: false
});
await context.sync();
if (!foundRanges.isNullObject)
foundRanges.calculate();
await context.sync();
}
}
Problem I am facing that when I call recalculate function, all cells referring to these functions show BUSY! which means that my function have not resolved promise yet, but no function is actually called. I added break points at start of each function during debugging but no code stops there (I a change a single cell then breakpoint is hit).
I enabled run-time logging and it has entries for each call begin but no end call entry.
Also one of the cell reference is passed to all functions and if I change its value, then all function calls are made properly and it shows result as desired and logfile contains entries for begin and end for all calls.
After thoroughly investigating the issue, I have come to conclusion that this client was using other Add-in which had same function names and it was saved in workbook. When I recalculated the sheet range areas, it was trying to call functions from those old Add-in which was uninstalled earlier. Therefore all cells were showing BUSY! and since code was not there in excel these promises were never resolved. Once I removed all taskpanes from workbook and re-opened it this problem does not appeared again.
To save Taskpanes info
Office.addin.setStartupBehavior(Office.StartupBehavior.load);
To Remove Taskpanes information, use File => Info => Check for Issues => Inspect Document => Inspect.
After it displays results , Task pane Add-ins => Remove All.
Related
In my rdlc report have following columns
SlNo, Item, Uom, Qty, Rate, Amount
Here the Amount field is a formula (Rate*Qty)
The report is working fine, and when i export to excel also displaying the values are correctly.
But my problem is, after export to excel, when i change the Qty or Rate columns in excel file the Amount is not get changed automatically, because the formula is missing in the excel cell.
How can we include the formula in Amount column while export to excel from .rdlc?
I'm afraid that this required behaviour isn't really possible by just using the rdlc rendering.
In my search I stumbled upon this same link that QHarr posted: https://social.msdn.microsoft.com/Forums/en-US/3ddf11bf-e10f-4a3e-bd6a-d666eacb5ce4/report-viewer-export-ms-report-data-to-excel-with-formula?forum=vsreportcontrols
I haven't tried the project that they're suggesting but this might possibly be your best solution if it works. Unfortunately I do not have the time to test it myself, so if you test this please share your results.
I thought of the following workaround that seems to work most of the times, but isn't really that reliable because the formula sometimes gets displayed as full-text instead of being calculated. But I guess this could be solved by editing the excel file just after being exported, and changing the cell properties of this column containing the formula or just triggering the calculate.
Using the built-in-field Globals!RenderFormat.Name you can determine the render mode, this way you can display the result correctly when the report is being rendered to something different than Excel. When you export to Excel, you could change the value of the cell to the actual formula.
To form the formula it's self you'll need to figure this out on your own, but the RowNumber(Scope as String) function can be of use here to determine the row number of your cells.
Here is a possible example for the expression value of your amount column
=IIF(Globals!RenderFormat.Name LIKE "EXCEL*", "=E" & Cstr(RowNumber("DataSet1")+2) & "*F" & Cstr(RowNumber("DataSet1")+2) ,Fields!Rate.Value * Fields!Qty.Value )
Now considering that this formula sometimes gets displayed as full-text, and you'll probably have to edit the file post-rendering. If it's too complicated to determine which row/column the cell is on, you could also do this post-rendering. But I believe that the above expression should be easy enough to use to get your desired result without having to do much after rendering.
Update: The following code could be used to force the calculation of the formula (post rendering)
var fpath = #"C:\MyReport.xlsx";
using (var fs = File.Create(fpath))
{
var lr = new LocalReport();
//Initializing your reporter
lr.ReportEmbeddedResource = "MyReport.rdlc";
//Rendering to excel
var fbytes = lr.Render("Excel");
fs.Write(fbytes, 0, fbytes.Length);
}
var xlApp = new Microsoft.Office.Interop.Excel.Application() { Visible = false };
var wb = xlApp.Workbooks.Open(fpath);
var ws = wb.Worksheets[1];
var range = ws.UsedRange;
foreach (var cell in range.Cells)
{
var cellv = cell.Text as string;
if (!string.IsNullOrWhiteSpace(cellv) && cellv.StartsWith("="))
{
cell.Formula = cellv;
}
}
wb.Save();
wb.Close(0);
xlApp.Quit();
I can not get tables name in active worksheet. I have a drop-down that will be populated with worksheets in workbook. I have another drop-down that should get all columns names(header) in selected worksheets. Somehow range.address has "sheet1!2:2" .
Here is code that I used:
function getRow(worksheetName) {
Excel.run(function (ctx) {
// Queue a command to write the sample data to the worksheet
// at moment i have only one worksheet named "Sheet1"
var range = ctx.workbook.worksheets.getItem(worksheetName).getRange().getRow(1);
range.load('address');
// Run the queued-up commands, and return a promise to indicate task
//completion
return ctx.sync().then(function () {
console.log(range.address); // prints Sheet1!2:2
})
})
.catch(errorHandler);
}
Here is a link to spreadsheet that I used for testing.
Any clue what i am doing wrong here?
I'd suggest that you replace getRange() with getUsedRange() instead, as shown here:
function getRow(worksheetName) {
Excel.run(function (ctx) {
var range = ctx.workbook.worksheets.getItem(worksheetName).getUsedRange().getRow(1);
range.load('address');
return ctx.sync().then(function () {
console.log(range.address);
})
})
.catch(errorHandler);
}
Rick Kirkham's comment above is correct. In the file that you've linked to, there is no table object -- although the worksheet does contain several rows/columns of data, that data is not explicitly contained inside a table.
Are you able to manually manipulate this file? If yes, then you can create a table object for the data by doing the following:
Select the data you want to be in the table.
With that data selected, choose the Table button (on the Insert tab).
Verify inputs in the Create Table prompt and choose OK.
If you are not able to manually manipulate this file, then you can create the table (from the range of data in your worksheet) by using the Office JavaScript API, as described here: https://dev.office.com/docs/add-ins/excel/excel-add-ins-tables#convert-a-range-to-a-table.
I have a question relating to Excel's worksheet protection...
The context is that I need to have different worksheets available for different user groups to edit but all groups must at least see all sheets e.g. usergroup1 can edit sheets two and three and parts of sheet one, usergroup2 can edit only sheet one.
I am able to set the FormatProtection (range.format.protection.locked = false;) accordingly and WorksheetProtection (worksheet.protection.protect();) to enable this but I don't appear to have the ability to set a password through the API against the Worksheet Protection? This means for example, that either group can simply click the Unprotect Sheet option in the review ribbon and edit the sheets that I don't want them to.
I've tried going through the below documentation but to no avail unfortunately.
http://dev.office.com/reference/add-ins/excel/worksheetprotection
https://github.com/OfficeDev/office-js-docs/blob/master/reference/excel/worksheetprotection.md
As an example, here is a function that I'd like to complete:
function CopyWorksheet() {
var newAddress;
Excel.run(function (ctx) {
var worksheet = ctx.workbook.worksheets.getActiveWorksheet();
var range = worksheet.getUsedRange();
range.load();
// insert new worksheet
var newWorksheetName = "Copied_Sheet";
var newWorksheet = ctx.workbook.worksheets.add(newWorksheetName);
return ctx.sync().then(function () {
// copy the old values to the new worksheet
newAddress = range.address.substring(range.address.indexOf("!") + 1);
newWorksheet.getRange(newAddress).values = range.values;
newWorksheet.getRange(newAddress).formulas = range.formulas;
newWorksheet.getRange(newAddress).text = range.text;
// protect both worksheets
worksheet.protection.protect();
newWorksheet.protection.protect();
// requirement here to set a password so that no one can
// edit the worksheets by selecting 'Unprotect Sheet' in excel
// ...
})
.then(ctx.sync)})
.catch(function(error) {
console.log("Error: " + error);
});
}
Currently, I'm using Excel 2016 (desktop version). Is this possible to implement or have I missed some functionality that exists which can achieve the same result?
Thanks for your help.
Password-protection is not available in our APIs. You can protect the sheet to avoid casual edits, but you can't password-protect. The reason is that password-protection is not available on all endpoints (IIRC, there was an issue with Excel Online).
If you want to file a suggestion bug on UserVoice, you can see if we'd consider doing password-protection as a Desktop-only API. We have so far avoided doing those in Excel, but I do know that Word has done a few "WordApiDesktop" APIs. So depending on how much it's blocking your (and others') scenario, that might be an option. In which case you'd be able to password-protect and unprotect on desktop, but wouldn't be able to take those actions online.
There is an update for this issue: we now support password protection. Check https://learn.microsoft.com/en-us/javascript/api/excel/excel.workbookprotection?view=office-js#protect-password-
I simply want the function to return the URL from hyperlinked text in a cell.
Found a solution:
Public Function GetAddress(HyperlinkCell As Range)
'GetAddress = Replace(HyperlinkCell.Hyperlinks(1).Address, "URL:", "")
Debug.Print ("Function was called on " + HyperlinkCell)
End Function
However, not even the Debug is being called.
The workbook is Macro Enabled and has all access under Trust.
Macros work fine, so what does it mean that functions are not working in my Spreadsheet?
Thanks
The problem is that you have not pasted the code in the module and hence excel is not able to recognize that name. You have to paste the code in a module. Same goes for other UDF.
After speaking to Google Enterprise Support they suggested I create a post on Stackoverflow.
I have a Google Doc sheet with a list of stores (sheet A) and I'm trying to reference another sheet (sheet B) to VOID specific stores.
What I'm going to accomplish is if a store name on the void sheet is entered into sheet A it will automatically convert the store name to VOID.
Google support believes an IF statement would be the beginning to the solution, they weren't able to help beyond this.
For anyone's time that comes up with a solution, I'd be happy to buy you a couple Starbucks coffees. Your support means a lot.
make it simple using Google Scripts. (Tutorial)
To edit scripts do: Tools -> Script Editor
and in the current add this function
EDIT:
Well, you need to make a trigger. In this case will be when the current sheet is edited
Here is the javascript
function onEdit(event) {
// get actual spreadsheet
var actual = event.source.getActiveSheet();
// Returns the active cell
var cellSheet1 = actual.getActiveCell();
// get actual range
var range = event.source.getActiveRange();
// get active spreadsheet
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// get sheets
var sheet_two = activeSpreadsheet.getSheetByName("Sheet2");
range.copyValuesToRange(sheet_two, range.getColumn(), range.getColumn(), range.getRow(), range.getRow());
// get cell from sheet2
var cell = sheet_two.getRange(range.getValue());
cell.setValue(cellSheet1.getValue());
// display log
Logger.log(cellSheet1.getValue());
}
if you want to test it you can check my spreadsheet, there you can check that all data thats is inserted in sheet1 will be copied to sheet2