July 2011

Volume 26 Number 07

Windows PowerShell with WPF - Secrets to Building a WPF Application in Windows PowerShell

By Doug Finke | July 2011

Windows PowerShell provides a new class of task automation. It doesn’t displace old technologies so much as amplify them. Using Windows PowerShell (simply PowerShell hereafter for brevity) doesn’t mean you should redo your application to take advantage of it. Rather, you can use PowerShell to seamlessly integrate and extend what you already have.

PowerShell is an automation technology presented as a command-line interface (CLI), scripting language and API.

In this article I’ll walk through key PowerShell techniques and build a present value calculator (bit.ly/7oEij1) with a Windows Presentation Foundation (WPF) GUI (see Figure 1).

The PowerShell Present Value Calculator

Figure 1 The PowerShell Present Value Calculator

I’ll introduce several key PowerShell elements: the WPF PowerShell Kit (WPK); the object pipeline; functions; PSObjects with properties; seamless integration with the Microsoft .NET Framework; modules and more.

PowerShell is built on the .NET Framework, letting you smoothly access the framework as you would from other .NET languages. Plus, you have access to the rest of the Windows OS and its components such as services, processes and Windows Management Instrumentation (WMI), and access to remote servers (that also have PowerShell version 2 and Windows Remote Management enabled).

All this is accomplished with the new scripting language exposed by PowerShell. All you need is Notepad and a few PowerShell cmdlets (pronounced “command-lets,” a cmdlet is a lightweight command that’s used in the PowerShell environment). The good news is, these are ready to go. The cmdlets are built into PowerShell and Notepad comes with Windows. The three key cmdlets are: Get-Help, which displays information about PowerShell commands and concepts; Get-Command, which gets basic information about cmdlets and other elements of PowerShell commands; and Get-Member, which gets the “members” (properties and methods) of objects.

It’s all about discovery, and these cmdlets help you navigate this new task-automation platform.

Let’s Get Started

Question: How many lines of code does it take to create a WPF app with a label saying “Hello World”?

Answer: Two, if you’re using PowerShell and WPK, as shown in Figure 2.

A Two-Line PowerShell WPF Application

Figure 2 A Two-Line PowerShell WPF Application

That’s a complete WPF application written in two lines of PowerShell. Line 1, Import-Module WPK, imports the WPK package, which contains a set of PowerShell cmdlets that wrap WPF. Interestingly, you don’t need Visual Studio, XAML or C# to get this to work. You do need to install WPK, though (see next section).

PowerShell version 2 is available out of the box in Windows 7 and Windows Server 2008 R2 (and it’s downloadable for older Windows systems). At the same time the client and server OSes were released, the PowerShell Pack was released (as a separate download), including the WPK. It’s a hat tip to the popular Unix scripting tool, Tcl/Tk.

I’ll start by building the application from a simple set of PowerShell variables to an interactive WPF application. I’ll use the PowerShell Integrated Scripting Environment (ISE).

Want to Follow Along?

If you have Windows 7, you’re almost ready to go (remember, PowerShell is built-in).

If you’re not running Windows 7, download and install PowerShell for older OSes. Be sure to choose the correct OS download. See the Windows Management Framework Core package (bit.ly/9POYjq).

No matter your OS version, you need to download and install the WPK (bit.ly/dFVpfL). Part of the Windows 7 Resource Kit, it contains nine other PowerShell modules, including an ISE Pack for use in the PowerShell ISE. The ISE is available out of the box with PowerShell version 2. The ISE Pack is a great learning resource as well, showing how to customize the ISE at several levels.

Once you’ve launched PowerShell, run this cmdlet: Set-ExecutionPolicy RemoteSigned. Out of the box, PowerShell is set up to not run scripts; this is a security feature and PowerShell users need to override this. For the Set-ExecutionPolicy to work, you need to have administrator rights and explicitly run PowerShell as an administrator by right-clicking on the PowerShell program file and selecting “Run as administrator.”

