Pieter van der Westhuizen

How to add custom dialogs in WiX installers

The WiX toolset provides a number of built-in dialogs that should be adequate for most installers. If, however, you need more flexibility when it comes to your MSI installer’s user interface, WiX provides the ability to build a custom UI of your setup project.

In this article, we’ll build a custom WiX setup project that will use the modern/metro style look and feel.

Our installation will start with an introduction dialog:

When the user clicks on the “Install Now” button, they will be prompted to enter their full name and email address.

Lastly, we’ll show a dialog to indicate the progress of the installation:

The WiX setup project

We’ll assume we already have an application for which we need an installer and only focus on creating the setup project. Start by creating a new WiX Setup project in Visual Studio:

The project wizard will create the initial Product.wxs WiX source file. For now we’ll only add a Feature element and a Fragment element that contains the product components. Change the mark-up in the file to the following:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="Win App" Language="1033" Version="1.0.0.0" Manufacturer="Silly Software Inc." UpgradeCode="5b0d1312-348e-492c-9447-de720c151cd8">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
 
    <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    <MediaTemplate EmbedCab="yes" />
 
    <Feature Id="ProductFeature" Title="WinAppSetup" Level="1">
      <ComponentGroupRef Id="ProductComponents" />
    </Feature>
 
    <UIRef Id="SetupDialogUI" />
 
    <Binary Id="bgPic" SourceFile="images/bg.bmp"/>
    <Binary Id="cancelbtn" SourceFile="images/cancelbtn.bmp"/>
    <Property Id="Cancel">cancelbtn</Property>
 
    <InstallExecuteSequence>
      <Custom Action='RegistrationInfoCustomAction' Before='InstallFinalize'>NOT Installed</Custom>
    </InstallExecuteSequence>
 
  </Product>
 
  <Fragment>
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLFOLDER" Name="WinApp" />
      </Directory>
    </Directory>
  </Fragment>
 
  <Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
      <Component Id="Executable" Guid="3252A379-9249-4DB4-9F7F-C6A10F68F88A">
        <File Id="WinAppExe" Name="WinApp.exe" Source="..WinAppbinDebugWinapp.exe" Vital="yes" />
        <RemoveFolder Id="INSTALLDIR" On="uninstall" />
      </Component>
      <Component Id="Documentation" Guid="A70DAF37-0EAC-49BD-A8B0-2C5D8AF81085">
        <File Id="ReadMeTxt" Name="ReadMe.txt" Source="..WinAppbinDebugReadMe.txt" Vital="yes" />
      </Component>
    </ComponentGroup>
  </Fragment>
 
</Wix>

Notice, the UIRef, Binary, Property and Custom Action elements.

Creating the first setup dialog

Next, we need to add a new WiX Installer File to the project. This file will be used to create the first dialog in the setup process.

Change the SetupDialog.wxs Installer file’s mark-up to the following:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <UI Id="SetupDialogUI">
 
      <Property Id="Install">installbtn</Property>
      <Binary Id="installbtn" SourceFile="images/installbtn.bmp"/>
 
      <DialogRef Id="ProgressDialog"/>
 
      <TextStyle Id="TahomaHeader" FaceName="Tahoma" Size="18" Bold="yes" />
      <TextStyle Id="TahomaNormal" FaceName="Tahoma" Size="8" />
      <Property Id="DefaultUIFont" Value="TahomaNormal" />
 
      <Dialog Id="SetupDialog" Width="400" Height="300" Title="Silly Software">
 
        <Control Id="background" Type="Bitmap" Text="bgPic" Height="300" Width="400" X="0" Y="0" TabSkip="no" />
 
        <Control Id="introText"  Type="Text" X="75" Y="50" Width="350" Height="22" Transparent="yes" Text="{TahomaHeader}Welcome to Metro App setup." />
        <Control Id="explanationText" X="85" Y="100" NoWrap="no" RightAligned="no" Transparent="yes" Type="Text" Width="250" Height="100" Text="{TahomaNormal}To continue with the setup click on the Install button. If you choose not to install this application, click on the Cancel button to exit." />
 
        <Control Id="installButton" Type="PushButton" Text="[Install]" Height="62" Width="222" X="90" Y="180" Bitmap="yes">
          <Publish Event="EndDialog" Value="Return" />
        </Control>
 
        <Control Id="cancelButton" Type="PushButton" Text="[Cancel]" Height="40" Width="144" X="135" Y="245" Cancel="yes" Bitmap="yes">
          <Publish Event="EndDialog" Value="Exit" />
        </Control>
 
      </Dialog>
 
    </UI>
 
    <InstallUISequence>
      <Show Dialog="SetupDialog" Before="ExecuteAction" />
    </InstallUISequence>
  </Fragment>
</Wix>

You’ll notice that we set the UI element’s Id attribute to SetupDialogUI; this is the value we’ve specified in the UIRef Element of the Product.wxs file. Next, we have a Binary element that contains a reference to an image file in our setup project:

We’ll use this image for a button that the user will have to click to start the installation process. Next, we specify the fonts that our custom dialog will use by adding two TextStyle elements. The Dialog element is used to “build” your UI. The Height and Width attributes are required and are used to set the size of the dialog.

The Dialog element contains a number of Control elements which make up the dialog UI. The following control uses the Binary element we’ve included in the Product.wxs file to set a white background for our dialog:

<Control Id="background" Type="Bitmap" Text="bgPic" Height="300" Width="400" X="0" Y="0" TabSkip="no" />

To specify which font to use for a Text control, add the TextStyle property to the controls’ Text attribute as illustrated below:

