Wix 安裝部署教程(十) --來,用WPF做個漂亮的安裝界面


       在上一篇中曾留下兩個問題,.Net捆綁安裝不觸發以及路徑選擇的問題現在都已經解決,這段時間花的最多的地方還是WPF調樣式上面,奈何WPF功力不夠,暫時還是沒有達到自己想要的效果。另外ViewModel做了些調整,狀態更加分明。安裝效果是仿照搜狗輸入法做的。先上效果圖。

 

      以上這只是四個基本的頁面,更細化一些可以分出卸載的進度頁面,卸載的完成頁面,對修復同理,還有安裝失敗,用戶取消提示頁面,再就是能檢測新版本並覆蓋安裝,這些都可以根據狀態去添加頁面。下面簡單的說下思路和新的問題點。

關鍵流程

     安裝包運行之后,首先觸發的重要的事件之一就是 DetectPackageComplete,Bootstrapper按照Bundle.wxs Chain里面Package的順序來檢測當前電腦是否安裝了其中的程序。

    <Chain DisableRollback='yes'>
      <PackageGroupRef Id="Netfx4Full"  />
      <MsiPackage Id="HeartBeats" SourceFile="D:\SetUp\Main\TestWix\bin\Debug\zh-cn\WPFDemo.msi" Compressed="yes"  DisplayInternalUI="no" >
        <MsiProperty Name="INSTALLFOLDER" Value="[InstallFolder]"/>
      </MsiPackage>
    </Chain>

   像這樣,首先會判斷是否安裝了.Net4.0,但是在WPF程序里面我們需要判斷的還是HeartBeats這個安裝包。·

   protected void DetectPackageComplete(object sender, DetectPackageCompleteEventArgs e)
        {
            PackageId = e.PackageId;
            //對應的是MsiPackage Id="HeartBeats" //MessageBox.Show(e.PackageId + e.State);
            if (e.PackageId.Equals("HeartBeats", StringComparison.Ordinal))
            {
                State = e.State == PackageState.Present ? InstallState.Present : InstallState.NotPresent;
                //State =  InstallState.NotPresent;
            }
        }

如果這個安裝包的State等於 PackageState.Present 那說明當前電腦已經安裝過了,那就要出現卸載和修復的界面,反之就是出現安裝的界面(因此也可以控制用戶不能直接卸載,可以讓用戶提交一些數據之后再卸載)。然后會執行PlanBegin->PlanComplete->

ApplyBegin->ApplyComplete. 從字面意思理解就是安裝准備到執行完畢的過程。InstallState的值就是在這些事件中發生變化,這也是從進度條頁面切換到完成頁面的順序。InstallState 為Applied就是安裝完成了。

 public enum InstallState
        {
            Initializing,
            Present,
            NotPresent,
            Applying,
            Cancelled,
            Applied,
            Failed,
        }

其他的一些細節在前文里面有講,這里就不再贅述。

