In WPF, there are two types of window which can host your application content. The most commonly used is simply the window (System.Windows.Window in .Net). But there is another window type, the navigation window (System.Windows.Navigation.NavigationWindow) which takes a slightly different form and includes some basic navigation controls similar to what you would use in a web browser.
This kind of window can be useful in certain situations such as hosting documentation or help content, or recording input over several pages, as the user can go back and forward between the content pages using the navigation history.
The key to making the navigation window work is to use a page control (System.Windows.Control.Page) to host your content. Like the window, a page can only contain a single child control, so you need to use a layout control like a grid to contain other window elements, such as textboxes and buttons etc. You can then simply call the relevant page when you navigate.
Below is a simple example of a navigation window which has two actual pages, and the third “page” is a website. The window itself and the pages are defined in XAML, stored to a hash table, then called by simply setting the Content property of the navigation window. In the case of the web page, we use the navigation service to load the page.
When the window is opened, the first page is displayed:
Clicking next loads the second page:
Clicking next again loads the web page:
You’ll notice that after you leave the first page the navigation history is enabled and you can go back to the previous pages.
PowerShell Navigation Window
Add-Type -AssemblyName PresentationFramework # Define Navigation Window in XAML [XML]$Xaml = @" <NavigationWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name = "NavWindow" Width = "600" Height = "400" WindowStartupLocation = "CenterScreen" ResizeMode = "CanMinimize" > </NavigationWindow> "@ # Define Page 1 text $Page1_Text1 = @" Amazon has confirmed its virtual assistant Echo speakers are coming to the UK, Germany and Austria. Until now, the company sold its voice-controlled devices only in the US. "@ $Page1_Text2 = @" The machines can answer questions, control other internet-connected devices, build shopping lists and link in to dozens of third-party services including Spotify, Uber and BBC News. Experts say they appeal to early adopters' sense of curiosity but tend to be a harder sell to others. "@ # Define Page 2 text $Page2_Text1 = @" The company set up to manufacture Europe's next-generation rocket - the Ariane 6 - says it is open to orders. Airbus Safran Launchers (ASL) expects to introduce the new vehicle in 2020. "@ $Page2_Text2 = @" This week, member states of the European Space Agency gave their final nod to the project following an extensive review process. The assessment confirmed the design and performance of the proposed rocket, and the development schedule that will bring it into service. "@ # Define Page 1 in XAML [XML]$Page1 = @" <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name = "Page1" Title="Amazon Echo" > <Grid> <StackPanel> <TextBlock Padding = "10" TextWrapping = "Wrap" FontFamily = "Segui" FontSize = "18" Height = "Auto" Width = "Auto"> $Page1_Text1 <LineBreak /><LineBreak /> $Page1_Text2 <LineBreak /> </TextBlock> <ListBox BorderThickness = "0" Width = "100" HorizontalAlignment = "Left" FontSize = "16"> <ListBoxItem Content = "That's great!" Foreground = "Green" /> <ListBoxItem Content = "I hate it!" Foreground = "Red" /> </ListBox> </StackPanel> <DockPanel Margin = "480,280,0,0"> <Button x:Name = "P1_Next" DockPanel.Dock = "Right" Content = "Next" Height = "30" Width = "50" /> </DockPanel> </Grid> </Page> "@ # Define Page 2 in XAML [XML]$Page2 = @" <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name = "Page2" Title="Ariane 6" > <Grid> <StackPanel> <TextBlock Padding = "10" TextWrapping = "Wrap" FontFamily = "Segui" FontSize = "18" Height = "Auto" Width = "Auto"> $Page2_Text1 <LineBreak /><LineBreak /> $Page2_Text2 <LineBreak /> </TextBlock> <ListBox BorderThickness = "0" Width = "100" HorizontalAlignment = "Left" FontSize = "16"> <ListBoxItem Content = "That's great!" Foreground = "Green" /> <ListBoxItem Content = "I hate it!" Foreground = "Red" /> </ListBox> </StackPanel> <DockPanel Margin = "480,280,0,0"> <Button x:Name = "P2_Next" DockPanel.Dock = "Right" Content = "Next" Height = "30" Width = "50" /> </DockPanel> </Grid> </Page> "@ # Create hash table, add WPF named elements and objects $hash = @{} $Hash.NavWindow = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $xaml)) $Hash.Page1 = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $Page1)) $Hash.Page2 = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $Page2)) $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object -Process { $hash.$($_.Name) = $hash.NavWindow.FindName($_.Name) } $Page1.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object -Process { $hash.$($_.Name) = $hash.Page1.FindName($_.Name) } $Page2.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object -Process { $hash.$($_.Name) = $hash.Page2.FindName($_.Name) } # Add code to handle the button click events, loading the next page $Hash.P1_Next.Add_Click({ $hash.NavWindow.Content = $Hash.Page2 $Hash.NavWindow.Title = "Clear path to Ariane 6 rocket introduction" }) $Hash.P2_Next.Add_Click({ $Hash.NavWindow.Navigate([URI]"http://www.bbc.com") $Hash.NavWindow.Title = "BBC Website" }) # Set the window content to the first page on load $hash.NavWindow.Content = $Hash.Page1 $Hash.NavWindow.Title = "Amazon's Echo speakers head to UK and Germany" # Show the NavWindow $null = $Hash.NavWindow.Dispatcher.InvokeAsync{$Hash.NavWindow.ShowDialog()}.Wait()
Wow, this is a cool PS GUI trick. Many thanks for greatly explaining this!
This is so cool!