Application.FileDialog vs GetOpenFilename: What is the difference? - excel

I'm trying to teach myself VBA during the short holiday break.
For my first module, I want to create a module that does the following:
Displays dialog box. Prompts user to select CSV files.
Open the CSV files via loop.
Summarize the CSV files based from their data.
Anyway, for number1, I search and found two methods in examples online:
Application.FileDialog and GetOpenFilename. Hm, I was wondering, what is the difference between the two (limitation, capabilities, speed, etc.) Is there a significant advantage of one method over another?
Thanks.

Application.FileDialog is umbrella property to provide you with several types of standard file dialogs: (1) file picker, (2) folder picker, (3) open file dialog and (4) "Save As" dialog. After getting FileDialog object, you can customize it further and call Show() (in some cases followed by Execute()) to display it and get user action.
Application.GetOpenFilename method displays dialog for getting file name (also see cases 1, 3, 4 above) allowing you to fast-preset selected set of its properties, namely
file filter, index of initial file filter, dialog title, action button text (e.g. "Open" or "Save") and option, whether multiple files can be selected at once (MultiSelect). But you have no control beyond these presets.
So basically former one gives you more control over the dialog window while the latter is "faster" to adopt in standard simple one-off scenarios.
Sticking with fast approach, you might want to prefer GetOpenFilename(), but if you wish to create library for larger application which will for example always use fie dialog with some custom standards (custom file filters or custom window custom title specific for your application), you might prefer custom method which tweaks FileDialog according to your standards and displays it.
Also please note that both above methods present you with dialog customized for Microsoft Office. With a bit of googling you can find your way to use standard dialogs from Microsoft Windows, for example folder picker dialog.

Here are differences I have sorted out, starting from the idea FileDialog gives an object to set up and retrieve results from, while GetOpenFileName gives a one-and-done function.
a. File Dialog has more options. These include:
pre-setting a filename for when the dialog opens (InitialFileName property)
changing the names of the button (ButtonName property)
changing how the files are shown (InitialView property)
an Execute method to actually open or save the file that's been selected.
b. GetOpenFilename goes with GetSaveAsFilename.
The difference between these two is also confusing at first, until you realize that you can't change the button caption (unless possibly on a Mac). That means you can change the title, but the button will still say "Open" or "Save". If you are picking files for other reasons, FileDialog lets you set the button to a custom string.
c. File Dialog Types. While FileDialog leaves more to the user, it has basic setups by use of the single fileDialogType argument:
set fd = Application.FileDialog(msoFileDialogFilePicker)
set fd = Application.FileDialog(msoFileDialogFileOpen)
set fd = Application.FileDialog(msoFileDialogSaveAs)
Each gets you the same object (see the next bullet), but with different properties. Most important is the DialogType property, which is then read-only, and can be changed only via a new set statement. This property determines what happens with .Execute: open, save, or for msoFileDialogPicker, an error. Other properties are set to match the initializing fileDialogType argument, but can generally be changed before you call .Show.
d. File Dialog quirks. With the FileDialog object, it turns out that you have just one per application. That means if you try to think ahead with set fdo = Application.FileDialog(msoFileDialogOpen) to open files and then set fdsa = Application.FileDialog(msoFileDialogSaveAs) to save files, hoping to keep both for later use, you'll soon learn that fdo and fdsa refer to the same object, now with the msoFileDialogSaveAs settings. The lesson is to set the parameters just before you use it.
e.) Getting the results. For the GetOpenFilename and GetSaveAsFilename methods, since they operate as functions, you get the selection as a return value. The return value may be a single selection, an array, or False if the user clicked Cancel. For the FileDialog object, in contrast, you get results through a SelectedItems collection as a property of the object. If a user clicked Cancel, the count will be 0, and the .Show method also returns -1. The latter is used in the pattern if fd.Show then fd.Execute, often in a with block.
Based on these, I think I'll stick with the file dialog. It can also pick folders, so it's probably a good one to be familiar with.

Application.FileDialog is an object. GetOpenFilename is a property. If you call GetOpenFilename, Excel owns the dialog object, configures it, displays it, and gives you the return value. If you use Application.FileDialog, you configure it, display it, and get the return value from the object. Outside of that, they're exactly the same - it's just a matter of who owns and controls the underlying FileDialog.

Related

How do you instantiate an Excel VBA AddIn variable