安裝界面布局

 這里的布局主要是四個Grid根據狀態不斷的切換。當然你可以添加更多的狀態也展示不同的頁面。

        <Grid  Grid.Row="1" Background="White" Visibility="{Binding InstallEnabled,Converter={StaticResource BooleanToVisibilityConverter}}"  >
                    <Grid.RowDefinitions>
                        <RowDefinition Height="2*" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Rectangle MouseLeftButtonDown="Background_MouseLeftButtonDown"  />
                    <StackPanel Grid.Row="0"  VerticalAlignment="Stretch"  Style="{StaticResource InstallGrid}" >
                        <StackPanel.Background>
                            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                <GradientStop Color="White" Offset="0" />
                                <GradientStop Color="AliceBlue" Offset="1" />
                            </LinearGradientBrush>
                        </StackPanel.Background>
                        <TextBlock HorizontalAlignment="Center"   Padding="10" FontSize="40" Foreground="#33CCFF" IsHitTestVisible="False"  Text="{Binding SoftName}" ></TextBlock>
                        <Button Height="40"  HorizontalAlignment="Center" IsEnabled="{Binding IsAgree,UpdateSourceTrigger=PropertyChanged}"   Width="200" Style="{StaticResource BuleBt}" Command="{Binding InstallCommand}"  >立即安裝</Button>
                        <TextBlock  Foreground="Gray"   Padding="5 10 0 0" VerticalAlignment="Center"  HorizontalAlignment="Center" TextDecorations="{x:Null}">
                            <Hyperlink NavigateUri="http://www.cnblogs.com/stoneniqiu/" RequestNavigate="Hyperlink_OpenGuid"  > 
                                 <Label Content="安裝指南" VerticalContentAlignment="Bottom" FontSize="13" Margin="0" Padding="0" Foreground="#666" ></Label>
                            </Hyperlink>
                        </TextBlock>
                    </StackPanel>

                    <Border  Grid.Row="1"  HorizontalAlignment="Left" Width="580" Height="110" VerticalAlignment="Top"  BorderBrush="Gainsboro"   BorderThickness="0 0 0 0">
                        <Grid Style="{StaticResource DirGrid}"   >
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"></ColumnDefinition>
                                <ColumnDefinition Width="3*" ></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Label Grid.Row="0" Grid.Column="0" Content="安裝位置:" HorizontalAlignment="Right" VerticalContentAlignment="Center" ></Label>
                            <TextBox Grid.Column="1" Grid.Row="0" Width="280" Height="25" Background="White" Margin="10 5" HorizontalAlignment="Left" Text="{Binding InstallFollder,UpdateSourceTrigger=PropertyChanged}" ></TextBox>
                            <Button  Grid.Column="1" Grid.Row="0" Width="50" Height="25" Style="{StaticResource BuleBt}" Margin="0 0 90 0" Name="SelectFile" Click="SelectFile_OnClick"  FontSize="14"  HorizontalAlignment="Right" >瀏覽</Button>
                            <CheckBox Grid.Row="1" IsChecked="{Binding IsAgree,UpdateSourceTrigger=PropertyChanged}" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center"  Margin="10 0 0 0" >

                                <TextBlock  Foreground="Gray"   Padding="5 0 0 0" VerticalAlignment="Center"  TextDecorations="{x:Null}">
                         <Label Content="我已經閱讀並最終接受:" VerticalContentAlignment="Bottom" FontSize="12" Margin="0" Padding="0" Foreground="Gray" ></Label>
                    <Hyperlink NavigateUri="http://www.cnblogs.com/stoneniqiu/" RequestNavigate="Hyperlink_OnRequestNavigate"  > 
                         <Label Content="用戶協議" VerticalContentAlignment="Bottom" FontSize="12" Margin="0" Padding="0" Foreground="DodgerBlue" ></Label>
                    </Hyperlink>
                                </TextBlock>

                            </CheckBox>

                            <Label Name="InstalLabel"  Grid.Column="0" Grid.ColumnSpan="2" Content="{Binding SpaceInfo,UpdateSourceTrigger=PropertyChanged}"  Foreground="Gray" HorizontalAlignment="Center" Grid.Row="2" Margin="5 0 0 0" />
                        </Grid>
                    </Border>
                </Grid>

                <Grid  Grid.Row="1" Visibility="{Binding UninstallEnabled,Converter={StaticResource BooleanToVisibilityConverter}}">
                    <Grid.Background>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                            <GradientStop Color="White" Offset="0" />
                            <GradientStop Color="WhiteSmoke" Offset="1" />
                        </LinearGradientBrush>
                    </Grid.Background>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition  Width="2*"/>
                        <ColumnDefinition  Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition  Height="35" />
                        <RowDefinition  Height="45"/>
                        <RowDefinition />
                        <RowDefinition Height="50" />
                    </Grid.RowDefinitions>
                    <Label Grid.Row="1" Grid.Column="1" Content="請選擇:"  HorizontalAlignment="Left"  VerticalAlignment="Top"  Foreground="Green"  FontSize="25" ></Label>
                    <StackPanel Grid.Row="2" Grid.Column="1">
                        <Button    Content="卸載" IsDefault="True" HorizontalAlignment="Left" Margin="10 10" Height="35" Width="120" Style="{StaticResource BuleBt}"  Background="Tomato" Command="{Binding UninstallCommand}"  ></Button>

                        <Button    Content="修復" IsDefault="True" HorizontalAlignment="Left" Margin="10 10" Height="35"  Width="120" Style="{StaticResource BuleBt}"  Command="{Binding RepairCommand}"  ></Button>
                    </StackPanel>
                    <TextBlock Grid.Row="3" Foreground="Gray" Grid.Column="0" Padding="5 0 0 0" VerticalAlignment="Center" Grid.ColumnSpan="2" TextDecorations="{x:Null}">
                         <Label Content="登陸官網了解更多:" VerticalContentAlignment="Bottom" FontSize="12" Margin="0" Padding="0" Foreground="Gray" ></Label>
                    <Hyperlink NavigateUri="{Binding WebSite }" RequestNavigate="Hyperlink_OnRequestNavigate"  > 
                         <Label Content="{Binding WebSite}" VerticalContentAlignment="Bottom" FontSize="12" Margin="0" Padding="0" Foreground="Gray" ></Label>
                    </Hyperlink>
                    </TextBlock>

                </Grid>
