WPF & PowerShell - Part 7 (Sharing Hosts)

WPF & PowerShell - Part 7 (Sharing Hosts)

  • Comments 5

Well here we are at the end of a week of WPF.  We've learned how to create basic, simple user interactive interfaces.  We've seen a brief glimmer of the golden UI layer that is WPF, and have seen how we can use PowerShell to add easy interactivity to XAML.  You've seen tricks to help you work your way through .NET code, and help you unwrap the mysteries of WPF. We've seen how we can use PowerShell's list processing technology allows for simple binding to WPF's controls, and how WPF applets can help you present a simple front ends to PowerShell functionality.  We've gotten a very brief taste of what the pipeline can bring to User interfaces, and we've showed you how to make controls that run in the background so you can build your control in PowerShell and still use PowerShell.

The last trick to learn is how to maintain the streaming behavior with PowerShell.  There are two cases that are useful: streaming data into a command so that you can update information in a control, and streaming information from a control.  While some commands have taken input and output, they have not updated their contents as items come in from the pipeline, nor have they outputted items onto the pipeline while still showing as a dialog.  The trick to doing both of these things was there in yesterday's post.  The update to Show-Control happens to include a line that makes sure the background runspace has a variable pointing to the foreground runspace.  Using this, and a new CTP2 feature, PowerShell events, you can stream data from one runspace to the other so you can update a control as data comes in.  You can also generate events in the host runspace, which gives you a way to stream data back from the original command.  Having the host runspace also lets you run code in that runspace, and, since you have your original host, you can also write output, error messages, progress bars, and more in the original runspace from the background runspace.

Since you can share simple data between runspaces in this way, you could also use PowerShell to create mutli-document interfaces with WPF.  With PowerShell remoting in the picture, you can easily make front ends or status monitors for remote tasks, or use a hefty remote backend to make working on lightweight clients easier (remember, both PowerShell v2 and WPF run on all operating systems after XP).

For the final sample we'll go back to our Select Command example, and we'll give it an option to output the help in the current runspace, or get the command syntax.  As you can see below, the code that produces this is still pretty short.  With a few new buttons come a few new click handlers, but writing to the host output is quick, simple, and easy (remember, $parentHost is a variable set by Show-Control in a background runspace it creates).  All of the code that interacts with the original host is in the last 3 event handlers.

@"
<StackPanel xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<Label FontSize='14'>Type a Command</Label>
<TextBox Name="CommandTextBox"/>
<ListBox Name="CommandListBox" Width='200' Height='200'/>
<Button Name="ShowHelp" FontSize='14'>Show Help</Button>
<Button Name="ShowSamples" FontSize='14'>Show Samples</Button>
<Button Name="OutputCommand" FontSize='14'>Output</Button>
</StackPanel>
"@ | Show-Control @{
   "CommandTextBox.TextChanged" = {      
       $listBox = $window.Content.FindName("CommandListBox")
       $textBox = $window.Content.FindName("CommandTextBox")
       $listBox.ItemsSource = @(Get-Command "*$($textbox.Text)*" | % { $_.Name })
   }
   "CommandListBox.SelectionChanged" = {
       $textBox = $window.Content.FindName("CommandTextBox")
       $textBox.Text = $this.SelectedItem
   }
   "ShowHelp.Click" = {
       $textBox = $window.Content.FindName("CommandTextBox")
       $helpText = (Get-Help $textBox.Text -detailed -ea SilentlyContinue) | Out-String      
       $parentHost.UI.Write($helpText)
   }
   "ShowSamples.Click" = {
       $textBox = $window.Content.FindName("CommandTextBox")
       $helpText = (Get-Help $textBox.Text -example -ea SilentlyContinue) | Out-String      
       $parentHost.UI.Write($helpText)
   }
   "OutputCommand.Click" = {$parentHost.UI.Write($textBox.Text)}
} -backgroundRunspace

This series should have armed you with the technical skill set required in order to create user interfaces in script.  The series has been largely rebuilt from my notes and scripts built when first exploring the interaction of WPF and PowerShell after STA mode was enabled.  These are designed primarily to be technical examples and a quickstart in the topic.  Hopefully this series has gotten you up to speed with some of the possibilities of using PowerShell and WPF together, but please stay tuned.  While this week has been about giving you the basics, starting next week (and every week) I'll post a new, useful small UI built with WPF and PowerShell.

I hope you enjoyed the series and I hope you get inspired to write a few quick UIs as scripts yourself.

Hope this Helps,
James Brundage [MSFT]

Leave a Comment
  • Please add 4 and 6 and type the answer here:
  • Post
  • Very nice and an excellent series. Thanks.

    I'm gonna have some fun with this!

  • My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. Scott Guthrie announced the availability of ASP.NET MVC Preview 3 . The release was also mentioned by Phil Haack and Scott Hanselman

  • I looked at the start of this series and wondered - why? oh why? I'd want to code GUI by using dotNet function calls to build up a layout button by button....

    Then some weeks later, I re-read the last four articles and went.. "Ohh"... and went on to find out more about XAML... basically with more "Ohhs..." followed by some "Ahhs..."

    However, After more reading on the MSDN site.. I really would like to know how to bind a XAML UI to a Object that is inside the Powershell environment.

    As, I'm now wanting to move beyond:

      $panel  = new-object Windows.Controls.StackPanel

    ls | $panel.Children.Add( $_ )

    To something like

    <ResourceDictionary

     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

     xmlns:local="clr-namespace:SmurfRacing"

     >

     <DataTemplate DataType="{x:Type local:SmurfRacing}">

       <DataTemplate.Resources>

         <LinearGradientBrush x:Key="SmurfHeight" StartPoint="0,0.5" EndPoint="1,0.5">

           ...

           ...

           ...

    But need a few examples of the whole sheebang to get me going.... and also to find out if it is possibe to actually bind a XAML gui to a PSObject...

    Cheers,

      Rob Lancaster.

  • (from the blog post)

    "Having the host runspace also lets you run code in that runspace"

    Can you please provide an example of how to do that?

    Thanks,

    Ben Langton

  • Is there a new tutorial that would show us how to do this with today's version of powershell2 . I get to the day 5 and things start to fall apart the show-control command provided no longer works.

    ie.

    no   cmdlet New-Event

    getting a powershell object is now differnt.

    $psCmd = [Management.Automation.Runspaces.Powershell]::Create($selfDefinition,$true)

    etc..

    please let me know if there is anything

    Thanks

Page 1 of 1 (5 items)