In the references window, you can browse to and set a reference to a custom AddIn. For purposes of discussion let's say it is named MyAddIn.xlam. I changed the project name to MyAddIn and it resides in the standard AddIns path. So I have successfully done that and then have defined an associated global variable as below in a different Excel workbook:
Private m_MyAddIn As MyAddIn.thisworkbook
How and when do I instantiate the variable I have defined? It would be simple if I could cast the associated Application.AddIns collection item to my specific AddIn but that isn't an option.
Within the MyAddIn code, I have tried defining a public property ParentWorkbook that just returns ThisWorkbook. I have also tried doing the same in a public class defined in MyAddIn. But I cannot figure out how to instantiate the variable.
Here is what I am trying to do:
MyAddIn raises a custom event MyEvent whenever a Workbook is opened or a new sheet is created
One or more custom xlsbs MyCustom1.xlsb, MyCustom2.xlsb will handle the event
Because the xlsbs are looking for an event, I need to be able to define a WithEvents MyAddIn variable in the xlsbs to be able to handle them
So for example, a PivotTable xlsb might do some custom actions based on PivotTable events. These could all be setup in the xlsb once it received notifications that a new file was opened. It could check to see if a PivotTable was present and skip all others, etc.
Why am I trying to do it this way?
I'm trying to avoid a bunch of chatter. If each xlsb uses the application object to check for open files, etc. they will all run every time a file is opened in Excel
My thought is that only MyAddIn will check that event and then raise the custom events as appropriate so the xlsbs.
I hope this helps.
Thanks for any ideas and/or suggestions.
I think you should trap App level Workbook.Activate events like explained here: https://www.exceltip.com/events-in-vba/how-to-create-application-level-events-in-excel-vba.html
Main points from that page:
Insert a class module. Name it as you want. I named it MyAppEvents.
Define an event variable of Application type with keyword WithEvents.
Initialize this event in class_initialize() subroutine.
Now define the events you want to use. From the top-left drop-down, select the event object.
You can then set wb = ActiveWorkbook and do what you want with it.

"Error - Script is busy" & crash, whenever I try to reference uidoc from button created in DXL

I have created a form in which the user enters values, then clicks "Add". This then creates a table in a Rich Text field on the form.
This is done by creating a temporary document with a richtextitem on it, expoting this via DXL, inserting the table in DXL into the richtextitem, and importing in back. Then I use uidoc.ImportItem to copy the table from the temporary document into my uidoc.
This works just fine, and I'm rather pleased with myself. In addition, the user can "Add" further rows. This is done by deleting the table (using Call uidoc.FieldSetText("table", "")) and recreating the table from the new values, as above.
However, each row of the document has two buttons, "Edit" and "Delete", so that the user can either edit a row, or delete it, after the table has been created.
So far, I am concentrating on coding the "Delete", and it works. However, when the code reaches any uidoc action, e.g. Call uidoc.FieldSetText("table", "") or Call uidoc.EditGotoField("Table") I get the dreaded "Error - Script is Busy" message in the status bar, and Notes crashes when I close the document.
I presume this is caused by the button thinking it's in a different document, so when it moves focus to a field in what it considers to be a different uidocument, it causes a problem.
I've tried a few different approaches, e.g. having the button set fields on the NotesDocument, and then refresh, using a hidden value to trigger the updateTable Sub. I've also experimented with triggering an agent, but as the uidoc has not been saved, I can't get a session.documentcontext, and of course I can't access the UI in an agent.
One thing I haven't tried yet, is making the Delete button trigger an formula command that would cause the uidoc to be refreshed, and set off the updateTable Sub from the PostRecalc event.
If anyone has any other suggestions, I'd be very grateful as this is the one niggle that is preventing me improving the performance of one of our legacy forms by about 1000%.
OK, so I tried using Formula language instead of Lotusscript in my DXL-created button in a rich text table in a rich text field in an unsaved uidoc. And it worked :-)
#Setfield("deleteRow"; 1);
#Command([ViewRefreshFields])
Obviously, the LotusScript that creates the DXL document subsitutes the approriate row number in each button's #Setfield. Also, I now have LotusScript in the QueryCalc event that checks the value of doc.deleteRow(0), and calls the updateTable subroutine if it's greater than 0.
This process has been yet more evidence that explaining one's problems to someone else can often trigger the thought that leads to the solution.

Defaulting parent entity of custom activity