<Control Id="introText"  Type="Text" X="75" Y="50" Width="350" Height="22" Transparent="yes" Text="{TahomaHeader}Welcome to Metro App setup." />

The install button is referenced in markup in the following manner:

<Control Id="installButton" Type="PushButton" Text="[Install]" Height="62" Width="222" X="90" Y="180" Bitmap="yes">
  <Publish Event="EndDialog" Value="Return" />
</Control>

You have to set the Bitmap attribute to Yes and set the Text attribute equal to the Id of the property that contains a reference to the Binary element, which in turn contains a reference to the image to use for the button e.g.:

<Property Id="Install">installbtn</Property>
<Binary Id="installbtn" SourceFile="images/installbtn.bmp"/>

Gathering user information

In this example, we would like the user to enter his name and e-mail address as part of the setup experience. To do this, we’ll add another Installer File to our project and call it UserRegDialog.wxs. Now, change its mark-up to the following:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <UI Id="UserRegDialogUI">
 
      <Property Id="Proceed">proceedbtn</Property>
      <Binary Id="proceedbtn" SourceFile="images/proceedbtn.bmp"/>
      <Binary Id="headerPic" SourceFile="images/header.bmp"/>
 
      <Dialog Id="UserRegDialog" Width="400" Height="300" Title="Silly Software : User Registration">
 
        <Control Id="background" Type="Bitmap" Text="bgPic" Height="300" Width="400" X="0" Y="0" TabSkip="no" />
        <Control Id="header" Type="Bitmap" Text="headerPic" Height="50" Width="400" X="0" Y="0" TabSkip="no" />
        <Control Id="headerText"  Type="Text" X="65" Y="10" Width="350" Height="40" Transparent="yes" Text="{TahomaBig}User Registration" />
 
        <Control Id="explanationText" X="85" Y="75" NoWrap="no" RightAligned="no" Transparent="yes" Type="Text" Width="250" Height="100" Text="{TahomaNormal}Before you can use this awesome product, you need to register. If you choose not to install this application, click on the Cancel button to exit." />
 
        <Control Id="nameLabel" Type="Text" X="85" Y="120" Height="17" Width="65" Transparent="yes" Text="{TahomaNormal}Your Full Name:" />
        <Control Id="nameTextbox" Type="Edit" X="150" Y="117"  Height="17" Width="120" Property="FULLNAMEProperty"  />
 
        <Control Id="emailLabel" Type="Text" X="85" Y="140" Height="17" Width="65" Transparent="yes" Text="{TahomaNormal}Your E-mail:" />
        <Control Id="emailTextbox" Type="Edit" X="150" Y="137"  Height="17" Width="120" Property="EMAILProperty"  />
 
        <Control Id="proceedButton" Type="PushButton" Text="[Proceed]" Height="62" Width="222" X="90" Y="180" Bitmap="yes">
          <Publish Event="DoAction" Value="RegistrationInfoCustomAction">1</Publish>
          <Publish Event="EndDialog" Value="Return">1</Publish>
        </Control>
 
        <Control Id="cancelButton" Type="PushButton" Text="[Cancel]" Height="40" Width="144" X="135" Y="245" Cancel="yes" Bitmap="yes">
          <Publish Event="EndDialog" Value="Exit" />
        </Control>
 
      </Dialog>
 
    </UI>
 
    <InstallUISequence>
      <Show Dialog="UserRegDialog" After="SetupDialog" />
    </InstallUISequence>
 
  </Fragment>
 
  <Fragment>
    <Binary Id="CustomActionBinary" SourceFile="$(var.RegistrationInfoCustomAction.TargetDir)$(var.RegistrationInfoCustomAction.TargetName).CA.dll"/>
    <CustomAction Id="RegistrationInfoCustomAction" BinaryKey="CustomActionBinary" DllEntry="SaveUserInfo"  />
  </Fragment>
 
</Wix>

The mark-up is pretty straight forward, but the most import bit which we’ll need to pay attention to is the proceedButton Control element. You’ll notice it contains a child Publish element whose Event attribute is set to DoAction and its Value attribute is set to the Id of the CustomAction element further down in the file. This will cause the custom action to execute as soon as the user clicks the Proceed button on the UI.

The custom action will use the emailTextbox and nameTexbox control elements’ Property attribute values to save the information the user entered.

Creating the custom action

The last step will be to create a custom action that will save the user’s registration information to a text file. Add a new C# Custom Action Project to your Visual Studio solution:

In the CustomAction.cs file, change the CustomAction1 methods’ name to SaveUserInfo and add the following code to it:

[CustomAction]
 public static ActionResult SaveUserInfo(Session session)
 {
     string name = session["FULLNAMEProperty"];
     string email = session["EMAILProperty"];
 
     string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
     if (!Directory.Exists(appdataPath + "Win App"))
         Directory.CreateDirectory(appdataPath + "Win App");
 
     File.WriteAllText(appdataPath + "Win AppregistrationInfo.txt", name + "," + email);
 
     return ActionResult.Success;
 }

The above code uses the values the user entered during setup and saves it to a text file. The two controls are referenced by the values contained in their Property attributes, e.g.:

<Control Id="nameTextbox" Type="Edit" X="150" Y="117"  Height="17" Width="120" Property="FULLNAMEProperty"  />
<Control Id="emailTextbox" Type="Edit" X="150" Y="137"  Height="17" Width="120" Property="EMAILProperty"  />

You can now build your setup project and will see a registrattioninfo.txt file in the C:Users<username>AppDataRoamingWin App folder after installation has completed.

Thank you for reading. Until next time, keep coding!

Available downloads:

Custom WiX UI project

You may also be interested in:

2 Comments

Post a comment

Have any questions? Ask us right now!