Merlinia logo
Yet Another WiX Tutorial - Part 2 - Adding the UI

Adding a user interface

Now we add the user interface (UI) to the install process, i.e., a set of dialog boxes.

WiX (and Windows Installer) allows you to specify almost any combination and sequence of dialog boxes you can imagine, but it is easiest if you start with one of the four WiX pre-defined sets of user interface dialog boxes: WixUI_Mondo, WixUI_FeatureTree, WixUI_InstallDir, or WixUI_Minimal. See here for more information:
http://www.tramontana.co.hu/wix/lesson2.php#2.3

For the Assistance Admin example we'll use WixUI_InstallDir set as our starting point.

Add these lines to the WXS file, near the end of the file, between the <Icon> element and the </Product> tag:

    <!-- Include a user interface - to be modified later -->
    <Property Id="WIXUI_INSTALLDIR">INSTALLDIR</Property>
    <UIRef Id="WixUI_InstallDir" />
    <UIRef Id="WixUI_ErrorProgressText" />

For this to work we need to add the WiX dialog library to the project. In the SharpDevelop Project Browser select "WiX Libraries", right click and select Add Wix Library. Then add WixUI.wixlib to the project. This is typically in "C:\Program Files\SharpDevelop\2.1\bin\Tools\Wix".

 

Languages and localization

One of the great things about WiX is that it makes it easy to "localize" everything. All of the default dialog boxes provided with WiX are localized, i.e., all texts are specified via localization variables, $(loc.xxxxxxx).

The localized texts are defined in "localization libraries", which are simply XML text files containing a large number of <String Id= > elements. Some of these files (German, US English, Spanish, Hungarian and Dutch) are distributed as part of WiX 2.0, and many others are available for download from this web page: http://www.tramontana.co.hu/wix/loc/index.php

To continue with this tutorial you'll need to specify which language you'll be using for test purposes. (Later, when you're making your final MSI files, you'll use all of the languages that are relevant for you.) You add the selected WiX "localization library" to your SharpDevelop project as follows: Select Project - Project Options - Application, and in the "Localized string file" field click on the browse button and find and select the file WixUI_en-us.wxl, typically in the same folder as the library file. (WixUI_en-us.wxl is the localization file for English - USA texts.)

 

At this point it should be possible to test the intallation package again. It now includes dialog boxes that welcome you, require you to accept a license agreement, and allow you to change the intallation folder path.

Beautiful!

 

Here's a snaphot of what the UI looks like at this point: Admin UI snapshot 2.1.htm

 

Understanding the user interface

We want to make some changes to the user interface, but first we need to understand how the WiX user interface is implemented.

To be able to make changes we first need to download the source code for WiX 2.0.4820. This can be found here: http://sourceforge.net/project/showfiles.php?group_id=105970