This should be a simple thing, but as I am quickly finding out with CRM 2011, simple things almost never are...
Anyway, I have a custom activity type, a "Trip". A Trip represents a single visit by a service technician to a client's site. Trips are always created in connection with a Case (Incident) and never with any other entity type, so the entity exists as a custom activity that does not "Display In Activity Menus". That allows me to drop a subgrid of Trips into the primary Case view, making it the only place these things can be created.
The problem is that when I try to create a new Trip from the Case form (by clicking the subgrid and choosing "Add New Trip" from the ribbon), the "Regarding" field isn't populated with the Case that I was on when I clicked "Add". Any of the "built-in" activity types will default their corresponding field with no problem, so it would seem to me that I can do the same here. The field's supposed to be read-only once this defaulting works, but just so I can set up the reference when creating new Trips, I've made it editable, and of course when you pop up the lookup, you can assign this Trip to anything (which is why I want the field read-only in the first place).
I need the form to default the parent Case when the Trip form loads. It has to be there when it's first created, because if it isn't there on the first save, then some plugin code that sets other default values (namely data relating to other Trips that may or may not exist for the same Case) won't work properly.
I read about relationship field mapping, where you can default the values of various fields based on fields of the parent entity, but the relationship between the Incident and my Trip is not listed as mappable from either side and I can't figure out how to make it mappable. I also know it's possible to set default values using JavaScript, but if there's a less "custom" way to set this particular behavior up I would love to know how.
As an epitaph based on Mike's comment, it does indeed seem that Microsoft doesn't want this done. But, it's still possible to do it. The basic steps are:
Download the Ribbon Workbench solution and import it into CRM.
Create a new solution containing the entity or entities you want to be able to customize.
Create a JavaScript file containing functions that will open the form you want and pass the information you need as a parameter. There are two ways to specify the parameters; you can define custom parameters on the form, or you can use Microsoft's undocumented parameters for the Regarding field:
function OpenNewTripFromCase()
{
var entityId = Xrm.Page.data.entity.getId();
var entityTypeCode= Xrm.Page.context.getQueryStringParameters().etc;
var entityDisplayName = Xrm.Page.getAttribute("title").getValue();
var params = {};
params["pId"] = entityId;
params["pType"] = entityTypeCode;
params["pName"] = entityDisplayName;
Xrm.Utility.openEntityForm("cst_trip", null, params);
}
Import this JavaScript file as a Web Resource, and add it to the Solution so it will be available to Ribbon Workbench.
Open up Ribbon Workbench and open the solution you created with your entity and resources. Click the Solution Elements tab, and find the "Commands" item. Right-click and select "Add New". Now expand the list, find and click on your new command. In the Properties pane, change the name of the command to be more descriptive (it's recommended you only change the third "Command#" term), then click the magnifying glass button to the right of the Actions field.
In the window that pops up, click "Add", then choose "JavaScript Function Action". A new item will be added to the action list. Click it, then in the properties pane to the right, find the library containing your function, then type in the function name. I didn't get any sort of IntelliSense here, so be careful typing in the function name. Add parameters if you need to (you shouldn't need to with the code above), then click OK
Now click the custom activity entity in the Entities list of Ribbon Workbench, then in the dropdown to the upper right of the ribbon layout, select the SubGrid ribbon. Create a button in this Ribbon, give it the icon and text you want, and in the Properties pane, set the Command to the command you just created.
Publish the customized Solution, and you should, if you did everything correctly, have a new button that will do the same thing as the "Add New" button when you've selected a sub-grid, but will additionally populate the Regarding field.

Refreshing a Lotus Notes folder

I'm working on a Lotusscript at the moment which places a menu of actions on the $Inbox (and thereby any folders derived from that design). This action menu has several items with "Hide action if formula is true" selected. The logic behind all of this works perfectly, however I now have the need to re-evaluate these formulas if the user performs certain actions within the folder (eg: if the action is hidden because a certain flag has been set, and that flag is changed I would then like to re-evaluate the formula so the action now appears). I can't seem to find any way to reload a folder or re-evaluate these formulas. Does anyone know of a way to do this?
There is a RefreshHideFormulas method for the NotesUIDocument, but not for a view. You may be able to call the ReloadWindow() method of NotesUIWorkspace, though, so that's worth a try.
As an alternative, triggering an agent that calls the #Command RefreshHideFormulas may also work for you:
Here is some code (borrowed from http://ideajam.net/ideajam/p/ij.nsf/0/3BBA7E25A972ABD88625759600445A50?OpenDocument)
1) Create an #Formula Agent called "RefreshActions", Agent List Trigger with the following code:
#SetTargetFrame("YourFrame");
#UpdateFormulaContext;
#Command([RefreshHideFormulas]);
2) In your Lotusscript, where you want to insert a "refresh frame" call, add this:
Dim agent As NotesAgent
Set agent = db.Getagent("RefreshActions")
Call agent.Run()
You can use property "Evaluate actions for every document change" found in view properties, [i] tab. Every selection (click, arrows) of document will trigger reevaluating hide whens for actions. Maybe it will work after refreshing view by some action.