Download and unzip the scripts from the accompanying code download. The simplest way to run the application is to run it in the ISE. On Windows 7 you can click Start and type ISE. (Note: You can’t run PowerShell scripts—which have a .ps1 file extension—by double-clicking on them. The easiest way to run the example scripts is to launch the ISE and use File | Open to open the script file.)

PowerShell 101

I’m going to build a present value calculator; this is a simple formula, shown in Figure 3.

Present Value Calculation

Figure 3 Present Value Calculation

Variables in PowerShell begin with a $. In line 7 I use the .NET Framework directly, calling the static method Pow on the System.Math namespace. The Pow method returns a specified number raised to the specified power. The syntax needed to call a static .NET method is brackets around the class followed by two colons and then the name of the method: [System.Math]::Pow(2,3). If you’re running this in the ISE, press F5 (or click the Run button) to see the results in the output pane. You could also copy and paste this code into the PowerShell command-line console; it will run and print the result.

This is a great start but not very reusable. I could keep typing new values and running this script, but I want to call it from other scripts. I’ll add the function keyword and turn the variables into parameters so I can vary the output and make the script interactive (see line 2 in Figure 4).

Creating a PowerShell Function

Figure 4 Creating a PowerShell Function

Adding the Function Keyword

I’m going to name the function Get-PresentValue. It’s good practice to follow the PowerShell verb-noun convention when naming functions—this is a fundamental concept in using and developing for PowerShell. It has predefined verbs such as Get, Invoke, New and Remove (type Get-Verb to see the entire list). Try typing Get-Command; this returns all the cmdlets (and more) defined in your PowerShell session, and it’s a huge list.

Creating a function is as simple as typing function Get-PresentValue {}. From here I’ll create the parameters, with default values and the body of the function, as seen in Figure 4.

Comparing Figure 3 to Figure 4, I transposed the variables—both the names and the values—to a single line and separated them by commas, wrapped them in parentheses and placed them after the name of the function, making them parameters to the function. These parameters now have default values. I left the present value calculation as is. A key tenet in PowerShell is “less typing.” In line 3 in Figure 4, I could’ve used the return statement. Omitting it has the same behavior, although there are times you want to use the return statement to break the flow of logic.

In this example I show a few ways you can call the Get-PresentValue function. First, without parameters, all the parameters are defaulted. Parameters can be supplied either by position (see line 8 in Figure 4) or they can be named (see line 9). I recommend reading up on PowerShell parameters; PowerShell supports a powerful parameter-binding engine.

Next up: changing the Get-PresentValue function to return a .NET object rather than simple text.

PowerShell Is Based on .NET

A key innovation in PowerShell is the ability to pass data as fully typed objects. Figure 5 introduces the concept of creating a PowerShell object, setting properties, returning the object and leveraging the PowerShell engine to print it out.

Return Fully Typed Objects from PowerShell Functions

Figure 5 Return Fully Typed Objects from PowerShell Functions

 In line 6, I use the cmdlet New-Object, which creates an instance of a .NET Framework object. I tell it the type of object to create, a PSObject, which allows for a consistent view of any object within the PowerShell environment. Also in line 6, I’m using the -Property parameter, which takes a hash table. A shorthand syntax for hash tables in PowerShell is @{}. The key/value pairs defined in the hash table—and passed in the -Property parameter—are transformed into properties and values on the new PSObject.

Finally, in line 15, I call the function, and results can be seen in the output pane (shaded blue in the ISE). Notice that PowerShell “knows” how to print the object. I don’t have to do any reflection to figure out what properties to print or how to print them—a key strength of PowerShell.

PowerShell Ranges and Pipelines

Next, I’ll use PowerShell pipelines and present two more PowerShell cmdlets: ForEach-Object and Format-Table (see Figure 6).

PowerShell Ranges and Pipelines

Figure 6 PowerShell Ranges and Pipelines

Line 13 is the chewy piece and begins to give insight into the flexible and compositional quality of PowerShell. There are three sections and two pipes defined here. The first section shows the range operator (consisting of two periods), the second section is the ForEach and the last section contains the Format-Table. I’ll discuss each.

