By Cary Roys
Anyone that’s developed a complex UI for an installer is probably aware that Windows Installer can be fairly limited in what you can accomplish with dialogs. However, there’s a lot of tools that Microsoft provides buried in the MSDN documentation that solve a lot of these issues as well.
You see, a lot of setup developers will choose to go the scripted approach for dialogs, either in an InstallScript MSI project, or by rolling their own dialogs with custom actions. But it need not always be that way; the more custom actions your setup contains, the more points of failure you are introducing. There are a great many things you can accomplish while adhering to the features and limitations of Windows Installer dialogs. What I’d like to talk about today is authoring a dialog that allows for Mutually Exclusive Feature selections without resorting to something other than a SelectionTree control or script code.
Consider the following Old Engineering Joke, implemented as a Feature Selection dialog:
Obviously, we are trying to express the idea that only two of the three features may be selected to enable the ‘Next’ button in the installation, as we can see from the screenshot. And any other compromise involving all features selected at once is impossible. How can we get to this point, though? You might be aware of the mechanism for enabling/disabling controls, which is to put a condition on them to Enable/Disable/Show/Hide. This is located in the Behavior section of a dialog, and on the ‘Conditions’ tab:
The Conditions are the next bit that’s useful in this case. We can use conditions to check against Feature selections in a SelectionTree in MSI dialogs because the SelectionTree immediately changes the Request State of the associated feature. For some good reading on the various syntax options involved, please see Conditional Statement Syntax. But, basically, using the Ampersand next to a feature name, and making a comparison against an action state of ‘Local’ is what’s desirable in this case, which is a value of 3. So, the following expression evaluates to ‘True’ when the feature is selected to be installed locally and ‘False’ when anything but being installed locally:
&Fast = 3
It’s worth noting that you can condition against ‘Absent’, which is a value of 2. However, if no selection has been made against a feature, this doesn’t evaluate properly, instead being ‘Unknown’ which is -1. So for the purpose of this example we’re only going to be using syntax like:
&Fast = 3
NOT &Fast = 3
Now that we know how we can Enable/Disable the ‘Next’ button, you may have noticed that it doesn’t always update properly even though your conditions are correct. This is because there needs to be an event that fires to tell the ‘Next’ button to reevaluate its conditions. In this case, an MSI ControlEvent that is published from another control.
In MSI, there is the concept of ‘Subscriptions’, which handle just this: listening for a particular ControlEvent to be published so they can update themselves. This is actually how the Progress dialog during installation updates its status messages and progress bars. Different types of controls are capable of publishing different types of ControlEvent messages, as well. As it stands, MSDN tells us that a SelectionTree can publish ‘DoAction’ (running a standard or custom action) or a SetProperty ControlEvent (sets a Property to some value). So we need to both publish one of these actions, and subscribe our ‘Next’ button to it.
I’ve chosen to use a SetProperty event to subscribe to below. You’ll have to manually type this event name on the Subscriptions tab and select the ‘Enabled’ attribute to be updated:
In addition to having to type this manually, it is worth being aware that your dialog is not going to function correctly on systems that have Windows Installer 3.0 or earlier. Therefore, it’s recommended that you include a newer version of Windows Installer in your installation to account for this scenario.
Our (almost) last piece here is setting a property from the SelectionTree control. What property do we set? It doesn’t matter. It just needs to be set every time you interact with the SelectionTree control*:
*Strictly speaking, the dialog will work without this step, as a SelectionTree control will set some properties that describe feature selections and actions (MsiSelectionTree* properties), and thus publishing our SetProperty Control Event. However, this is a useful illustration for when you have a different control type that perhaps doesn’t natively publish such an event, and you still need to trigger an update of other controls
The only remaining thing at this point is setting up a series of conditions to turn on and off the ‘Next’ button. I’ve got specific checks in place for each scenario to Enable the ‘Next’ button (i.e. 2 features selected, remaining feature not selected), and 4 sets of conditions that detect All features selected, No features selected, or just One of the three features selected:
And now at this point, we have a dialog that properly implements our engineering joke:
You can find the project file here, if you’d like to tinker with this yourself.