Enable/disable editing of a form field from code

I'm not a Notes programmer, however, for my sins, have been working on some Notes features for an in-house project recently. I need to enable/disable editing of a field depending on circumstances. It seems to me to be a fairly standard feature, I need, but I can't find any information on how to do this anywhere.
In form setup (and other field's onchange) code, something like the following:
if some requirement = true then
textField.enable = true
else
textField.enable = false
end if
I've seen other places where there's a workaround of conditionally hiding paragraphs based on some code, having 2 paragraphs with opposite hiding conditions, one with an editable field, the other with a computed field. However, I don't know enough about Notes to see how this is implemented (I can see it done on other forms, but there seem to be some 'magic' steps within Notes which I either can't see or don't get).
[EDIT]
The reply from Kerr seems to be what I'm looking for, but I still can't find out where the InputEnabled property is located. Should have said in the initial question, I'm using Notes 7.0.3.
In fairness, it doesn't matter what the circumstances are for when to enable/disable the field, it's just some boolean condition that is set, in my case only on form loading so I don't even have to worry about this changing dynamically while the form is displayed.
I've got a few issues with Notes, my largest bugbear being that it's so tied so tightly to the Designer UI, which is utter shite. I can do this sort of thing programmatically in most GUI languages (C#, Java, Delphi, even VB), but I need to open property boxes in Notes and set them correctly.
This would be OK as an optional method, but forcing you to go this way means you can only work as well as the IDE lets you in this case, and the IDE here seems to actively work against you. You can't open multiple functions/scripts, you can't swap from one script to another without going back to the menus on the left, you can't easily search the codebase for occurrences of variables/fields (and believe me, this is a major failing for me because either Notes or the internal codebase in my case seems to make a lot of use of global variables!), you can only work with fields through the property boxes that get displayed, you can't edit code in Designer while debugging through the main Notes client.
While the Java side of the coding is better than LotusScript, it's still fairly crappy (why can't you debug INTO Java code?? Why do you need to re-import JAR files for each Java class, does each class have a different CLASSPATH???). Possibly this was improved in Notes 8, I hear it's based on Eclipse. Does anyone know whether this is true or not?
It would help to hear more specifics about the 'circumstances', but the most common way to handle this is to use a hide when formula on the field you want to enable/disable.
Technically you are not enabling or disabling the field, just hiding it, but usually that works just as well.
Since there are few events to work with in Notes, developers commonly use the document refresh as the 'event' to cause the field to hide or show.
Let's assume you have two fields called TriggerField and Subject. Say also you want to disable the Subject based on a value in the TriggerField. The easiest way to do so is to set the TriggerField as a Dialog List type and check the "Refresh fields on keyword change" option. This means when the value of the dialog list changes, the entire document will get refreshed.
Then in your hide when formula for the Subject field, you specify your criteria for when to show or hide that field. Anytime field values change, followed by a refresh of the document (i.e. form), that hide when formula will be re-evaluated.
There are other ways, depending on your circumstances, to solve this problem. If you want to let the user refresh the form themselves, put a button on the form that calls the #Command([ViewRefreshFields]) command. You can add any other formulas to that button before the refresh command if you want to make other changes to the form at the same time.
Another option is to make a certain field display-only. Then create a button that runs LotusScript to allow users to change that display-only field. In the script you can propmt the user for a value, set the display-only field, and then call for a document refresh.
In ND7 and up if you want to just disable the field for input, write an appropriate formula in the InputEnabled section of the field you want to disable.
So I have two fields one called Trigger, a checkbox with the value "On" and another Subject that is a text field. When Trigger is checked I want the value Subject to be enabled.
I simply put the following formula in the Input Enabled element of the field Subject:
Trigger = "On"
I also want this to be recalculated whenever the value of Trigger changes so I select the "Refresh fields on keyword change" option on the Trigger field.
If you're stuck in an older version you need to to hide paragraphs appropriately.

Resources