First Section, 1000..10101000..1010 represents an array of integers from 1,000 to 1,010, inclusive. Now PowerShell will start pushing these one at a time down the pipeline as soon as one is available. PowerShell, like Unix/Linux-based shells, implements a pipeline, which enables the output of one cmdlet to be piped as input to another cmdlet. With PowerShell, the pipeline consists of .NET objects. Using objects eliminates the need to parse arbitrary text output from one command to extract data, as all objects export a consistent interface (see bit.ly/lVJarT).

Second Section, ForEach { Get-PresentValue $_ } This section uses ForEach (also aliased to %), which takes a scriptblock. Think of a scriptblock as an anonymous method (sometimes called lambda expressions in other languages). For more on this, see the book, “PowerShell in Action, Second Edition,” by Bruce Payette (Manning Publications, 2011).

 $_ is a PowerShell automatic variable and contains the current object in the pipeline. The end result, an array of 10 integers, is passed one integer at a time to Get-PresentValue. Because we aren’t naming the parameter, I’m passing it as a positional parameter to $Amount, as seen in the output pane in Figure 6.

Last Section, Format-Table Format-Table does what it says; it formats the output as a table. I use the -AutoSize parameter because it adjusts the column size based on the width of the data.

Note that I’m not managing the iteration over the collection, and PowerShell “knows” how and what to print about the object being pushed over the pipeline. This results in writing fewer lines of code, which means fewer lines to debug. Because I spend 90 percent of my time debugging and the other 10 percent writing bugs, I come out nicely ahead.

It’s GUI Time—Round 1

It’s time to use the WPK. The script in Figure 7 produces the GUI in Figure 8. I added type information to the parameters of Get-PresentValue function (see line 1 in Figure 7). This helps others using this function to easily detect if they passed along wrong data—for example, strings rather than numerics.

The WPK New-ListView

Figure 7 The WPK New-ListView

Viewing the GUI

Figure 8 Viewing the GUI

The essence of the original code in Figure 6 has been retained and the calling of Get-PresentValue has been added to the DataContext of the ListView, which is a WPF control providing the infrastructure to display a set of data items. The rest of the the WPK pieces integrate with WPF databinding and set up the view in the ListView so the data can be displayed.

The WPK follows the fundamental tenet of PowerShell, using the verb-noun pair. So, if I want to create a Window, Grid, Canvas or ListBox, I simply add “New-” to them—New-Window, New-Grid, New-Canvas or New-ListBox—and these framework elements are ready to use.

Import-Module

A module is a package that contains members—such as cmdlets, scripts, functions, variables, and other tools and files—that can be used in PowerShell. After a module is imported, you can use the module’s members in your session. As noted earlier, the WPK is part of the PowerShell Pack, and the WPK contains more than 700 PowerShell functions that simplify layering a WPF GUI over PowerShell.

Coming from a traditional WPF background, lines 17 and 18 in Figure 7 may seem unusual. The WPK supports data binding by surfacing two parameters: DataContext and DataBinding. The DataContext parameter takes a scriptblock, so here I pass a line of PowerShell code that creates the 10 present value objects I had in Figure 6, line 13. Next, I set up the ItemsSource property of the ListView to bind to in line 18. The DataBinding parameter takes a hash table (note the @{}). The keys are the names of the properties of the GUI object you want to bind to.

The WPK helps you write less code. The New-GridViewColumn function takes a string—for example, Amount (which is the name of a property on the object emitted from the Get-PresentValue function)—sets it as the Header and automatically does the data binding for you.

I started out with a simple function, Get-PresentValue, and ended up with the output shown in Figure 8 by adding parameters to make it reusable, emitting a .NET PowerShell PSObject and leveraging the PowerShell object pipeline and range operator to generate present value items. Then I imported the WPK, injecting the PowerShell objects into the DataContext of a WPF ListView control, binding the ItemsSource parameter and creating the ListView view with column names that match the property names of the object injected. Finally, I have a simple present value PowerShell/WPK application. This is nice, but it’s hardcoded.