View Code

像安裝頁面對應的是InstallEnabled 屬性,來決定是否顯示。

     public bool InstallEnabled
        {
            get { return State == InstallState.NotPresent; }
        }
 public InstallState State
        {
            get
            {
                return state;
            }
            set
            {
                if (state != value)
                {
                    state = value;
                    Message = "Status: " + state;
                    OnPropertyChanged("State");
                    OnPropertyChanged("CompleteEnabled");
                    OnPropertyChanged("ExitEnabled");
                    OnPropertyChanged("CancelEnabled");
                    OnPropertyChanged("InstallEnabled");
                    OnPropertyChanged("ProgressEnabled");
                    OnPropertyChanged("UninstallEnabled");
                }
            }
        }

一些文字的修改直接寫在InstallConfig里面

 public static class InstallConfig
   {
       public static string SoftName = "HeartBeats";
       public static string WebSite = "http://www.cnblogs.com/stoneniqiu/";
       public static string IcoUrl = "../Resources/heart.ico";
       public static string BkUrl = "../Resources/Slider/b1.png";
       public static string SoftTitle = "HeartBeats2.0正式版 安裝向導";
       public static string InstallFinished = "HeartBeats2.0安裝完成";
   }

.Net安裝

 這個問題的解決還得的感謝一位網友,WIX的很多問題困擾着我,就像《勿忘心安》那首歌里面唱的,“只是苦於這些問題無人交流,只好任憑生命去阻礙”。很多時候一個人思路有限,知識有限,沒有交流很難解決問題。解決辦法就是修改一下bundle文件。

  <Fragment>
    <WixVariable Id="WixMbaPrereqPackageId" Value="Netfx4Full" />
    <WixVariable Id="WixMbaPrereqLicenseUrl" Value="NetfxLicense.rtf" />

    <util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\Net Framework Setup\NDP\v4\Full" Value="Version" Variable="Netfx4FullVersion" />
    <util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\Net Framework Setup\NDP\v4\Full" Value="Version" Variable="Netfx4x64FullVersion" Win64="yes" />
    <PackageGroup Id="Netfx4Full">
      <ExePackage
          Id="Netfx4Full"
          Cache="yes"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="$(var.Dia)dotNetFx40_Full_x86_x64.exe"
          InstallCommand="/q /norestart "
          DownloadUrl="http://go.microsoft.com/fwlink/?LinkId=164193"
          DetectCondition="Netfx4FullVersion AND (NOT VersionNT64 OR Netfx4x64FullVersion)"/>
    </PackageGroup>
  </Fragment>

如果是4.5就修改成

  <Fragment>
    <WixVariable Id="WixMbaPrereqPackageId" Value="Netfx45Full" />
    <WixVariable Id="WixMbaPrereqLicenseUrl" Value="NetfxLicense.rtf" />

    <util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\Net Framework Setup\NDP\v4.5\Full" Value="Version" Variable="Netfx45FullVersion" />
    <util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\Net Framework Setup\NDP\v4.5\Full" Value="Version" Variable="Netfx45x64FullVersion" Win64="yes" />
    <PackageGroup Id="Netfx45Full">
      <ExePackage
          Id="Netfx45Full"
          Cache="yes"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="D:\dotNetFx45_Full_setup.exe"
          InstallCommand="/q /norestart "
          DownloadUrl="http://go.microsoft.com/fwlink/?LinkId=164193"
          DetectCondition="Netfx45FullVersion AND (NOT VersionNT64 OR Netfx45x64FullVersion)"/>
    </PackageGroup>
  </Fragment>
</Wix>

如果用原生的Bootstrap界面,這樣寫就行了。

 <util:RegistrySearchRef Id="NETFRAMEWORK40"/>
    <PackageGroup Id="Netfx4Full">
      <ExePackage
          Id="Netfx4FullExe"
          Cache="no"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="$(var.Dia)dotNetFx40_Full_x86_x64.exe"
          InstallCommand="/q /norestart "
          DetectCondition="NETFRAMEWORK40"
          DownloadUrl="http://go.microsoft.com/fwlink/?LinkId=164193"/>
    </PackageGroup>

對應的BootstrapperCore.config 稍微做一下修改。

