Private/Notification/Show-WPFDialog.ps1
|
function Show-WPFDialog { <# .SYNOPSIS Shows a WPF dialog window .DESCRIPTION Creates and displays a WPF dialog for user interaction .PARAMETER Xaml The XAML definition for the window .PARAMETER DataContext Data to bind to the window .PARAMETER Timeout Auto-close timeout in seconds #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Xaml, [Parameter()] [hashtable]$DataContext, [Parameter()] [int]$Timeout = 300 ) Add-Type -AssemblyName PresentationFramework, System.Windows.Forms -ErrorAction SilentlyContinue try { [xml]$xamlXml = $Xaml $reader = New-Object System.Xml.XmlNodeReader $xamlXml $window = [Windows.Markup.XamlReader]::Load($reader) return $window } catch { Write-PatchLog "Failed to create WPF dialog: $_" -Type Error return $null } } function Get-DeferralDialogXaml { <# .SYNOPSIS Returns XAML for the deferral dialog #> [CmdletBinding()] param( [Parameter()] [string]$Title = "Update Required", [Parameter()] [string]$Message, [Parameter()] [string]$AppName, [Parameter()] [int]$DeferralsRemaining, [Parameter()] [string]$AccentColor = "#0078D4" ) return @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="$Title" Height="320" Width="520" WindowStyle="None" AllowsTransparency="True" Background="Transparent" Topmost="True" WindowStartupLocation="CenterScreen" ResizeMode="NoResize"> <Border CornerRadius="12" Background="#FF2D2D30" BorderBrush="$AccentColor" BorderThickness="2" Margin="10"> <Border.Effect> <DropShadowEffect BlurRadius="20" ShadowDepth="0" Opacity="0.6"/> </Border.Effect> <Grid Margin="25"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- Header --> <StackPanel Grid.Row="0" Orientation="Horizontal"> <TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="28" Foreground="#FFD83B01" Margin="0,0,12,0"/> <TextBlock Name="HeaderText" Text="$Title" FontSize="20" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/> </StackPanel> <!-- App Name --> <TextBlock Grid.Row="1" Name="AppNameText" Text="$AppName" FontSize="16" Foreground="$AccentColor" Margin="0,15,0,5"/> <!-- Message --> <TextBlock Grid.Row="2" Name="MessageText" TextWrapping="Wrap" Foreground="#CCCCCC" FontSize="14" Margin="0,10"> $Message </TextBlock> <!-- Countdown --> <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10"> <TextBlock Text="Auto-installing in: " Foreground="#888888" FontSize="15"/> <TextBlock Name="CountdownText" Text="5:00" Foreground="$AccentColor" FontSize="15" FontWeight="Bold"/> </StackPanel> <!-- Buttons --> <StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0"> <Button Name="DeferButton" Content="Defer ($DeferralsRemaining remaining)" Width="160" Height="36" Margin="0,0,12,0" Background="#FF3F3F3F" Foreground="White" BorderThickness="0" Cursor="Hand"> <Button.Style> <Style TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="10,5"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </Button.Style> </Button> <Button Name="UpdateButton" Content="Update Now" Width="120" Height="36" Background="$AccentColor" Foreground="White" BorderThickness="0" Cursor="Hand"> <Button.Style> <Style TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="10,5"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </Button.Style> </Button> </StackPanel> </Grid> </Border> </Window> "@ } function Get-ToastNotificationXaml { <# .SYNOPSIS Returns XAML for toast-style notification #> [CmdletBinding()] param( [Parameter()] [string]$Title = "Update Available", [Parameter()] [string]$Message, [Parameter()] [string]$AccentColor = "#0078D4" ) return @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Title="Toast" Height="120" Width="400" WindowStyle="None" AllowsTransparency="True" Background="Transparent" Topmost="True" ShowInTaskbar="False"> <Border CornerRadius="8" Background="#FF1F1F1F" BorderBrush="#FF3F3F3F" BorderThickness="1" Margin="10"> <Border.Effect> <DropShadowEffect BlurRadius="15" ShadowDepth="3" Opacity="0.5"/> </Border.Effect> <Grid Margin="15"> <Grid.ColumnDefinitions> <ColumnDefinition Width="40"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="30"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="" FontFamily="Segoe MDL2 Assets" FontSize="24" Foreground="$AccentColor" VerticalAlignment="Center"/> <StackPanel Grid.Column="1" VerticalAlignment="Center" Margin="10,0"> <TextBlock Name="TitleText" Text="$Title" Foreground="White" FontSize="14" FontWeight="SemiBold"/> <TextBlock Name="MessageText" Text="$Message" Foreground="#AAAAAA" FontSize="12" TextWrapping="Wrap"/> </StackPanel> <Button Name="CloseButton" Grid.Column="2" Content="" FontFamily="Segoe MDL2 Assets" Background="Transparent" Foreground="#888888" BorderThickness="0" Cursor="Hand"/> </Grid> </Border> </Window> "@ } function Get-RebootPromptDialogXaml { <# .SYNOPSIS Returns XAML for a reboot prompt dialog (Restart now / Later) #> [CmdletBinding()] param( [Parameter()] [string]$Title = "Restart Required", [Parameter()] [string]$Message = "Updates were installed and a restart is required to finish applying changes.", [Parameter()] [string]$AccentColor = "#0078D4", [Parameter()] [string]$CompanyName = "IT Department" ) return @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="$Title" Height="260" Width="520" WindowStyle="None" AllowsTransparency="True" Background="Transparent" Topmost="True" WindowStartupLocation="CenterScreen" ResizeMode="NoResize"> <Border CornerRadius="12" Background="#FF2D2D30" BorderBrush="$AccentColor" BorderThickness="2" Margin="10"> <Border.Effect> <DropShadowEffect BlurRadius="20" ShadowDepth="0" Opacity="0.6"/> </Border.Effect> <Grid Margin="25"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- Header --> <StackPanel Grid.Row="0" Orientation="Horizontal"> <TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="28" Foreground="$AccentColor" Margin="0,0,12,0"/> <TextBlock Text="$Title" FontSize="20" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/> </StackPanel> <!-- Subheader --> <TextBlock Grid.Row="1" Text="$CompanyName" Foreground="#888888" FontSize="12" Margin="0,8,0,0"/> <!-- Message --> <TextBlock Grid.Row="2" TextWrapping="Wrap" Foreground="#CCCCCC" FontSize="14" Margin="0,15,0,0"> $Message </TextBlock> <!-- Buttons --> <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,18,0,0"> <Button Name="LaterButton" Content="Later" Width="120" Height="36" Margin="0,0,12,0" Background="#FF3F3F3F" Foreground="White" BorderThickness="0" Cursor="Hand"/> <Button Name="RestartButton" Content="Restart Now" Width="140" Height="36" Background="$AccentColor" Foreground="White" BorderThickness="0" Cursor="Hand"/> </StackPanel> </Grid> </Border> </Window> "@ } function Get-FullScreenInterstitialXaml { <# .SYNOPSIS Returns XAML for a full-screen interstitial prompt (RUXIM-style) .DESCRIPTION Creates a full-screen, borderless, topmost window replicating Microsoft's Windows 11 upgrade prompt pattern (RUXIM). Designed for critical updates when deferral deadline has elapsed. #> [CmdletBinding()] param( [Parameter()] [string]$Title = "Action Required", [Parameter()] [string]$Subtitle = "IT Department", [Parameter()] [string]$Headline = "Your device requires attention", [Parameter()] [string]$Message = "Important updates must be installed now. Please save your work. Your device may restart after updates are applied.", [Parameter()] [string]$AccentColor = "#0078D4", [Parameter()] [string]$UpdateButtonLabel = "Update Now", [Parameter()] [string]$DeferButtonLabel = "Remind me in 1 hour", [Parameter()] [bool]$ShowDeferButton = $false, [Parameter()] [string]$CountdownLabel = "Updating automatically in:", [Parameter()] [string]$HeroImagePath = "" ) # Hero image section (optional) $heroImageSection = "" if ($HeroImagePath -and (Test-Path $HeroImagePath -ErrorAction SilentlyContinue)) { $heroImageSection = @" <!-- Hero Image --> <Image Grid.Row="0" Source="$HeroImagePath" Stretch="UniformToFill" Height="200" HorizontalAlignment="Stretch" VerticalAlignment="Top"/> "@ } # Defer button visibility $deferVisibility = if ($ShowDeferButton) { "Visible" } else { "Collapsed" } return @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="$Title" WindowStyle="None" AllowsTransparency="False" WindowState="Maximized" Topmost="True" ShowInTaskbar="False" ResizeMode="NoResize" Background="#FF0A0A0A"> <Window.Resources> <!-- Button Style - Matches Windows 11 Fluent Design --> <Style x:Key="PrimaryButtonStyle" TargetType="Button"> <Setter Property="Background" Value="$AccentColor"/> <Setter Property="Foreground" Value="White"/> <Setter Property="FontSize" Value="16"/> <Setter Property="FontWeight" Value="SemiBold"/> <Setter Property="Padding" Value="32,14"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#FF1A86D9"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" Value="#FF005A9E"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="SecondaryButtonStyle" TargetType="Button"> <Setter Property="Background" Value="#FF2D2D30"/> <Setter Property="Foreground" Value="White"/> <Setter Property="FontSize" Value="14"/> <Setter Property="Padding" Value="24,12"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="BorderBrush" Value="#FF555555"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6" Padding="{TemplateBinding Padding}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#FF3D3D40"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> $heroImageSection <!-- Main Content Area --> <Grid Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="800"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- Icon --> <Border Grid.Row="0" Width="80" Height="80" CornerRadius="40" Background="$AccentColor" HorizontalAlignment="Center" Margin="0,0,0,30"> <TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="40" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <!-- Title --> <TextBlock Grid.Row="1" Text="$Title" FontSize="42" FontWeight="Light" Foreground="White" HorizontalAlignment="Center" Margin="0,0,0,8"/> <!-- Subtitle (Company Name) --> <TextBlock Grid.Row="2" Text="$Subtitle" FontSize="16" Foreground="#FF888888" HorizontalAlignment="Center" Margin="0,0,0,30"/> <!-- Headline --> <TextBlock Grid.Row="3" Text="$Headline" FontSize="20" FontWeight="SemiBold" Foreground="White" HorizontalAlignment="Center" Margin="0,0,0,16"/> <!-- Message --> <TextBlock Grid.Row="4" TextWrapping="Wrap" TextAlignment="Center" FontSize="16" Foreground="#FFCCCCCC" HorizontalAlignment="Center" MaxWidth="600" Margin="0,0,0,40"> $Message </TextBlock> <!-- Countdown Timer --> <StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,30"> <TextBlock Text="$CountdownLabel " FontSize="16" Foreground="#FF888888"/> <TextBlock Name="CountdownText" Text="5:00" FontSize="16" Foreground="$AccentColor" FontWeight="Bold"/> </StackPanel> <!-- Buttons --> <StackPanel Grid.Row="6" Orientation="Horizontal" HorizontalAlignment="Center"> <Button Name="DeferButton" Content="$DeferButtonLabel" Style="{StaticResource SecondaryButtonStyle}" Visibility="$deferVisibility" Margin="0,0,16,0"/> <Button Name="UpdateButton" Content="$UpdateButtonLabel" Style="{StaticResource PrimaryButtonStyle}"/> </StackPanel> </Grid> <!-- Close hint (small text at bottom) --> <TextBlock Grid.Row="1" Text="Press Escape to minimize (update will proceed automatically)" FontSize="12" Foreground="#FF555555" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,40"/> </Grid> </Window> "@ } function Get-ToastNotificationWithActionsXaml { <# .SYNOPSIS Returns XAML for an enhanced toast notification with action buttons .DESCRIPTION Creates a toast-style WPF window with Update Now, Defer, and Dismiss buttons for interactive user response when native toast isn't available. #> [CmdletBinding()] param( [Parameter()] [string]$Title = "Update Available", [Parameter()] [string]$Message, [Parameter()] [string]$AppName, [Parameter()] [string]$AccentColor = "#0078D4", [Parameter()] [string]$UpdateLabel = "Update Now", [Parameter()] [string]$DeferLabel = "Later", [Parameter()] [int]$DeferralsRemaining = 5, [Parameter()] [bool]$CanDefer = $true ) $deferEnabled = if ($CanDefer) { "True" } else { "False" } $deferOpacity = if ($CanDefer) { "1" } else { "0.5" } $deferContent = if ($CanDefer) { "$DeferLabel ($DeferralsRemaining)" } else { "No deferrals left" } return @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Toast" Height="160" Width="420" WindowStyle="None" AllowsTransparency="True" Background="Transparent" Topmost="True" ShowInTaskbar="False"> <Border CornerRadius="8" Background="#FF1F1F1F" BorderBrush="#FF3F3F3F" BorderThickness="1" Margin="10"> <Border.Effect> <DropShadowEffect BlurRadius="15" ShadowDepth="3" Opacity="0.5"/> </Border.Effect> <Grid Margin="15"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="40"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="30"/> </Grid.ColumnDefinitions> <!-- Icon --> <TextBlock Grid.Row="0" Grid.Column="0" Text="" FontFamily="Segoe MDL2 Assets" FontSize="24" Foreground="$AccentColor" VerticalAlignment="Top"/> <!-- Content --> <StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" Margin="10,0"> <TextBlock Name="TitleText" Text="$Title" Foreground="White" FontSize="14" FontWeight="SemiBold"/> <TextBlock Name="AppNameText" Text="$AppName" Foreground="$AccentColor" FontSize="12" Margin="0,2,0,0" Visibility="{Binding HasAppName}"/> <TextBlock Name="MessageText" Text="$Message" Foreground="#AAAAAA" FontSize="12" TextWrapping="Wrap" Margin="0,4,0,0"/> </StackPanel> <!-- Close Button --> <Button Name="CloseButton" Grid.Row="0" Grid.Column="2" Content="" FontFamily="Segoe MDL2 Assets" Background="Transparent" Foreground="#888888" BorderThickness="0" Cursor="Hand" VerticalAlignment="Top"/> <!-- Action Buttons --> <StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0"> <Button Name="DeferButton" Content="$deferContent" IsEnabled="$deferEnabled" Opacity="$deferOpacity" Background="#FF3F3F3F" Foreground="White" BorderThickness="0" Padding="12,6" Margin="0,0,8,0" Cursor="Hand"> <Button.Template> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </ControlTemplate> </Button.Template> </Button> <Button Name="UpdateButton" Content="$UpdateLabel" Background="$AccentColor" Foreground="White" BorderThickness="0" Padding="12,6" Cursor="Hand"> <Button.Template> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </ControlTemplate> </Button.Template> </Button> </StackPanel> </Grid> </Border> </Window> "@ } |