Up next, I want to interact with this application so I can see what happens to my investment by changing the amount, interest and other parameters.

It’s GUI Time—Round 2

The current PowerShell/WPK script requires me to change the parameters, save the file and rerun the script, which isn’t very agile.

I’m going to rework it so I can tweak five parameters from the GUI and display new values in the ListView.

New-Range Function First up, I want to add a function called New-Range (see Figure 9).

The New-Range Function

Figure 9 The New-Range Function

The range operator PowerShell provides doesn’t let you vary how much you increment by. In other words, I can’t specify (1..10 by 2). The New-Range function will let me specify my own increment, so “New-Range 1 10 2” prints 1 3 5 7 9.

Next, I’ll flesh out the WPK GUI by wrapping New-ListView in a New-Window function. Using the New-ListView by itself, the WPK will wrap in a window for you. Specifying New-Window gives me more control (see Figure 10).

The New-Window Function

Figure 10 The New-Window Function

I also lifted the DataContext from the ListView up to the Window scope and applied the new function New-Range. Now I want to add five text boxes, labels and a button. This will allow me to keep the application running and tweak the output of Get-PresentValue. I’m going to use the WPK New-Grid, New-Label, New-TextBox and New-Button functions to create the controls I need. Using New-Grid to create a grid control gives me flexibility in resizing the window and control placement.

In order to lay out the GUI, I’m going to nest grids within grids as well as controls. I’m still using the New-Range and Get-PresentValue functions in the DataContext, as shown in line 2 of Figure 11.

The New-Grid Function
(click to zoom)

Figure 11 The New-Grid Function

Also, the New-ListView is still there in Figure 11, lines 30-37. I added two additional parameters in line 30, –Row and –Column, which tell the ListView which row and column it should be located in, in the New-Grid defined in line 5. The New-Grid defined in line 5 uses the Rows and Columns to lay out a grid. I want two columns with a width of 75 pixels and three rows with a height of 35 pixels each. The asterisk after the second 75 in the Rows parameter indicates it will take all the space available.

Now I place five pairs of labels and text boxes in the window, telling the controls what quadrant of the grid to anchor to via the Row and Column parameters. Also, I name the text box so I can access it later, give it a default value with the -Text parameter and embellish the controls with the Margin and HorizontalAlignment parameters.

Finally, I place the button in the grid with the New-Button function. Notice I place an underscore in front of Calculate. This lets me access the button with the keystroke Alt-C.

The present value calculator is almost complete. I have to hook up the click event to some PowerShell code, read the values in the text boxes, pass them as parameters to the Get-PresentValue and bind it to the ListView.

Do-Calculation Function

I’m going to add a Do-Calculation function that takes a single parameter, $window (see Figure 12).

The Do-Calculation Function

Figure 12 The Do-Calculation Function

I’ll call Do-Calculation from the -On-Click property on the New-Button, the one with the content “_Calculate.”

Do-Calculation is straightforward. It grabs the information in all the text boxes, sets them to PowerShell variables and passes them as parameters to the New-Range and Get-PresentValue functions. I pass the parameter $window (see line 2 in Figure 13), which contains a reference to the New-Window created in Figure 10. Using this, I can get to all the properties of the window as well as the child controls, specifically the text boxes. Because I named each of the text box controls, I can pipe $window to Get-ChildControl, passing the name of the control and retrieving the text through the .Text property (see lines 3-7 in Figure 12).

Completed Grid
(click to zoom)

Figure 13 Completed Grid

Gathering all the details from the text boxes, I set the $window.DataContext to the result of the New-Range being piped to Get-PresentValue, which generates an array of PowerShell objects containing the results of PresentValue calculations (see lines 9-12 in Figure 12).

Figure 13 shows how to connect up the Calculate button’s click event to call the Do-Calculation function and pass the $window variable (see lines 28-29). I added the -On_Click parameter, which takes a ScriptBlock. In there I call the Do-Calculation function, passing in the $window variable that’s created for me when I use the New-Window function. Every time I click the button, the screen will recalculate. Note that I also changed line 2 in Figure 13 to also call the Do-Calculation function.

