Excel UDF takes empty parameter in unwanted second invocation - c#

In my project the argument of one UDF is the result of another UDF function.
For example:
GetPeriod(string name, string date) {
// logic
}
Data(string name, string period) {
// logic
}
I am using these UDF in two different cells as
cell a1: =GetPeriod("KMP", "27-12-18")
cell a3: =Data(a$1,"KMP")
I am using the result of one UDF (GetPeriod) as a parameter in another UDF(Data).
The issue here is the Data UDF is triggered twice: one time with the actual result, and one time with empty value of cell A1.
I tried setting calculation mode to manual, but it still gets invoked twice.
Is there any possible problem with this design or is there any way to set up the precedence in UDF so that Excel understands which UDF to trigger first?

I'm not sure how VBA code translate to c# for Excel, so I'll try to give a comprehensive explanation/example.
In VBA, there are two main ways to pass a reference for an excel cell (or Range) to a UDF.
Using a string parameter (i.e. Data(name as String))
Using a Range Object parameter (i.e. Data(name as Range) or more broadly Data(name as Object))
When the string parameter is used, the range's value is usually accessed in the code via Worksheet("Name").Range("String Param")).Value. When the object parameter is used, the code generally looks like RangeParam.Value.
There are also times when a range is hard-coded into or indirectly referenced by a UDF. I would avoid doing this; especially if that range contains a formula.
When Excel is doing calculations, it performs multiple steps.
Initiate calculations in each cell to determine the range parameters. (This is used to determine dependencies.)
Create the dependency tree with the proper order of calculations.
Do the finalized calculations in the determined order.
The important things to understand from this are:
If a range is referenced via a string parameter, Excel may not understand the dependency.
If a range is referenced in the calculation, but hardcoded in, indirectly referenced, or otherwise not passed in as a parameter, Excel will not understand the dependency.
If the UDF will get differing results depending on the number of times it's run, then there will be unexpected results (from Excel's multi-step dependency-determinations).
A few other notes:
If an error occurs in a UDF, how that's dealt with can vary a tad between whether the UDF being used in the worksheet or whether it's being called via other code.
I don't think you'll be able to prevent Excel from running the calculation twice, as that's part of how it's determining dependencies (unless I'm completely misunderstanding what you wrote). However, I would also expect that the calculation with the empty value of A1 would occur before the final calculation value with the actual result. If the second (final) calculation result is the one with the blank A1, then I'm not sure what to tell you, besides checking that all cells/ranges being referenced in a UDF are being passed into that UDF as parameter objects.

Make sure that cell A3 is empty before you insert the UDF into cell A1. The only way that the "Data" UDF gets called is if it already exists in the sheet you're adding it to. To clarify - If there is no formula in cell A3, then there's no way the "Data" UDF can get called. But changing the value in cell A1 by inserting the "Period" UDF will trigger the formula in cell A3 to recalculate, since one of the parameters being passed into it just got changed.
Another option would predicated on needing just the values and not the actual UDFs in the cells. In that case simply set the ".Value2" of the cells using the "Data" and "Period" functions, passing the "Period" value as a function into the "Data" function.
Range rngPeriod; //Set to cell "A1"
Range rngDate; //Set to cell "A3"
rngPeriod.Value2 = GetPeriod("KMP", "27-12-18");
rngData.Value2 =Data(GetPeriod("KMP", "27-12-18"),"KMP");

Related

SSIS Excel destination inserting null values on other columns

I have 2 script components which extract data from result set objects say User::AllXData and User::AllYData.
It is run through a foreach loop and the data is stored in a data table.
Next, I'm adding the data into a excel sheet using Excel destination. Now when I do that. All the data corresponding to column A (i.e, the data from User::AllXData) is being added to the excel sheet, but the column B gets filled with null values till the end of column A's data.
Then column B gets filled leaving column A with null data. It's supposed to be aligned.
Is there a workaround for this?
Edit:
After a long of grinding and running many tests, finally came across a solution.
The answer to this is pretty simple. Instead of using two objects as result set, it's better to use only one.
If you're going to query from a single source, include all the required columns in your SQL query into one object result set and use that as a read only variable in the script component.
Create a single data table that includes all the required columns and adds them into your excel destination row by row without any null values.
Here's an article that has a good example.

EPPlus - How to rename a named range and maintain correct formulas?

I have an Excel file with a named range (just one cell). There are other cells who's formulas use this name. I am trying to programatically rename the cell using EPPlus. My first attempt I simply removed the old name and added the new:
workbook.Names.Remove("OldName");
workbook.Names.Add("NewName", mySheet.Cells["B2"]);
This worked to rename the range, but the formulas still use "OldName" so they now come up with #NAME? instead of the corresponding cell's value.
If I get a reference to the name using LINQ like var nameReference = workbook.Names.First(x => x.Name == "OldName") I notice by trying to set the name property like nameReference.Name == "NewName" that it is a read-only property.
Is there a method I haven't seen for renaming these named ranges? If not, what alternatives are there to my situation? If I could get a reference to all cells who's formulas call this named range I could change each formula. I think I would need to simply scan every used cells' formulas for this reference, which I think would take a while, especially considering I actually may have dozens of Excel workbooks to scan through.

Excel RTD multiple cells

I did a single cell subscription, so when I put the formula into the cell, it updates it correctly.
Now, I'm returning an object with multiple values and I want to display all of them in Excel cells. Is it possibly to only put a formula in 1A, subscribe once, get all values at once, and then distribute the information from one object to 1A, 1B, 1C... Or is the only way to subscribe individually to each field and put an RTD formula for every cell?
I came up with a workaround using a VBA function. Create your Excel sheet, make column headings that will match the fields that you need, put a formula in your 1A cell, and run the VBA function.
The function is just a for loop over all columns in Range (number of rows is still up to), that just gets the column header value and does your magic and the rest is up to simple string manipulation of getting the formula, converting it to string and replacing $C1 to $D1 etc.
Example:
"=RTD("ProgId", , "Your arbitrary parameter here", $C1)"
Wouldn't say it is the fastest way, but it is a good solution

How to return a custom object or double[,] to a cell in Excel from ExcelDna?

Ideally my UDF would return some double results, either in a form of double[,], double[] or as a custom object. I would like it to be all stored in a single cell in Excel and then use another UDF to extract them. It is similar like cache the result of a calculation and display it later on demand.
Is it possible?
One approach is to have an internal 'object store' in your Excel-DNA add-in, and then have a function that returns some kind of 'handle' to Excel, together with accessor functions that take the 'handle' and access the object as required. Cleaning up the objects you can create is a possible issue, but can be dealt with using some of the RTD features.
This discussion on the Excel-DNA group has an example of implementing this idea in F# - https://groups.google.com/group/exceldna/browse_frm/thread/138bc83923701e6d.
It certainly is possible, but it is difficult to give you a detailed answer without knowing how you are using ExcelDNA. What I mea by that is whether you are wrapping your C# methods in vba code, or are getting a handle on the Excel application in C# and writing to the workbook directly from there.
In both cases you can do what you want, but the way to do it would be slightly different. The wrapper method would also be slightly less flexible, since you have to first pass out your values to the vba UDF and then write them to the cell from there, so you will be restricted in the data type you can return (as far as I know anyway).
The way to do this would be to write the results to a specific cell, maybe on a hidden sheet, so it can't be tampered with, and then retrieve those using another UDF. You would have to hardcode the cell address, or maybe parse the sheet to find the values you want.
In case of a method returning a double[,], you would probably have to write the values to two different cells, or in one cell as text with a separator and then convert from text to double when you retrieve the cell value, something like Double.Parse(cell.value.ToString().Split(',')[0]) to get the first values (assuming that you are storing the values as a comma-separated string) or similar code in vba if you use a pure vba UDF to get the values...
If you want to do this, I think you should definitely use a hidden sheet with a well-defined structure to store your values. If you only need to store the values for the duration of the session, then I think you should store them in global variables in a vba module.
UPDATE
Since you are just writing functions, I don't think you will be able to pass out a custom object (unless you implement your own converter, convert it to text and then read it back in that way).
You pass back the double or double[,] to a variable in your UDF and write it to a cell from there. Then read it back again from that cell with any other UDF.
The rest is the same as I wrote above. If you store two values in the same cell, you will have to do that as text, so you will have to split and parse the values first in your UDF before passing them to your C# method (or you can do the parsing in the method).
In practice there should be no problem at all with what you are trying to do.

How to specify data types when dragging content to Excel

I am initiating drag and drop from my WinForms application using this simple
IDataObject data = new DataObject();
string textToExcel = "Hello\tWorld\t1\t2\nHello\tWorld\t1\t2\n"
data.SetData(DataFormats.Text, textToExcel);
I works fine when dropped on Excel, it ends up nicely in columns and rows.
Problem is that Excel does not know that the cells with values 1 and 2 are numerics
and it gets worse when dropping a date value.
How can I tell Excel the data types of the individual cells, is there some richer type that Excel accepts when content is being dropped into it.
All you are able to communicate with Drag-Drop to Excel are strings, which Excel will automatically convert to a date or numeric type if it can. If you don't want it to convert identifable types to a data type then you should begin the value with an appostrophe (') character, such as '100, instead of 100.
If you wish to have full control, you should subscribe to the Excel.Worksheet.Change event which will be triggered at the end of the Drag-Drop action. At that point you can do a custom conversion of your own data. This would be facilitated if you transmit your data as a custom format to begin with, one that would not be automatically converted by Excel. For example, instead of sending a 2x2 block of values such as:
100 Hello
$1.25 10/9/2010
You could send it through as:
<DragDrop:Double>100</Double> <DragDrop:String>Hello</String>
<DragDrop:Currency>1.25</Currency> <DragDrop:Date>2010.10.9</Date>
These values would be received by the cells as strings. But when the Worksheet.Change event fires, it will tell you which cells have been changed, and your routine could process the strings, looking for anything that begins with "<DragDrop:". You could then convert these to the required data types as specified in the strings themselves.
For a detailed example, see the article Adding Drag-and-Drop Functionality using the .NET Framework and Visual Studio 2005 Tools for Office Second Edition. In that article, they use an example that is much more unique than "<DragDrop:" -- they use a GUID at the front of the string to really make sure that the string is unique and identifiable. But the basic strategy is as I described, above.
Hope this helps,
Mike

Categories

Resources