Welcome back to my series of posts on the WiX installer technologies. Previously we've looked at the basics of using WiX and Visual Studio, and learnt how to use packaged project output in your installers. This time we'll move onto the user interface side of the equation and discover how to allow your end users to begin customizing their installs. We'll do all this with Wix's custom UI functionality which will better prepare you for the more complex implementations we'll be using in future articles.

We'll be using the same application as in the last 2 posts. You can find the code to start from here: WixInstallerExample-02.zip (617.77 kb).

Preparing For A User Interface

The first step is to import the correct WiX extensions to start playing with the UI. Add a reference to WixUIExtension.dll from the bin folder inside your WiX install; this will pull in all the required UI templates. Next create a new wxs file in your installer project called UserInterface.wxs - this will contain all our custom UI coding. Inside the Fragment tag place the following:

<UIRef Id="WixUI_Common" />

This will get us a reference to all the common dialogs we need. Next add in the following:

<UI Id="MyCustomUI">
  <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
  <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
  <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

  <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />

  <DialogRef Id="ErrorDlg" />
  <DialogRef Id="FatalError" />
  <DialogRef Id="FilesInUse" />
  <DialogRef Id="UserExit" />
</UI>

The main UI tag here will contain all our custom UI stuff. The first section with the TextStyle and Property nodes is just setting up some font settings and the DialogRef tags give us some references to standard dialogs for handling errors and early exits from the dialog sequence.

That's the basic setup done - now we need to think about the dialogs we want and how control will flow between them.

Dialogs and Flow

There are two main entry points to any installer - a welcome for new installs, or a welcome for changing a current install. We also want a licensing dialog and a way for the user to specify the install location. Our flow will then look something like this:

Luckily, WiX has dialogs included which can perform all these actions; all we have to do is specify how the control flows between them. To do this add the following right after your DialogRef tags:

<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog"
  Value="LicenseAgreementDlg">NOT Installed</Publish>

<Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog"
  Value="WelcomeDlg">1</Publish>
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog"
  Value="InstallDirDlg">LicenseAccepted = "1"</Publish>

<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog"
  Value="LicenseAgreementDlg">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath"
  Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction"
  Value="WixUIValidatePath" Order="2">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog"
  Value="InvalidDirDlg" Order="3"
  ><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog"
  Value="VerifyReadyDlg" Order="4">WIXUI_INSTALLDIR_VALID="1"</Publish>
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty"
  Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog"
  Value="BrowseDlg" Order="2">1</Publish>

<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction"
  Value="WixUIValidatePath" Order="3">1</Publish>
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog"
  Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>

To reiterate: this code is simply the rules for how we move from one dialog to the other. All of these Publish nodes have common attributes:

  • Dialog - the dialog screen we are adding a rule to;
  • Control - the control on the dialog we are adding a rule to;
  • Event - the event to handle;
  • Value - the action to take; depending on the event this could be the name of a dialog to move to, a reference to a variable to be updated or a new event to call.

In addition, the content of the Publish node is a condition for it to be executed. A condition of 1 will always run. Let's run through this flow:

  1. We start with the WelcomeDlg and, as long as the application is not installed, when the Next button is pushed we open the LicenseAgreementDlg;
  2. When Back is pushed on LicenceAgreementDlg we always go back to WelcomeDlg, or when Next is pushed we go to the InstallDirDlg as long as the agreement has been accepted;
  3. InstallDirDlg is the complicated one, simply it:
    1. Always lets you go back;
    2. When you push browse (ChangeFolder) it sets the start location for the browse to the value of the WIXUI_INSTALLDIR variable and spawns the BrowseDlg;
    3. When you push next it sets the target path to WIXUI_INSTALLDIR then validates it and either spawns an invalid path message or lets you continue to the VerifyReadyDlg.
  4. BrowseDlg validates the selected path when you click OK.

 

Following me so far?

We've defined the left hand column of dialogs from the diagram above, and done all the hard stuff. We'll add the maintenance dialogs next - put this code right after the previous Publish nodes:

<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog"
  Value="MaintenanceTypeDlg">1</Publish>

<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog"
  Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog"
  Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog"
  Value="MaintenanceWelcomeDlg">1</Publish>

<!-- Greys out the 'Change' button on MaintenanceTypeDlg -->
<Property Id="ARPNOMODIFY" Value="1" />

This is much simpler than the new install sequence - the MaintenanceWelcomeDlg will always move to MaintenanceTypeDlg which will always go straight to the VerifyReadyDlg. The APPNOMODIFY property will grey out the modify button on the MaintenanceTypeDlg as we don't have any optional components which could be modified. Notice that nowhere in all of this have we specified a start screen - WiX is smart enough that it will load either WelcomeDlg or MaintenanceWelcomeDlg depending on weather the application is already installed.

The only thing missing from the UI now is handling the back button from VerifyReadyDlg and early exits from the installer:

<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog"
  Value="InstallDirDlg" Order="1">NOT Installed</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog"
  Value="MaintenanceTypeDlg" Order="2">Installed</Publish>

<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog"
  Value="Return" Order="999">1</Publish>

You should be able to figure this out with little trouble at this point.

That's it for the UI setup, all that remains is to tell WiX to use our interface.

Bringing It All Together

Specifying the UI to use is a simple task - back in the Product.wxs file add the following after the Feature tag:

<Property Id="WIXUI_INSTALLDIR">INSTALLLOCATION</Property>
<UIRef Id="MyCustomUI" />

This maps the WIXUI_INSTALLDIR variable used in the UI to the INSTALLLOCATION we use in the Directory setup and ensures that WebRoot\WixInstallerExampleWeb is the default. The UIRef node simply tells WiX to use the custom UI we defined.

There is still one final task - if you try to build now you'll get an error because WiX can't find EULA.rtf. Create one and add it to the root of the installer project.

The project should now build fine and you'll be able to see the various dialogs:

 

If you run the installer again you'll be able to repair or remove the install.

Why?

Now you've done all that hard work, a confession. WiX supplies a UI which does almost exactly this and which could have been added with 2 lines of code, so why have we gone to the trouble of creating it ourselves? Well for almost any scenario more complex than choosing a location on disk to install files to, you'll find yourself needing to use a custom UI, when doing so it will pay to already be familiar with how this stuff works.

That's it once again, but in part 4 we'll take a look at adding a custom dialog page to the installer which will add IIS integration, and in part 5 we'll go back and look again at Heat.exe to see how we can specify permissions on a directory. If you'd like to be notified when the next article is published you can subscribe to my RSS feed.

Source Code

Here is the full source code for this example: WixInstallerExample-03.zip (731.69 kb)