Download the completed application in the accompanying source code samples to see the entire script.

A Simple, Interactive WPF Application

I presented here a script of fewer than 75 lines of code, resulting in an interactive WPF application layered over the Microsoft automation platform, PowerShell. PowerShell manifests in both a scripting language and command-line console. Its deep integration with both the .NET Framework and Windows enables exciting automation opportunities. Of course, this means one must invest time in learning this new platform. The good news: You can engage with the low-hanging fruit to become more productive and then, when you need to, deep dive into the large offering of automation that PowerShell provides, both from Microsoft and the general PowerShell community.

The Ad Hoc Development Model and the Origin of Windows PowerShell

Doug Finke’s article is an excellent example of the ad hoc development model. PowerShell differentiates itself from other programming technologies in many ways: its emphasis on high-level, task-oriented abstractions; its adaptive type system that normalizes different type systems (.NET, Windows Management Instrumentation [WMI], XML, ADSI, ADO and so on) and allows you to add members to types and instances; its dataflow engine that eliminates much of the API impedance mismatch code developers have to write; and its support for the ad hoc development model.

The ad hoc model is where you start off solving a problem using informal techniques. When you decide you’re going to use it more, you convert it into an informal script, and if you share it, you make it more formal. As tool builders, we often build tools for people with different skill sets, so it’s important to meet the needs and expectations of our entire audience. Often that means delivering a GUI.

Doug’s article starts off with a nameless, hard-wired script to produce the PresentValue for a certain amount of money, a fixed interest rate and time. He then turns it into a named function with named parameters and initial values returning a single value. Next he returns an object so that it can be manipulated by other tools. And eventually he turns it into a simple GUI, and then a richer one. He only invests when he needs to, and each script adds small increments to the previous one. In the end, Doug shares his script, allowing other people to use his tool and also offer suggestions about how to make it better (such as, when suggested, he “typed” his parameters so his tool didn’t barf if someone passed strings). We all benefit from sharing. I got a cool tool and owe a debt of gratitude to Doug. I partially repaid that debt by reviewing his code and offering suggestions. I know a bit about PowerShell, and yet I still benefit greatly from all the suggestions the community gives me on my scripts. [Snover is the inventor of Windows PowerShell and one of the principal designers, along with Bruce Payette and James Truher; seebit.ly/696Jor.—Ed.]

The ad hoc development model comes from the PowerShell goal to be both a great interactive shell and a great scripting language. Bruce Payette, one of the language designers, once said that the lifespan of 99 percent of PowerShell scripts starts with the command prompt and ends with the carriage return. PowerShell supports a wide range of scripting styles, starting from interactive one-liners at a command prompt to Bash-style functions using $args, to more formal scripts where parameters are named, typed and decorated with validation, data binding and help attributes. The reason we took this approach stems from my many years as a Unix developer, when I wrote tons of shell scripts. As people used my scripts and requested more features, I found myself throwing them away and rewriting in Tcl or Perl. Often I’d end up throwing those away as well and rewriting it all in C. It struck me that, while different problems 
require different levels of formalism and performance, it was insane that there wasn’t a single tool that could span this wide range of scripting needs. That way, people could invest in becoming an expert in that tool versus being sort of competent in a large set of tools. It took awhile, but I finally got around to producing a tool that would do just that. I hope you enjoy PowerShell.

— Jeffrey Snover, Distinguished Engineer and Lead Architect for Windows Server


Doug Finke*, a Microsoft MVP for Windows PowerShell, is a software developer at Lab49, a company that builds advanced applications for the financial services industry. For the past 20 years, he has been a developer and author working with numerous technologies. You can catch up with him at his blog, Development in a Blink, at dougfinke.com/blog.*

Thanks to the following technical experts for reviewing this article: James Brundage, Sal Mangano, Sivabalan Muthukumar, Marco Shaw, Jeffrey Snover and Sylvain Whissell