(Newer versions of WiX 2.0 are available, but I don't know if they are supposed to be as stable as the 2.0.4820 version.)

We place these files in D:\WiX\wix-2.0.4820.0-sources.

The files that define the user interface are in D:\WiX\wix-2.0.4820.0-sources\src\ui\wixui.

The user interface consists of the following five components:

1. The dialog boxes themselves. These are defined in files such as WelcomeDlg.wxs, LicenseAgreementDlg.wxs, etc. There are 26 of these files. (A few of these files, such as Common.wxs and ErrorProgressText.wxs, are not really dialog boxes, but for the purpose of this tutorial I'll simply refer to "the 26 dialog box files".)

2. The four files that determine the number and sequence of dialog boxes that the user will see. These are defined in individual folders one level down from the other files for some reason, and are called WixUI_FeatureTree.wxs, WixUI_InstallDir.wxs, WixUI_Minimal.wxs, and WixUI_Mondo.wxs.

3. The localized texts that are placed in the dialog boxes. These are defined in files such as WixUI_en-us.wxl, WixUI_de-de.wxl, etc.

4. The six bitmaps for the graphics that are used in the dialog boxes. These are defined as BMP files in folder \Bitmaps.

5. The license text, which is defined in file License.rtf.

All of the WXS files (26 + 4 = 30 of them) from points 1. and 2. above are linked together in the file WixUI.wixlib that was used in our previous step.

The "<UIRef Id="WixUI_InstallDir" />" element that we added to our WXS file in the previous step somehow results in selecting the correct one of the four sequencing files. This in turn selects the set of dialog boxes we want displayed when the install is run, even though the definitions for all 20 or so of the pre-defined dialog boxes are actually present in the MSI file.

 

Preparing to insert additional dialog boxes

We want to add two additional dialog boxes to the user interface. This requires that we modify the source code for a couple of the existing dialog box files, so we'll delay that leap of faith until the next step. (And even then we'll add the new dialog boxes one at a time. Slowly, but surely...)

Our objective in this step is fairly limited. We will create a new SharpDevelop project that builds the existing WiX user interface from the source. We will then switch from using the WixUI.wixlib that is in the WiX binary distribution, and instead use the corresponding library that we create ourselves from the source files. ("Use the source, Luke.")

At this point we are not trying to make any changes to the dialog boxes, so the result of this step will be an MSI that shows the exact same behavior and functionality as we had when we first added the user interface two steps ago.

 

First start a new copy of SharpDevelop and create a new SharpDevelop solution and project, and a folder to contain them. Select File - New - Solution - Setup, and select "Empty Setup" as the template. Location = "D:\Developer\Assistance\Trunk\WiX projects", Name = "Custom Dialogs", and check the "Auto create project subdir" option is on.

Open up the project in the project browser and delete the Setup.wxs file.

Check that the newly-created TestDialogs folder contains only an SLN and a WIXPROJ file.

 

Now we get some of the WiX source code files for the UI. (See previous step regarding download of the WiX source files.)

Copy the 26 WXS files in D:\WiX\wix-2.0.4820.0-sources\src\ui\wixui to the new "D:\Developer\Assistance\Trunk\WiX projects\Custom Dialogs" folder. (Don't copy the five language files or the License.rtf file just yet.)

Add one more file, the single file that controls the sequencing of the dialog boxes. For our example this is WixUI_InstallDir.wxs from the \installdir folder. Copy this file to the "D:\Developer\Assistance\Trunk\WiX projects\Custom dialogs" folder. (I prefer to not place this file in a folder by itself.) If you are using one of the other three corresponding files in your case then copy it instead.

 

There should now be a total of 29 files in "D:\Developer\Assistance\Trunk\WiX projects\Custom dialogs".

In the SharpDevelop Project Browser, right-click on the project name and select Add - Existing item, and then add the 27 WXS files (26 dialog box files + WixUI_InstallDir.wxs) that are in the "D:\Developer\Assistance\Trunk\WiX projects\Custom dialogs" folder.

Select Project - Project Options - Application, and set Output type to WiX Library.

As with the other project, we assume there is no difference between doing "debug builds" and "release builds" for WiX projects, and we want to reduce the folder clutter, so we remove the extra folder level \Debug, both for \obj and \bin. Do this by selecting Project - Project Options - Compiling. Change the output path from "bin\Debug\" to just "bin\". Set the "Intermediate Output Path" to "obj\" instead of it being blank.

 

Now build our "Custom Dialogs.wixlib" file by selecting Build - Build Solution.

The resulting CustomDialogs.wixlib (which can be found under \bin) is 208 KB, slightly smaller than the original wixui.wixlib file (216 KB) because we omitted the three alternative WXS files that define alternative sets and sequences of dialog boxes.

Now we're almost ready to test to see if the version of the WiX UI that we have built works as well as the version that comes with the WiX binary download.

But first we need to copy some additional files to our private location where the "Custom Dialogs.wixlib" file is located.

Copy the License.rtf file and the six language localization files from D:\WiX\wix-2.0.4820.0-sources\src\ui\wixui to "D:\Developer\Assistance\Trunk\WiX projects\Custom dialogs\bin".

We also need the six BMP files locally, so copy the D:\WiX\wix-2.0.4820.0-sources\src\ui\wixui\Bitmaps folder and its contents to "D:\Developer\Assistance\Trunk\WiX projects\Custom dialogs\bin".

Finally, there's a file called wixca.dll (something about "custom actions"?) that needs to be copied to the "D:\Developer\Assistance\Trunk\WiX projects\Custom dialogs\bin" folder. This file can be found in the WiX binary download or in the SharpDevelop copy of the WiX binary files, for example here: "C:\Program Files\SharpDevelop\2.1\bin\Tools\Wix".

 

Now we're ready to test.

Switch to the copy of SharpDevelop with the Assistance Admin solution/project that we were working on until a couple of steps ago. In the Project Browser window, delete the wixui library. Then right-click on "WiX Libraries" and add the "Custom Dialogs.wixlib" library that we've just created.

Also, because we'll be changing the localization texts a bit later, switch to our own private copy of the WixUI_en-us.wxl file by selecting Project - Project Options - Application, and in the "Localized string file" field click on the browse button and switch to our own copy of WixUI_en-us.wxl.

 

Now do a build and test the result, for example by hitting F5.

This install package should function identically to the one we had two steps ago.

 

Inserting an additional (non-functioning) dialog box

This is where things start to get VERY messy. :-(

We want to insert an additional dialog box that allows the user to specify two things:
- whether this is a "per-user" or an "all-users" install
- which of the three shortcuts we defined in part 1 are to be created

(Strangely enough, none of the 20-odd "standard" dialog boxes provided with WiX offers this functionality. Perhaps because this functionality is not implemented exclusively in the UI but also partially in the main project.)

 

To start with we'll just insert an additional dialog box into the UI, and not care that it doesn't do anything. We'll add the functionality in the next two steps.

 

Switch to the copy of SharpDevelop containing our Custom Dialogs project.

First we need to create a new WXS file for the new dialog box.

We look around for an existing dialog box that at least slightly resembles what we want, so we won't have to start from scratch. In this case the best choice as far as I can see is to use the InstallDirDlg.wxs dialog box, and remove almost all of the contents. (The advantage of using this dialog box is that it at least has the Back, Next and Cancel buttons at the bottom.)

 

So we make a copy of the InstallDirDlg.wxs dialog file and call it AssistanceOptionsDlg.wxs, and add it to the SharpDevelop project.

Open AssistanceOptionsDlg.wxs for editing, and make the necessary changes to make it an almost-empty dialog box that can co-exist with the other dialog boxes.

1. Remove the three controls FolderLabel, PathEdit and Browse, including the Publish elements inside the Browse control.

2. Remove the following two lines which are not relevant for this new dialog box:

    <PropertyRef Id="WIXUI_INSTALLDIR" />

    <Publish Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]">1</Publish>

  (But leave the other Publish element in the Next button alone.)

3. Replace all occurances of "InstallDirDlg" with "AssistanceOptionsDlg".

 

We observe strict localization policies for everything we do, so we have to add some lines to the WixUI_en-us.wxl file:

  <String Id="AssistanceOptionsDlg_Title">[ProductName] Setup</String>
  <String Id="AssistanceOptionsDlgTitle">{\WixUI_Font_Title}Installation Settings</String>
  <String Id="AssistanceOptionsDlgDescription">Select current user or all-users install, and selection of shortcuts.</String>
  <String Id="AssistanceOptionsDlgBannerBitmap">WixUI_Bmp_Banner</String>

These lines should also be added (translated) to the other language files if/when necessary, so the dialog box will be properly localized.

 

To modify the sequencing of the dialog boxes we must replace the WixUI_InstallDir.wxs file with a different file that controls the new sequencing of dialog boxes. Make a copy of this file and rename the copy to WixUI_AssistanceAdmin.wxs. This file must then be added to the SharpDevelop project, and the WixUI_InstallDir.wxs file can be removed from the project.

Make the following changes to WixUI_AssistanceAdmin.wxs:

1. Add AssistanceOptionsDlg to the comments at the start so they say this:

   <!--
    First-time install dialog sequence:      Maintenance dialog sequence:
    - WixUI_WelcomeDlg                  - WixUI_MaintenanceWelcomeDlg
    - WixUI_LicenseAgreementDlg      - WixUI_MaintenanceTypeDlg
    - WixUI_InstallDirDlg                  - WixUI_InstallDirDlg
    - WixUI_AssistanceOptionsDlg      - WixUI_VerifyReadyDlg
      - WixUI_VerifyReadyDlg
      - WixUI_DiskCostDlg
     -->

2. Modify a couple of lines to show that this is a different sequence:

   <UI Id="WixUI_AssistanceAdmin">

   <Property Id="WixUI_Mode" Value="AssistanceAdmin" />

3. Add AssistanceOptionsDlg to the <DialogRef> elements, i.e.:

    <DialogRef Id="AssistanceOptionsDlg" />

4. Add two new lines for the AssistanceOptionsDlg dialog box to specify its Back and Next button navigation, and modify the Next value for the preceding dialog box and the Back value for the following dialog box:

    <Property Id="WixUI_InstallDirDlg_Back" Value="LicenseAgreementDlg" />
    <Property Id="WixUI_InstallDirDlg_Next" Value="AssistanceOptionsDlg" />
    <Property Id="WixUI_InstallDirDlg_Browse" Value="BrowseDlg" />

    <Property Id="WixUI_AssistanceOptionsDlg_Back" Value="InstallDirDlg" />
    <Property Id="WixUI_AssistanceOptionsDlg_Next" Value="VerifyReadyDlg" />

    <Property Id="WixUI_VerifyReadyDlg_BackRepair" Value="MaintenanceTypeDlg" />
    <Property Id="WixUI_VerifyReadyDlg_BackRemove" Value="MaintenanceTypeDlg" />
    <Property Id="WixUI_VerifyReadyDlg_BackAssistanceAdmin" Value="AssistanceOptionsDlg" />

 

As has perhaps been noticed, the Back button in the VerifyReadyDlg dialog box "publishes" different actions depending on which sequence of dialog boxes is being used. So we have to edit the VerifyReadyDlg.wxs file, and in the control for the Back button add the following line:

  <Publish Event="NewDialog"
  Value="[WixUI_VerifyReadyDlg_BackAssistanceAdmin]">NOT Installed AND WixUI_Mode = "AssistanceAdmin"</Publish>

Now we can re-build our Custom Dialogs user interface library.

 

Finally, switch to the SharpDevelop project for "Assistance Admin" setup and change the "Assistance Admin.wxs" file to specify the new sequence of dialog boxes:

    <UIRef Id="WixUI_AssistanceAdmin" />

Now when you re-build and test the MSI you should see a new dialog box between the one that prompts for an alternative install directory and the final dialog box.

 

Don't worry about the fact that the new dialog box is more-or-less empty. The important thing is that we succeeded in creating a new dialog box and making it part of the install process sequence of dialog boxes.

 

CONGRATULATIONS! This was a tough step. From here on it gets easier.

 

Here's a snaphot of what the UI looks like at this point: Admin UI snapshot 2.2.htm

Two of the files: AssistanceOptionsDlg.wxs snapshot 2.2.txt, WixUI_AssistanceAdmin.wxs snapshot 2.2.txt

 

Selecting current-user or all-users install

Windows Installer has the basic functionality of doing either a "per user" or "per machine (all users)" install. The default, if nothing is specified, is to install for a single user.

Note however, that an "all-users" install requires that the person doing the install has administrator privileges, and that it can be especially problematic with Windows Vista, and it must not be used for a Citrix client. There are many possibilities for an "all-users" installation to go wrong. (But on the other hand, an all-users install is definitely recommended for server-based programs - we return to this later.)

We want to make the "all-users" install available for those who want it. In this step we'll add a couple of radio buttons to our new dialog box so the users can choose either single-user or all-users.

 

Windows Installer uses a Property called ALLUSERS to control this functionality.

If ALLUSERS is not defined, or is null, then a single-user install is done.

ALLUSERS=1 means, well, "all users", or a per-machine installation. If the user does not have admin rights the install fails.

ALLUSERS=2 tells Windows Installer to see if it's possible to install for all users, do it if so, and if not to install for one user. THIS IS NOT RECOMMENDED. It is especially problematic with Windows Vista, and can also cause problems for later uninstalls and upgrades.

Note that ALLUSERS=0 does not exist, for some strange reason.

Another strange thing to be aware of is that there is no way in WiX to define a Property with a null value using the <Property> element. But this doesn't matter, because it is possible to both define ALLUSERS and set it to a null value with a <Publish> element. The syntax is a bit special (Value="{}") but never mind, it does work.

 

Here's how to add the necessary functionality to our new dialog box:

 

First, just add this line to the "Assistance Admin.wxs" file in the SharpDevelop project that creates the MSI for Assistance Admin:

    <Property Id="ASSISTANCE_USERS">cur</Property>    <!-- cur or all -->

This "property" (whose name must be all-uppercase because it is being manipulated in the UI) is NOT the ALLUSERS property. It is our behind-the-scenes token that we use in conjunction with the radio buttons.

 

In the SharpDevelop project for the user interface, open the the MsiRMFilesInUse.wxs file. Here you can see how radio button controls are defined in WiX. Copy the six lines that define the two radio buttons and the "radio button group" to the AssistanceOptionsDlg.wxs file.

Alternatively, you can experiment with the "design view" of SharpDevelop - just click on Design on the tab below the editing window.

We also need a couple of text controls above the radio buttons to indicate what they are for.

Here's what the definition of the controls should look like when they are properly in place:

  <Control Id="UserText1" Type="Text" X="20" Y="60" Width="330" Height="35"
             Text="$(loc.AssistanceOptionsDlgUserText1)" />
  <Control Id="UserText2" Type="Text" X="20" Y="95" Width="330" Height="20"
             Text="$(loc.AssistanceOptionsDlgUserText2)" />
  <Control Id="UserSelection" Type="RadioButtonGroup"
            X="26" Y="115" Width="305" Height="45" Property="ASSISTANCE_USERS"
            Text="ASSISTANCE_USERS">
    <RadioButtonGroup Property="ASSISTANCE_USERS">
      <RadioButton Value="cur" X="0" Y="0" Width="295" Height="16"
                 Text="$(loc.AssistanceOptionsDlgRBOneUser)" />
      <RadioButton Value="all" X="0" Y="20" Width="295" Height="16"
                 Text="$(loc.AssistanceOptionsDlgRBAllUsers)" />
    </RadioButtonGroup>
  </Control>

Add the following two lines as child elements to the Control element for the Next button:

  <Publish Property="ALLUSERS" Value="{}">ASSISTANCE_USERS = "cur"</Publish> <!-- set null value -->
  <Publish Property="ALLUSERS" Value="1">ASSISTANCE_USERS = "all"</Publish>

These two lines make the necessary translation from our behind-the-scenes property to the ALLUSRS property used by Windows Installer.

 

In WixUI_en-us.wxl add these lines:

  <String Id="AssistanceOptionsDlgUserText1">It may be possible to install this program with shortcuts and registry settings such that the installation will work for all users. This requires that you must have administrator rights. (For Citrix systems use single-user install only.)</String>
  <String Id="AssistanceOptionsDlgUserText2">Install this program for:</String>
  <String Id="AssistanceOptionsDlgRBOneUser">Current user only. ([LogonUser])</String>
  <String Id="AssistanceOptionsDlgRBAllUsers">Anyone who uses this computer.</String>

These lines should also be added (translated) to the other localization files if/when necessary, so the dialog box will be translated.

 

Now we can re-build the user interface, and then re-build the Assistance Admin WiX project and test out the new functionality. It should now be possible to select current-user or all-users installing.

 

Selecting which shortcuts are wanted

Now we complete the new dialog box by adding three checkbox controls allowing the user to select which of the three possible shortcuts he/she wants.

 

Here are the controls that we add to the AssistanceOptionsDlg.wxs dialog box:

  <Control Id="SplitLine" Type="Line" X="0" Y="160" Width="370" Height="0" />
  <Control Id="ShortcutDesktop" Type="CheckBox" Height="18" Width="295" X="40" Y="170"
             Text="$(loc.AssistanceOptionsDlgShortcutDesktop)"
             Property="ASSISTANCE_SHORTCUT_DESKTOP" CheckBoxValue="1" />
  <Control Id="ShortcutProgramMenu" Type="CheckBox" Height="18" Width="295" X="40" Y="190"
             Text="$(loc.AssistanceOptionsDlgShortcutProgramMenu)"
             Property="ASSISTANCE_SHORTCUT_PROGRAMMENU" CheckBoxValue="1" />
  <Control Id="ShortcutStartup" Type="CheckBox" Height="18" Width="295" X="40" Y="210"
             Text="$(loc.AssistanceOptionsDlgShortcutStartup)"
             Property="ASSISTANCE_SHORTCUT_STARTUP" CheckBoxValue="1" />

 

As usual, all text is localized, so here are the lines to be added to WixUI_en-us.wxl (and, translated, to any other relevant localization files):

  <String Id="AssistanceOptionsDlgShortcutDesktop">Create a shortcut on the desktop.</String>
  <String Id="AssistanceOptionsDlgShortcutProgramMenu">Create a shortcut at Start - Programs - Merlinia.</String>
  <String Id="AssistanceOptionsDlgShortcutStartup">Create a shortcut at Start - Programs - Startup. (Provides automatic start).</String>

 

Now, in the other SharpDevelop project, make the following additions to the "Assistance Admin.wxs" file:

  <Property Id="ASSISTANCE_SHORTCUT_DESKTOP">1</Property>
  <Property Id="ASSISTANCE_SHORTCUT_PROGRAMMENU">1</Property>

Note that ASSISTANCE_SHORTCUT_STARTUP is simply not declared. This is the only way to indicate that this option is not wanted by default - attempting to declare it to be zero or null doesn't work.

 

And finally, change the three <Condition> elements that control the Components that contain the <Shortcut> elements.

   <Condition>ASSISTANCE_SHORTCUT_PROGRAMMENU</Condition>

   <Condition>ASSISTANCE_SHORTCUT_STARTUP</Condition>

   <Condition>ASSISTANCE_SHORTCUT_DESKTOP</Condition>

 

Once again we re-build the user interface, and then re-build the Assistance Admin WiX project and test out the new functionality.

Here's a snaphot of what the UI looks like at this point: Admin UI snapshot 2.3.htm

Two of the files: AssistanceOptionsDlg.wxs snapshot 2.3.txt, Assistance Admin.wxs snapshot 2.3.txt

 

This ends part 2 of this tutorial. We now have a working UI. What's more important, it's OUR working UI - we can do whatever we want with it.

In the next part we'll change the graphics and the license agreement, and add yet another dialog box, one that can set various program-specific parameters via registry entries.

 

-- To be continued, in "Part 3 - Extending the UI"

 

Rennie Petersen, March-May 2007

Expand all       Collapse all Copyright 2007 Merlinia - All rights reserved - Terms and conditions