WPF & PowerShell - Part 5 ( Using WPF & PowerShell Modules)

WPF & PowerShell - Part 5 ( Using WPF & PowerShell Modules)

  • Comments 2

In the last post we met XAML, and I gave you a core function (Show-Control) that will help you make interactive WPF controls quite nicely in PowerShell.

You can see that, by using Show-Control, it is possible to reduce the size and complexity of a script that creates UI.  Today, I'll show you how to build small applications by building functions and modules you can use later.

But before we get into that, I'll explain some more of the benefits I see of using scripts to produce user interfaces:

  • Flexible Dependencies
    • If I compile an application, I have forced my customers to forever use the custom controls I have built in an application.  Using script, I can reuse controls that match a signature a run time, instead of being locked into the same choice forever.  I also am encouraged to make each part of my application something that can be flexibly depended on.  This encourages an ecosystem of compact, reusable controls written as scripts.
  • Size / Memory Consumption
    • Because each script is not running in its own application space, the additional overhead of an executable per window goes away.  Also, the scripts themselves are smaller and easier to distribute.
  • Discoverability & the Visual Pipeline
    • All applications, in my opinion, can be thought of as a pipeline.  Much as PowerShell pipelines may end up nested, so may pipelines of interfaces, but I believe it is possible to think of applications in this way.  I also believe that when you think of an application this way, describing the application in Verb-Noun pairs makes is a natural way of thinking about it.  Think, for instance, of the application a customer service representative has to go through to look at your account.  It be thought of like this:

            Get-UserFullName | Search-User | Get-UserConfirmationInformation | Get-UserIdentifyingInformation | Show-UserRecord

      When you think about user interfaces in this way, the benefit of expressing small controls as Verb-Noun pairs becomes clear, as does the usefulness of using Get-Command to help discover what controls exist.

Let's focus on the 3rd benefit as we build up a few controls that rely on each other.  First, let's make a Controls module and put an updated Show-Control in it.  The only difference between this Show-Control and the other Show-Control is that it emits an item.  Whatever is stored in the Tag property of Window is emitted whenever our control is closed.  This means that you can catch when the window closes and pack information into the tag so it can be emitted into the pipeline.  This is important to remember, because we'll use it to create some commands from Get-Listbox.

To create a package, create a directory called Packages under Documents\WindowsPowerShell.  In this directory, create a folder called Controls.  Create a file, Controls.psm1 in this directory.

A PSM1 file is a module file.  It declares a number of functions that you can import as a group by using Add-Module.  In a PSM1 file, a variable, $psScriptRoot exits.  This is the path where the module is installed.  Personally, I like to write my modules so that they just dot source other files in the same directory that contain the functions in the module.  This way, each function is easy to locate because it is in it's own file, but you can still import them all as a group.  Remember, however, that since PowerShell is an interpreted language, you'll need to import the functions in order.  Right now, it doesn't matter, because Get-Listbox doesn't have anything to do with Show-Control yet.

In this module, I'll start with the following lines

. $psScriptRoot\Get-Listbox.ps1

. $psScriptRoot\Show-Control.ps1

Since this series is starting to have a little too much inline code, this time I'm attaching the scripts.  Get-Listbox will return you a listbox from an enumerated type, an existing type, or a list of strings.  Load them into your runspace by typing "Add-Module Control".  When you're done, you might want to add the functions below to the module.

I can easily turn Get-Listbox into Select-Listbox, which will display the control and emit the item the user picked onto the pipeline.

See:

function Select-Listbox() {
   if ($input) {
       $listBox = $input | Get-Listbox
   } else {
      $listBox = $args  | Get-Listbox
   }  
   $listBox | Show-Control @{
      "Window.Closed" = {$window.Tag = $window.Content.SelectedItem}
      "MouseDoubleClick" = {$window.Close()}
   }
}

And I can also put Get-Listbox together with Get-History to give me Get-HistoryListbox, and Select-Listbox together with Get-History to give me Select-History, and Select-History can be used to create Invoke-History

function Get-HistoryListbox() {
    Get-History | % { $_.CommandLine } | Get-Listbox
}

function Select-History() {
   Get-History | % { $_.CommandLine } | Select-Listbox
}

function Invoke-History() {
   Select-History | Invoke-Expression
}

A much more advanced example of using WPF to create a function to select output is called Select-Grid and has been written by Jaykul Bennett  You can find more information about it HERE.

Let's make a couple more quick controls, Get-VideoPlayer and use the same technique to build Show-Video to give you a teaser for tomorrow.

function Get-VideoPlayer($file) {
    $mediaPlayer = New-Object Windows.Controls.MediaElement
    $mediaPlayer.Source = New-Object System.URI (Resolve-Path $file)
    $mediaPlayer.LoadedBehavior = "Manual"
    $mediaPlayer
}

Now lets build a basic Show-Video:

function Show-Video($file) {
    $videoPlayer = Get-VideoPlayer $file
    $videoPlayer.AllowDrop =$true
    $videoPlayer | Show-Control @{
       "Drop"={
          $this.Source = New-Object System.URI ($_.Data.GetFileDropList() | select -first 1)
          $this.Play()
        }
       "Window.Activated" = {$window.Content.Play() }
       "Window.Closed"={$window.Content.Stop()}
    }
}

Tomorrow, we'll take Show-Video and Get-VideoPlayer and turn them into a video player with some controls, and we'll show you how to run controls in the background.

Hope this Helps,
James Brundage [MSFT]

Attachment: Show-ControlAndGet-Listbox.zip
Leave a Comment
  • Please add 8 and 6 and type the answer here:
  • Post
  • PingBack from http://www.alvinashcraft.com/2008/05/26/dew-drop-may-26-2008/

  • OK, so I've created XAML forms from Powershell, and have the event handling technique figured out.  But recently, I've started playing with PowershellASP.  What I'd really love to be able to do is create a web page using XAML (not just a windows application), and implement the code-behind for the events in Powershell, instead of C#.

    I haven't found any solution for this...but I have a feeling that if it were possible, it would blow open a lot more doors for Powershell...

Page 1 of 1 (2 items)