<configuration>
    <configSections>
        <sectionGroup name="wix.bootstrapper" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore">
            <section name="host" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore" />
        </sectionGroup>
    </configSections>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0" />
    </startup>
    <wix.bootstrapper>
      <host assemblyName="CustomBA">
        <supportedFramework version="v4\Full" />
        <supportedFramework version="v4\Client" />
      </host>
    </wix.bootstrapper>
</configuration>

在沒有.Net4.0的時候會出現類似於如下的安裝界面。完成之后再會啟動WPF安裝界面。不然WPF界面是跑步起來的。

設置安裝路徑

 這個功能就是講WPF用戶自定義的路徑傳遞到MSI文件中去。分三步。

 1.去掉目錄中的ProgramFilesFolder。

 在原來的模式下,我們的路徑應該是這樣的:目標目錄下是ProgramFiles文件夾再是我們的軟件目錄。

   <!--文件安裝目錄-->
    <Directory Id="TARGETDIR" Name="SourceDir">
      <!--程序目錄-->
      <Directory Id='ProgramFilesFolder'>
        <Directory Id='INSTALLFOLDER' Name='!(loc.SoftName)'>
  </Directory>
  </Directory>
  </Directory>

 修改成:

    <!--文件安裝目錄-->
    <Directory Id="TARGETDIR" Name="SourceDir">
      <!--程序目錄-->
      <Directory Id='INSTALLFOLDER' Name='!(loc.SoftName)'>
      </Directory>      
</Directory>

我這是實驗得來的結果,我的理解是ProgramFilesFolder對於Program Files或者Program Files(x86) 文件夾,有強制性,導致我們自定義的路徑設置失敗。

2.在Bundl.wxs文件中加入MsiProperty。

  <MsiProperty Name="INSTALLFOLDER" Value="[InstallFolder]"/>
INSTALLFOLDER 對應的就是MSI安裝包中的變量,也就是上一步驟中的 Directory。而[InstallFolder]是我們在Bundle中自定義的變量。接下來看最后一步。

3.Bing定並設置變量InstallFolder

 在ViewModel中定義一個InstallFolder屬性

 public string InstallFollder
        {
            get { return _installFollder; }
            set
            {
                if (_installFollder != value && ValidDir(value))
                {
                    _installFollder = value;
                    OnPropertyChanged("InstallFollder");
                    model.SetTargetFolderPath(value);
                }
            }
        }
       private string TargetFolder = "InstallFolder";
        public void SetTargetFolderPath(string path)
        {
            SetBurnVariable(TargetFolder,path);
        }
        public void SetBurnVariable(string variableName, string value)
        {
            BootstrapperApplication.Engine.StringVariables[variableName] = value;
        }

改變的時候,先驗證,再設置這個變量。

   <TextBox Grid.Column="1" Grid.Row="0" Width="280" Height="25" Background="White" Margin="10 5" HorizontalAlignment="Left" Text="{Binding InstallFollder,UpdateSourceTrigger=PropertyChanged}" ></TextBox>

界面上直接綁定這個屬性即可。

Installer4.5及vcredist安裝

這是兩個額外的知識點,有的電腦沒有Installer4.5不能觸發安裝,沒有vcredist 也會導致一些com注冊失敗。 

    <Chain DisableRollback='yes'>
      <PackageGroupRef Id="Netfx4Full"  />
      <PackageGroupRef Id="WindowsInstaller45"  />
      <PackageGroupRef Id="vcredist"  />
<
MsiPackage ..... Compressed="yes" DisplayInternalUI="yes" /> </Chain>

開發電腦太強大,測試電腦可能缺這個缺那個的,歡迎大家補充一些。

