This is the fourth release of the PowerBoots module for PowerShell 2 (Download7z). PowerBoots is a framework for creating WPF GUIs from PowerShell, and this latest release brings a couple of bug fixes and two new features.

Now with tab completion

The biggest change in PowerBoots 0.0.4 is that tab-completion for parameters works in the shell.

Behind the scenes what has happened is that I’ve discovered that in the current CTP3 release of PowerShell 2, Dynamic Parameter sets are not handled correctly for aliases, and the Get-Command for aliases of functions doesn’t return the same set of parameters as it does on the function itself. The result is that Tab-Completion for dynamic parameters doesn’t work on aliases (neither the built-in tab completion, nor MoW’s PowerTab). I wrote up a bug against that, but I also found that I can work around it by creating hundreds of functions (all pointed at the same script-block), instead of hundreds of aliases (all pointed at the same function).

PowerBoots produces functions instead aliases

This means the PowerBoots commands show up by default when you run Get-Command. Incidentally, this means that the first time you run Get-Command after you Import-Module PowerBoots you incur a huge penalty (17 seconds on my machine) while Get-Command caches all the dynamic parameter information about the functions. NOTE: because of the DynamicParam bug, I’ve ditched aliases altogether for this particular project and am generating two functions for each element/type in PowerBoots: the full New-Namespace.Type version, and a “Type” alias when possible. Trust me when I say: “it’s worth it” ... with the backing scriptblock, there’s virtually no difference between the two approches in terms of resources (other than the aforementions hit on Get-Command).

Now with more controls

The second big change (and the one which I hope will have the biggest long-term impact) is that you can now import your own WPF control libraries into PowerBoots. This means that you can, for example, go get the open source Visifire charting library, extract their WPFVisifire.Charts library into your PowerShell folder, and use it like so:


Add-BootsContentProperty 'DataPoints', 'Series'
Add-BootsFunction -Assembly "~\Documents\WindowsPowershell\Libraries\WPFVisifire.Charts.dll"

Chart -Width 200 -Height 150 -Theme Theme3 (
  DataSeries $(
      DataPoint -YValue (Get-Random 100)
      DataPoint -YValue (Get-Random 100)
      DataPoint -YValue (Get-Random 100)
      DataPoint -YValue (Get-Random 100)
  )
) | Boots -Title "Sample, Theme 3"
 

The main thing required to add new types to PowerBoots is to call Add-BootsFunction and pass it a Type or an Assembly … however, in order to get the nice clean syntax, and pipeline support for the “content” property (without having to specify the parameter name), we have to know which properties should be the default. PowerBoots has special handling for ‘Content’, ‘Child’, ‘Children’, ‘Items’, ‘Blocks’ and ‘Inlines’ which are the properties used by the various content models of the built-in WPF controls. I also added ‘GradientStops’ to make it easier to work with the GradientBrushes. But Visifire (and other third-party controls) may not follow this very well, so in order to get the simple syntax for Visifire that you see here, I modified PowerBoots so that ‘DataPoints’ and ‘Series’ would be added as Content properties.

Now, I’ve added functions to allow you to Get, Add, or Remove from the collection of these default property names … so you can call Add-BootsContentProperty to add to the list of property names which should be treated as default properties. You can also list them, and remove from the list. You should use these features with care, because if you have two default properties on the same object, you’ll probably have problems. ;) There’s no need to worry about adding duplicate names though, so if you want to write a PowerBoots-based module to, say, make charting easier … you could just require PowerBoots, and then call Add-BootsContentProperty and add the types you wanted functions for … and then add your customized functions.

Visifire … Slick Charting!

I’ve been meaning to write a whole post dedicated to Visifire and what it can do — even the screenshots you’re seeing here don’t really do it justice. It has very nice configureable mouse-over tooltips, all the chart elements are interactive, you can add hyperlinks to data points, or manually handle events like MouseEnter, and MouseDown/Up/Click with a simple scriptblock, and you can even add context menus, text input, etc. There’s a really good silverlight graph gallery on their site — pretty much anything you see there, you can do in Visifire — And now, you can do it all from PowerShell, easily. This is actually why I’ve been holding off writing about it, because their default syntax is a bit of a pain to work with, especially from PowerShell, with all the New-Object calls, over and over.

