9 May
In my last post I wrote about how you could make a WPF Splash screen window in PowerShell, but I stated that: “if the images are remote, the WPF window has to download them, and therefore won’t work” correctly. I had played with downloading images directly by setting the Source attribute of the image to the image URL, and hadn’t been able to find a way to get the threading to work well enough to actually download the image.
Well, I figured that out, so I thought I’d go ahead and share … basically, all you have to do is call Invoke() on the window’s dispatcher. The reason that works is that WPF handles events and “work” in general in the order it needs to be done, so since the most important thing is drawing the UI it will do that first, and since your UI is based on downloading the image, it will take care of that first.
All of this code is written to target the CTP2 release of PowerShell v2 — it will not work in PowerShell v1 or even in v2 CTP1 … and it may not work in later releases, although I expect it will. Also, as with any code which uses WPF from PowerShell, these code examples require you to launch PowerShell with the -STA option.
Having said that, lets just straight to a WPF example
That’s almost as simple as it could get, but let me explain a few things which I left out previously, in case you’re new to XAML or even WPF. The Window tag is one of several acceptable root tags in XAML, but it’s the only one which will let you POP (Short for POP3 (Post Office Protocol 3 - for email), the Post Office Protocol) up a new window (in PoshConsole, you can display XAML inline using Out-WPF, and you can use almost any XAML nodes, but for now, lets focus on what works in general in the CTP2 console). The xmlns attribute is required in order for the xml to be verifiable to the schema, but in XAML it’s particularly important because without it, this would just be XML —not Xaml— and wouldn’t produce UI.
There’s several fun tricks you can do in WPF with the Window that you can’t do in Windows Forms without a lot of hacking. ShowInTaskbar is hardly worth mentioning (it just does what it says). The Opacity attribute is kind of cool — it just controls the translucency level (valid values are from 0 to 1, but below the smaller values aren’t really very useful) — but you can animate this pretty easily to cause your windows to fade in and out. But there are a few that are more impressive:
One of my favorite features of WPF windows it that you can use SizeToContent to have the form automatically take the size of whatever is inside it (which is really useful when you’re loading images off the web). You have to be careful with this though, because WPF is resolution independent but it’s also aware of how many pixels-per-inch images have been saved with. Typically, if you have a PNG (Portable Network Graphics) file, it’s been saved at something like 200 or 300 pixels per inch to make it printable … but your screen’s resolution is only (approximately) 96 pixels-per-inch. This results in images being displayed, by default … at HUGE sizes compared to what you expect. Caveat emptor.
A WPF window with AllowsTransparency set to true can be non-rectangular! It’s very important that you understand this isn’t about allowing translucency (that is, it has nothing to do with the Opacity setting). With this setting on, your window will basically not exist in any spot where there’s no color (See demo4 below). It will be not only be invisible — mouse-clicks won’t register. You’ll also be able to have true per-pixel transparency, with the ability to set the alpha level of each pixel, since WPF colors can be specified as #AARRGGBB quads.
This wouldn’t merit mentioning, except that it has a tie-in with Transparency. You can set the WindowStyle in Windows Forms too, and just like Windows Forms, the valuesfor WPF are None, SingleBorderWindow, ThreeDBorderWindow, and ToolWindow … however, if you use AllowsTransparency, the only valid value for WindowStyle is “None.”
As with WindowStyle, this attribute is mostly special because of it’s exceptions. You can use WindowStartupLocation to specify where you want the form to appear. Valid values are Manual, CenterScreen, and CenterOwner. CenterScreen is the obvious choice for a splash screen, but I should point out something about the other two as well. CenterOwner means to center the window on it’s parent window … but it only works with other WPF windows. If you don’t set the owner to a WPF window, CenterOwner works just like Manual. The Manual setting allows you to specify the window position with the Top and Left attributes, or just let Windows position you in the default location.
There’s one line of code in the sample above which bears explaining — the call to Dispatcher.Invoke. The point of this call is to basically transfer the thread control to the window for a moment. In fact, the signature of the Invoke method is that it takes a priority (I use render because it’s basically immediate), a delegate (I pass a scriptblock), and arguments for the scriptblock. PowerShell will let you easily cast a scriptblock to a delegate method which takes two object parameters and doesn’t return a value … but it won’t let you convert them to any other method without using reflection and IL.Emit. Thus, we cast our scriptblock to any handy delegate type, and pass $null for both parameters.
Of course, we don’t like having to manually update the window by calling Invoke(), but I still haven’t figured out how to spin out another thread safely … so the only other option is to just give control to the window (actually I think this could be done with background jobs, but I can’t seem to get the WinRM CTP configured properly).
Giving control to the window is actually the normal way of doing UI programming … and isn’t really a very big deal in PowerShell — you can write your event handlers for the controls on the window, and then call ShowDialog() and you’re off. Just to give you an example, I’ve tweaked an old WPF sample I had and ported it to PowerShell … the xaml file is here and I’ll paste the code (with comments) inline, but if you may also download the PowerShell script. You need both files for it to work, because the clock.xaml file is the only argument to the demo-wpf4.ps1 script 
Check it out:

Post ScriptI should probably mention that I coded the XAML for that clock by hand, using kaxaml, rather than using Expression Blend — I don’t necessarily recommend it, but it’s good practice once in a while.
XAML supports a lot of animations, and I spent almost an entire day trying different things to create a smooth animation for the CPU and RAM bars which would let them animate smoothly from one value to the next. In the end I decided it would have to be written in code rather than XAML markup, and would complicate things even more than they already were.
2 Responses for "WPF From PowerShell - Updating Windows"
[...] WPF From PowerShell – Updating Windows (Joel Bennett) [...]
Great WPF sample(s). I hope you will continue
One note: [DateTime]::Now.ToString(“hh:MM.ss”) should be maybe [DateTime]::Now.ToString(“hh:mm.ss”).
I think WPF will persuade many people to start with Powershell.
Send a comment