<Fragment>
    <util:RegistrySearchRef Id="NETFRAMEWORK40"/>
    <PackageGroup Id="Netfx4Full">
      <ExePackage
          Id="Netfx4FullExe"
          Cache="no"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="$(var.Dia)dotNetFx40_Full_x86_x64.exe"
          InstallCommand="/q /norestart "
          DetectCondition="NETFRAMEWORK40"
          DownloadUrl="http://go.microsoft.com/fwlink/?LinkId=164193"/>
    </PackageGroup>
    <util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\DevDiv\VC\Servicing\9.0\RED\1033" Value="SP" Variable="vcredist" />
    <PackageGroup Id="vcredist">
      <ExePackage Id="vcredist_x86"
        Cache="no"
        Compressed="yes"
        PerMachine="yes"
        Permanent="yes"
        Vital="yes"
        Name="vcredist_x86.exe"
        SourceFile="$(var.Dia)vcredist_x86.exe"
        DownloadUrl="http://www.microsoft.com/zh-cn/download/confirmation.aspx?id=5638"
        InstallCommand="/q"
        DetectCondition="vcredist AND (vcredist &gt;= 1)">
        <ExitCode Value ="3010" Behavior="forceReboot" />
      </ExePackage>
    </PackageGroup>
    <!-- Windows Installer 4.5 -->
    <PackageGroup Id="WindowsInstaller45">
      <ExePackage
          Cache="no"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="$(var.Dia)WindowsXP-KB942288-v3-x86.exe"
          DownloadUrl="http://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/WindowsXP-KB942288-v3-x86.exe"
          InstallCondition="VersionNT=v5.1 AND NOT VersionNT64 AND VersionMsi &lt; v4.5"
          InstallCommand="/quiet /norestart">
        <ExitCode Behavior="forceReboot"/>
      </ExePackage>
      <ExePackage
          Cache="no"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="$(var.Dia)WindowsServer2003-KB942288-v4-x86.exe"
          DownloadUrl="http://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/WindowsServer2003-KB942288-v4-x86.exe"
          InstallCondition="VersionNT=v5.2 AND NOT VersionNT64 AND VersionMsi &lt; v4.5"
          InstallCommand="/quiet /norestart">
        <ExitCode Behavior="forceReboot"/>
      </ExePackage>
      <ExePackage
          Cache="no"
          Compressed="yes"
          PerMachine="yes"
          Permanent="yes"
          Vital="yes"
          SourceFile="$(var.Dia)WindowsServer2003-KB942288-v4-x64.exe"
          DownloadUrl="http://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/WindowsServer2003-KB942288-v4-x64.exe"
          InstallCondition="VersionNT=v5.2 AND VersionNT64 AND VersionMsi &lt; v4.5"
          InstallCommand="/quiet /norestart">
        <ExitCode Behavior="forceReboot"/>
      </ExePackage>
      <MsuPackage
          Cache="no"
          Compressed="yes"
          Permanent="yes"
          Vital="yes"
          KB="KB942288"
          SourceFile="$(var.Dia)Windows6.0-KB942288-v2-x86.msu"
          DownloadUrl="http://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/Windows6.0-KB942288-v2-x86.msu"
          InstallCondition="VersionNT=v6.0 AND NOT VersionNT64 AND VersionMsi &lt; v4.5"/>
      <MsuPackage
          Cache="no"
          Compressed="yes"
          Permanent="yes"
          Vital="yes"
          KB="KB942288"
          SourceFile="$(var.Dia)Windows6.0-KB942288-v2-x64.msu"
          DownloadUrl="http://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/Windows6.0-KB942288-v2-x64.msu"
          InstallCondition="VersionNT=v6.0 AND VersionNT64 AND VersionMsi &lt; v4.5"/>
    </PackageGroup>
  </Fragment>
View Code

 小結

 QQ,搜狗,UC,360等等這些互聯網巨頭的安裝包都蠻炫酷的,對卸載和升級都做的很細致精美,很多效果我想WPF是可以做到的,但是現在有些效果我還沒有成功實現:

 1.炫彩滾動的進度條,想到用漸變,但效果不理想也就沒有放上來。如果是css3,那是很好寫的。像這些效果-->猛擊

  

 2.圖片滾動。像Web里面的Slider一樣,在顯示進度條的時候放個三張介紹圖片來回切換。這個我找到了一個Demo,還沒有套用到這個安裝包中。

 3.安裝軟件的時候,用戶需要打開用戶許可或者安裝向導,而這個除了用官網的網址,我想用文件來展示,比如html、txt。一般的WPF程序,復制到輸出目錄就可以了,但是在安裝包中需要打開dll資源中的文件,這個不知道大家有什么辦法。

   比如我的根目錄下有一個文件,InstallGuid.Html文件。生成操作為Resource。

   

 在Hyperlink的事件中用

Process.Start(new ProcessStartInfo(filepath));

 打開這個文件,寫相對路徑是會報錯找不到文件。

 以上三個問題,如有知道的園友還請不吝指教下。 

 需要Demo的同學 http://pan.baidu.com/s/1bntxKsB

      我喜歡看書,也喜歡分享書籍(不限技術書籍),建了一個書山有路群,誠邀有興趣的朋友加入q:452450927

        

     書山有路一起走,學海無涯是朋友。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM