UI/HDTWizardShell.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Hephaestus Deployment Toolkit"
    Height="700" Width="900"
    WindowStartupLocation="CenterScreen"
    ResizeMode="NoResize"
    WindowStyle="None"
    Background="#FF1E1E1E">
 
    <!--
        MDT'S LITETOUCH WIZARD, REBUILT - the shape an MDT admin already knows:
        a coloured rail down the left listing the pages, the current page's
        content to the right of it, and Back / Next / Cancel at the bottom.
        PSDWizard_Template_Classic_en-US.xaml is the closest prior art and this
        follows its layout deliberately (NOTICE.md).
 
        WHERE IT DEVIATES FROM PSD, AND WHY BOTH ARE FORCED:
 
          NO x:Class. PSD's template declares PSDWizard.MainWindow and relies on
          code-behind. There is no compiler in WinPE; New-HDTWizardHost attaches
          every handler through FindName instead, and
          tests/contract/WinPeUiStack.Contract.Tests.ps1 refuses a code-behind
          class outright.
 
          NO MERGED ResourceDictionary. PSD's template merges six of them by
          Source=. That is six more files that must reach the RAM disk intact,
          and a page that half-loads in WinPE is a XamlParseException where a
          wizard should be. The host merges HDTTheme.xaml at runtime instead, so
          this file names no other file at all.
 
        THE RAIL IS A LIST, NOT A TabControl. MDT's panes are not tabs a
        technician may click between - the order is the deployment's order, and
        a page becomes reachable by being reached. The rail SHOWS progress; the
        buttons MAKE it. HDTPageList is therefore display-only, and the host
        drives HDTPageHost.
 
        EVERY PAGE IS INDIVIDUALLY SKIPPABLE (DESIGN 11.2). A page whose values
        are all supplied never appears, and never appears in the rail either -
        the rail lists what this deployment will actually ask, not the catalogue
        of what a deployment could ask.
    -->
 
    <!--
        THE ROOT PAINTS ITS OWN GROUND. Window.Background alone is not enough:
        anything that renders this tree without the Window around it - a
        preview, a screenshot harness, a page hosted elsewhere - gets whatever
        is behind it, and white text on white ground is a wizard nobody can
        read. It cost exactly one screenshot to learn.
    -->
    <Grid Background="#FF1E1E1E">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="230" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="76" />
        </Grid.RowDefinitions>
 
        <!-- ============ the rail ============ -->
        <!--
            DARKER THAN THE ACCENT, not the accent itself: the rail is a
            backdrop that must never compete with the selected row painted on
            top of it in that accent.
        -->
        <Border Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Background="#FF083B5C" x:Name="HDTDragBanner">
            <DockPanel LastChildFill="True">
 
                <!--
                    BOTH LINES WRAP, AND THE HEADING IS WHY. It read
                    'Hephaestus' on every machine ever built, which fits this
                    rail with room to spare - so nothing revealed that the
                    TextBlock had no wrapping at all until HDTBrandingName let a
                    site put its own name here and 'Contoso Field Services' came
                    out as 'Contoso Field S', clipped mid-word with no ellipsis
                    to say it had been. A banner that silently truncates the one
                    thing on it that identifies the deployment is worse than no
                    banner. HDTShellBuild below has wrapped since it was written.
                -->
                <StackPanel DockPanel.Dock="Top" Margin="24,28,24,24">
                    <TextBlock x:Name="HDTShellTitle" TextWrapping="Wrap"
                               Foreground="White" FontSize="26" FontWeight="SemiBold" />
                    <TextBlock x:Name="HDTShellSubtitle" TextWrapping="Wrap"
                               Foreground="#FFA9CDE6" FontSize="15" Margin="0,2,0,0" />
                </StackPanel>
 
                <!--
                    THE VERSION AND THE BUILD, at the bottom of the rail where
                    MDT puts them. On a machine that has just failed, the first
                    question is always WHICH IMAGE THIS IS - and the answer has
                    to be on screen, because the log is on a share the machine
                    may not have reached.
                -->
                <TextBlock DockPanel.Dock="Bottom" x:Name="HDTShellBuild" Text=""
                           Foreground="#FF7FA8C4" FontSize="13"
                           Margin="24,0,24,20" TextWrapping="Wrap" />
 
                <!--
                    EACH ROW STATES WHAT IT IS, AND THIS DECIDES WHAT THAT LOOKS
                    LIKE. Step-HDTWizardPage hands over Title and State - Done,
                    Current or Pending - and nothing else. It used to be handed
                    a Fill and an Ink brush, which put the look in a PowerShell
                    command: a second place the palette is defined, and the
                    first one to drift from HDTTheme.xaml.
                -->
                <!--
                    IsTabStop="False" BECAUSE THIS IS A PICTURE, NOT A CONTROL.
                    Control.IsTabStop defaults to TRUE and the rail inherited
                    it, so the FIRST Tab on every page landed here - on an
                    ItemsControl of Borders and TextBlocks with nothing to
                    click. Measured with an STA probe walking MoveFocus: Tab 1
                    the rail, Tab 2 the page host, Tab 3 the first real field.
                -->
                <ItemsControl x:Name="HDTPageList" Margin="0,8,0,0"
                              IsTabStop="False"
                              Background="Transparent" BorderThickness="0">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="24,10,16,10">
                                <Border.Style>
                                    <Style TargetType="Border">
                                        <Setter Property="Background" Value="Transparent" />
                                        <Style.Triggers>
                                            <!-- THE ACCENT, and only on the row being asked. -->
                                            <DataTrigger Binding="{Binding State}" Value="Current">
                                                <Setter Property="Background" Value="#FF0E639C" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Border.Style>
 
                                <TextBlock Text="{Binding Title}" FontSize="16" TextWrapping="Wrap">
                                    <TextBlock.Style>
                                        <Style TargetType="TextBlock">
                                            <!--
                                                PENDING IS THE DIMMEST, and it is
                                                the default because most of the
                                                rail is ahead of the technician.
                                            -->
                                            <Setter Property="Foreground" Value="#FF7FA8C4" />
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding State}" Value="Done">
                                                    <Setter Property="Foreground" Value="#FFA9CDE6" />
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding State}" Value="Current">
                                                    <Setter Property="Foreground" Value="White" />
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </TextBlock.Style>
                                </TextBlock>
                            </Border>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </DockPanel>
        </Border>
 
        <!-- ============ the page ============ -->
        <Grid Grid.Row="0" Grid.Column="1" Margin="36,32,36,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
 
            <TextBlock Grid.Row="0" x:Name="HDTPageHeading" Text=""
                       Foreground="White" FontSize="26" FontWeight="SemiBold" />
 
            <TextBlock Grid.Row="1" x:Name="HDTPageSubheading" Text=""
                       Foreground="#FFCCCCCC" FontSize="16"
                       Margin="0,6,0,0" TextWrapping="Wrap" />
 
            <!--
                WHERE THE PAGE ITSELF GOES. The host parses the page's own XAML
                and drops it in here, which is what lets DESIGN 11.2's eight
                pages live on the SHARE under Scripts\UI and be redesigned
                without rebuilding a boot image.
 
                IT SCROLLS, AND THE PAGE THAT PROVED IT WAS COMPUTER DETAILS. A
                name, a domain-or-workgroup choice with five boxes under it, and
                the panel explaining what a computer name may hold do not fit in
                this window - and the half that fell off the bottom was the
                workgroup, which is the option most machines take. A page host
                that clips is a page host that hides whichever control was added
                last.
 
                THE SHELL SCROLLS, NOT THE PAGES. Every page would otherwise
                have to carry its own ScrollViewer and remember to, and the one
                that forgot would be the one that grew. Pages live on the SHARE
                and are edited by administrators who cannot be expected to know
                the window is 700px tall.
 
                HORIZONTAL IS DISABLED ON PURPOSE. A page wide enough to scroll
                sideways is a layout fault to fix rather than a thing to give a
                technician a second scrollbar for.
            -->
            <ScrollViewer Grid.Row="2" Margin="0,24,0,0"
                          VerticalScrollBarVisibility="Auto"
                          HorizontalScrollBarVisibility="Disabled"
                          Padding="0,0,12,0">
                <!--
                    AND THE SECOND DEAD STOP. The container is not the thing
                    being filled in; what is loaded INTO it is, and that is
                    where Tab should arrive.
                -->
                <ContentControl x:Name="HDTPageHost" IsTabStop="False" /></ScrollViewer>
        </Grid>
 
        <!-- ============ the buttons ============ -->
        <Border Grid.Row="1" Grid.Column="1" Background="#FF252526">
            <!--
                TWO COLUMNS, NOT TWO THINGS IN ONE CELL. The message and the
                buttons used to share a single cell, the message pinned left
                with MaxWidth="300" to keep it off them - so a refusal long
                enough to be useful was CLIPPED, and the technician could see
                that something was wrong but not what. A message nobody can
                finish reading is the same as no message.
 
                The message now takes the width the buttons do not, and wraps
                inside it.
            -->
            <Grid Margin="36,0,36,0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
 
                <!--
                    A WARNING IS NOT A REFUSAL, AND MUST NOT LOOK LIKE ONE.
                    HDT_01 is a legal computer name that DNS cannot carry: the
                    wizard lets it through and says so, and painting that in the
                    same red as a refusal would make a technician go looking for
                    what they had done wrong.
 
                    THE SEVERITY ARRIVES IN Tag, and the colour is decided here.
                    Test-HDTComputerName returns 'None', 'Warning' or 'Error';
                    the host sets Tag and paints nothing, which is the same
                    split as the rail - the engine states what a thing IS and
                    the markup decides what that looks like.
 
                    NO LOCAL Foreground. A local value beats a Style trigger, so
                    the attribute that used to be here would have won over both
                    triggers and made the warning red anyway.
                -->
                <TextBlock Grid.Column="0" x:Name="HDTMessageText" Text="" HorizontalAlignment="Left"
                           VerticalAlignment="Center" FontSize="14" TextWrapping="Wrap" Margin="0,0,24,0">
                    <TextBlock.Style>
                        <Style TargetType="TextBlock">
                            <Setter Property="Foreground" Value="#FFF48771" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="Warning">
                                    <Setter Property="Foreground" Value="#FFE2C08D" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>
                </TextBlock>
 
                <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
                    <!--
                        OPEN CMD IS NOT A WAY OUT, it is the diagnostic door -
                        and it stays on every page because the page a technician
                        is stuck on is the page they need a prompt from.
 
                        HDTOpenCmdButton, AND THE NAME IS NOT A DETAIL. This
                        button shipped as HDTCommandPromptButton, which is what
                        the ANSWER is called - but New-HDTWizardHost wires the
                        BUTTON, and it wires HDTOpenCmdButton, the name
                        HDTWelcome.xaml uses. Both files read correctly on their
                        own and every assertion passed. The button was dead: it
                        opened no prompt and did not even close the window.
                        WinPeUiStack.Contract.Tests.ps1 now refuses a named
                        button no engine code mentions.
                    -->
                    <Button x:Name="HDTOpenCmdButton"
                            Width="120" Height="38" Background="#FF3A3A3C" Margin="0,0,16,0" />
                    <Button x:Name="HDTBackButton"
                            Width="110" Height="38" Background="#FF3A3A3C" Margin="0,0,10,0" />
                    <Button x:Name="HDTCancelButton"
                            Width="110" Height="38" Background="#FF3A3A3C" Margin="0,0,10,0" />
                    <Button x:Name="HDTNextButton"
                            Width="130" Height="38" Background="#FF0E639C" />
                </StackPanel>
            </Grid>
        </Border>
    </Grid>
</Window>