PowerBoots makes working with Visifire charts from PowerShell much easier, but you still have to specifically create the DataSeries and DataPoints, even if you don’t want to specify any parameters for them. If I keep using the Visifire stuff, I’ll eventually have to create a New-DataSeries function along the lines of Select-Object: taking a hashtable to define the values for the DataPoint, like ls | New-DataSeries @{YValue={$_.Length}; AxisXLabel={$_.Name}} or something. In the meantime, to create the graph on the right, you need to do something like this:


[int]$tk    = Invoke-Http get http://google.com/search -with @{q="TCL Tk"} |
              Receive-Http Text "//div[@id='ssb']//b[3]"
[int]$shoes = Invoke-Http get http://google.com/search -with @{q="Ruby Shoes"} |
              Receive-Http Text "//div[@id='ssb']//b[3]"
[int]$boots = Invoke-Http get http://google.com/search -with @{q="PowerShell PowerBoots"} |
              Receive-Http Text "//div[@id='ssb']//b[3]"

Chart -MinHeight 100 -MinWidth 100 (
   DataSeries -RenderAs Bar $(
      DataPoint -YValue $tk    -AxisXLabel Tk    -Href http://google.com/search?q=TCL+Tk
      DataPoint -YValue $shoes -AxisXLabel Shoes -Href http://google.com/search?q=Ruby+Shoes
      DataPoint -YValue $boots -AxisXLabel Boots -Href http://google.com/search?q=PowerSHell+PowerBoots
   )
) | Boots
 

The Invoke-Http and Receive-Http commands here are courtesy of HttpRest, and in this case, the XPath I’m using returns the total number of hits on Google for each search term…

What sorts of graphs and charts do you need?

Obviously Visifire lets you create all sorts of charts. Everything from stock tracking, to computer uptimes, to file sizes … I’ve got a few examples here, but really I need a few more real-world ones.

How about a graph of your active directory structure?

We can show the number of users at each location (or in each group, or whatever) fairly easily from ActiveDirectory. The interesting thing about this task, however, is that it lends itself to piping the data, instead of getting the data inside. If you don’t like this syntax (it is a little harder to read, I think) you can always write the whole AD stuff inside a $(evaluation) block for the content (the -DataPoints parameter) of the DataSeries function.


$ad=New-Object DirectoryServices.DirectorySearcher [ADSI]''
# Set a limit or TimeOut, PageSize lets us get more later
$ad.PageSize = 200

# ADSI field names are awful.
# l = location, l=* returns only users with locations set
$ad.Filter = "(&(objectClass=Person)(l=*))"  
$results = $ad.FindAll().GetEnumerator() | ForEach { $_ }
$users   = $results | ForEach { $_.GetDirectoryEntry() }

# "l" is a PropertyValueCollection, use the first value
$users | Group-Object {$_.l[0]}  | ForEach {
   DataPoint -YValue ([int]$_.Count) -AxisXLabel $_.Name
}| DataSeries -RenderAs Doughnut |
   Chart -Height 300 -Width 300  |
   Boots -Title "AD Users by Location"
 

Or perhaps a graph showing the relative age and size of the files in a directory …

Bubble charts are a bit more complicated, just because there’s more stuff to set. You should really have a look at the Visifire Documentation, because there are a metric boatload of options and parameters, and a lot of the parameters that are present on the DataPoint element are only used for certain charts (like ZValue, which is only for bubble charts).

In this example I also set the ToolTip text so you can see how to do it, and I store the DataSeries so that I can remove things from it later on… thought that would be a useful example :)

We handle clicks by writing the selected objects out (in this case, to the Remove-Item cmdlet), and then removing them from the graph so they can’t be selected again. If you run this script an play with it, you’ll notice that VisiFire charts automatically redraw when you remove an item, so if you click on the largest file(s), the rest of the files bubbles will grow…


ls | ForEach {
   DataPoint -YValue ([DateTime]::Now - $_.LastWriteTime).TotalDays `
             -ZValue ($_.Length/1KB) `
             -AxisXLabel $_.Name -Tag $_ `
             -On_MouseLeftButtonUp {
               if($this.Tag) {
                  Write-Output $this.Tag;
                  $series[0].DataPoints.Remove($this)
               }
            }
} | DataSeries -RenderAs Bubble -OutVariable series `
               -ToolTipText "#AxisXLabel`nAge: #YValue days, Size: #ZValue Kb" |
   Chart -MinHeight 250 -MinWidth 500 -Theme Theme3 | Boots | Remove-Item -Confirm
 

One Response to “PowerBoots 0.0.4 – Tab Completion – Visifire”