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"> <StackPanel DockPanel.Dock="Top" Margin="24,28,24,24"> <TextBlock x:Name="HDTShellTitle" Foreground="White" FontSize="26" FontWeight="SemiBold" /> <TextBlock x:Name="HDTShellSubtitle" 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. --> <ItemsControl x:Name="HDTPageList" Margin="0,8,0,0" 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"> <ContentControl x:Name="HDTPageHost" /></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> |