Google Analytics

Monday, February 11, 2013

Adding UI to WiX Installer with Visual Studio 2012


This is a continuation of my last post regarding using Wix 3.7 with Visual Studio 2012. Previously I walked through the Wix tutorial (http://wix.tramontana.co.hu/tutorial/ ) from “Getting Started” through “Putting it to Use”. In this post I want to move into some of the User Interface and adding a custom dialog to the install sequence.

First Steps

The first thing I did was add a Reference from my Wix Installer Project to WixUIExtension library. You can find this in the bin directory of the Wix Toolset installation folder. This will tell the linker (light) to link in that library when you build the installer like running light.exe –ext WixUIExtension Product.wixobj from the command line.
The tutorial had previously broken out the manual into a separate feature, so I broke out the manual into a separate ComponentGroup and broke out the Documentation feature from the MainProgram feature. We can give the user the option to install either or both of these features now. I added Title and Description fields for all the features. The Title is the name of the feature the user will see in the installer treeview. The Description will appear alongside the feature when the user clicks on it. As per the tutorial I also added a Display=”expand” attribute and ConfigurableDirectory=”INSTALLFOLDER” attribute to the Complete Feature. The first will have the tree item expanded by default to show the two component features. The second attribute will allow the user to select an install directory(??).
The tutorial sets the Level of the Documentation to 1000. Since I don’t plan on using the “Mondo” UI, which includes the Typical, Custom and Complete installation options, I don’t think this is really necessary but will leave it. Anything with level 1-3 will be installed in a “Typical” installation. Everything above 3 will be left out of a Typical install but can be installed in the Custom and Complete options.

<Feature Id="Complete" Title='Foobar 1.0' Description='The complete package.'
            Display='expand' Level='1' ConfigurableDirectory='INSTALLFOLDER'>
    <Feature Id='MainProgram' Title='Program' Description='The main executable.' Level='1'>
        <ComponentGroupRef Id="ProductComponents" />
        <ComponentGroupRef Id="Shortcuts" />
    </Feature>
    <Feature Id='Documentation' Title='Documentation' Description='The instruction manual.' Level='1000'>
        <ComponentGroupRef Id='DocumentationComponents' />
    </Feature>
</Feature>
...
<Fragment>
    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="ProgramFilesFolder">
            <Directory Id="INSTALLFOLDER" Name="WixTutorial" />
        </Directory>
        <Directory Id="ProgramMenuFolder" Name="Programs">
            <Directory Id="ProgramMenuDir" Name="Foobar 1.0" />
        </Directory>
        <Directory Id="DesktopFolder" Name="Desktop" />
    </Directory>
</Fragment>

UI Wizardry

The next two lines that I added as children to the Product element are all it takes to add some basic user interface to the installer. If you went through the tramontana tutorial you saw the Mondo set of Wix UI. That contains all the built-in UI features for Wix. The Feature Tree set contains most of those screens but always assumes a Custom installation and allows the user to select the features to be installed from the feature tree. The ErrorProgressText extension uses Wix localized error and progress text rather than the built-in Windows Installer messages.

<UIRef Id="WixUI_FeatureTree" />
<UIRef Id="WixUI_ErrorProgressText" />



In this setup scheme any feature that is not Level One will show as not being installed by default as our Documentation feature shows above. The figure also shows the “Browse. . .” button for the user to change the installation directory if desired.

New Link in the Chain

Now we want to add a custom dialog into our setup sequence. I start off by adding the new .wxs file by right-clicking on the Wix project and doing Add > New Item > WiX > WiX File. I give the new file the name UserRegistrationDlg.wxs. Now I have a Wix file with an empty fragment.

To this fragment I add a UI element with a single Dialog. The Dialog attributes are fairly self-explanatory. The [ProductName] variable will be replaced by the Name attribute of our Product element. The NoMinimize=”yes” attribute indicates that the user cannot minimize the dialog. The dialog is compsed of different Control elements of different types.  Without going too much into designing new dialogs, I want to point out the Edit type, MaskedEdit type, and PushButton controls the new dialog has. The Edit type allows the user to input text that is written to a property of the install session. In this case we have the USERNAME and COMPANYNAME properties. The MaskedEdit allows the user to input text in a specific format. We will specify the PIDTemplate in the main file. The PushButton controls publish events. Primarily they are navigating back and forth between different interface dialogs. Notice that the “Next” button will also publish an event “ValidateProductID”.  This event tells the installer to create a ProductID with the input from the PIDKEY property if the key fits the format specified by the template. Also a condition on moving to the Next dialog, CustomizeDlg, is that the ProductID must exist. Thus, the user will not be able to move to that dialog until they have input a proper PIDKEY. Later we’ll write a custom action to handle this validation.

xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
       <Fragment>
        <UI>
            <Dialog Id="UserRegistrationDlg" Width="370" Height="270" Title="[ProductName] Setup" NoMinimize="yes">
                <Control Id="NameLabel" Type="Text" X="45" Y="73" Width="100" Height="15" TabSkip="no" Text="&User Name:" />
                <Control Id="NameEdit" Type="Edit" X="45" Y="85" Width="220" Height="18" Property="USERNAME" Text="{80}" />
                <Control Id="OrganizationLabel" Type="Text" X="45" Y="110" Width="100" Height="15" TabSkip="no" Text="&Organization:" />
                <Control Id="OrganizationEdit" Type="Edit" X="45" Y="122" Width="220" Height="18" Property="COMPANYNAME" Text="{80}" />
                <Control Id="CDKeyLabel" Type="Text" X="45" Y="147" Width="50" Height="10" TabSkip="no">
                    <Text>CD &Key:</Text>
                </Control>
                <Control Id="CDKeyEdit" Type="MaskedEdit" X="45" Y="159" Width="250" Height="16" Property="PIDKEY" Text="[PIDTemplate]" />
                <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="&Back">
                    <Publish Event="NewDialog" Value="LicenseAgreementDlg">1</Publish>
                </Control>
                <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="&Next">
                    <Publish Event="ValidateProductID" Value="0">1</Publish>
                    <Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg">CostingComplete = 1</Publish>
                    <Publish Event="NewDialog" Value="CustomizeDlg">ProductID</Publish>
                </Control>
                <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
                    <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
                </Control>
                <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="WixUI_Bmp_Banner" />
                <Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes">
                    <Text>Please enter your customer information</Text>
                </Control>
                <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
                <Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes">
                    <Text>{\WixUI_Font_Title}Customer Information</Text>
                </Control>
                <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
            </Dialog>
        </UI>
       </Fragment>
</Wix>

We next need to fit our dialog into the sequence of dialogs in our UI. It should go between the LicenseAgreementDlg, specified in our “Back” action, and the CustomizeDlg specified by the “Next” action. If you followed the tutorial you will notice that I’ve traded the CustomizeDlg in for the SetupTypeDlg that the tutorial uses. The CustomizeDlg is what the FeatureTree uses, as we don’t allow the user to change the setup type as they can with the Mondo UI.

In my product file I create a new UI container and move the references to WixUI_FeatureTree and WixUI_ErrorProgressText to it. I also add the DialogRef to the new dialog we created. Finally I add the Publish elements that establish where to place our new dialog in the UI sequence. The Order attribute gives a precedence to the publish events. Those with a higher number will be fired before lower ones. Since the WixUI_FeatureTree  set adds its own hooks to the buttons on the builtin dialogs, we need to add order to our publish events for them to be fired before the built-in events. The dialog reference (http://wix.sourceforge.net/manual-wix3/WixUI_dialog_reference_toc.htm) explains the different built-in dialogs and what is included in each UI set.

Within the Fragment I also include the PIDTemplate property as well as a property element for the PIDKEY property that tells the installer not to log its value (Hidden=’yes’). Our property tells the user to enter 3 digits in each of two fields. In the Product element above I reference this UI container. The MaskedEdit control and templates are explained here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa369797(v=vs.85).aspx.

. . .
    <UIRef Id="MyWixUI_FeatureTree" />
   
</Product>
. . .
<Fragment>
    <UI Id="MyWixUI_FeatureTree">
        <UIRef Id="WixUI_FeatureTree" />
        <UIRef Id="WixUI_ErrorProgressText" />
        <DialogRef Id="UserRegistrationDlg" />

        <Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="UserRegistrationDlg" Order="3">LicenseAccepted = "1"</Publish>
        <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="UserRegistrationDlg" Order="2">1</Publish>
    </UI>
    <Property Id="PIDTemplate"><![CDATA[12345<### ###>@@@@@]]></Property>
    <Property Id="PIDKEY" Hidden='yes' />
</Fragment>

And with that you should be able to rebuild the installer and run it. The Wix VisualStudio tool knows to compile and link both dialog files. You should see our new dialog after the license agreement and see the key validation working. Next we’ll walk through creating a custom action to do that validation.

5 comments:

Immanuel said...

it is great post. You can also modify the default selections to have the rom that you want, with the minimum stock stuff. installerex custom installers

Immanuel said...

this is nice and great post.Hi I'm a web developer and i used this company installerex to build a custom installer , they build me a great and agile installer just the way i wanted , the installer is the fastest i saw , and now each time someone download my product online the installer install the program in the user computer

Immanuel said...

this is nice and great post.Hi I'm a web developer and i used this company installerex to build a custom installer , they build me a great and agile installer just the way i wanted , the installer is the fastest i saw , and now each time someone download my product online the installer install the program in the user compute installerex

installerex said...

Thank you so much for sharing this informative and useful post !!!!

Anonymous said...

I am getting a lot of errors when trying to build

"Entity 'User' not defined" and "Expecting ';'" etc.

Can you advise please?