Huddled Masses

You can do more than breathe for free...

Rich formatting for PowerShell help

By Joel 'Jaykul' Bennett on 27-Jan-2012

[updated] Ok, I just updated this with a new post on PoshCode. I posted the HtmlHelp module to PoshCode for generating HTML web pages based on the help for functions or cmdlets. It basically has one command: Get-HtmlHelp, which takes a couple of parameters now. The only mandatory parameter is the name of the command you want help for, in this case the html is output on the pipeline and can be redirected into a file.

Get-HtmlHelp Get-HtmlHelp | Set-Content Get-HtmlHelp.html
 

The markup generated is (I hope) reasonable and lightweight, with some simple css rules pre-applied. Feel free to customize the script to generate help however you like.

[new] Generating many at once

I forgot to mention the other parameters on Get-HtmlHelp. They’re pretty cool, because if you want to upload your help you can do so with this. Say you created a module, and you wanted to generate all the help into files for uploading. You want to set the -BaseUrl to the location you will upload them to, and then use the -OutputFolder parameter to generate an html file for each command into the specified folder:

Get-Command -Module ModuleName |
Get-HtmlHelp -BaseUrl http://HuddledMasses.org/HtmlHelp/ -OutputFolder ~\sites\HuddledMasses\HtmlHelp\
 

Now you can just take those files and upload them to the right spot on your website. I actually have some scripts which I can wrap around this to post the help to a wiki, but you’re just going to have to wait for that until the next time I get inspired to work on help …

Show-Help

I did include a little function in the comments and in the help for Get-HtmlHelp which uses ShowUI to display the rich formatted help in a popup window:

function Show-Help {
[CmdletBinding()]
param([String]$Name)  
   Window { WebBrowser -Name wb } -On_Loaded {
      $wb.NavigateToString((Get-HtmlHelp $Name))
      $this.Title = "Get-Help $Name"
   } -Show
}
 

So anyway, enough of that. When you run it, it looks like this (click for the full screenshot):

Click to see full screen-shot

Posted in Huddled | Tagged Help, Html, PowerShell, ShowUI | 4 Responses

Did you know PowerShell can use Selenium?

By Joel 'Jaykul' Bennett on 05-Jan-2012

This is sort-of a place-holder for a full-length post that I really ought to write about driving web testing from PowerShell using Selenium. I actually have a little module around for doing that with WaTiN, but honestly the Selenium project seems to be a lot more active, and has quite a bit of muscle behind it since they’ve merged with WebDriver…


Add-Type -path ~\Downloads\selenium-dotnet-2.16.0\net40\WebDriver.dll

# Navigate to google in IE (or Firefox, Chrome, Opera, etc)
$driver = New-Object OpenQA.Selenium.IE.InternetExplorerDriver
$driver.Url = "http://google.com"

# Type PowerShell into the query box, the page will update via AJAX
# Note we won't hit enter or anything
$q = $driver.FindElementByname("q")
$q.SendKeys("PowerShell")

# Use a CSS selector to find the first result link and click it
$driver.FindElementByCssSelector("li.g h3.r a").Click()
 

One Catch

The Security tab of the Internet Options dialogIf you try this with IE and you get the error Unexpected error launching Internet Explorer. Protected Mode must be set to the same value (enabled or disabled) for all zones ... it means exactly what it says. You need to open “Internet Options” from your start menu (or from IE), and go through each “zone” and set the “Enabled Protected Mode” check box to the same value for each zone (either all checked, obviously the most secure, or all unchecked). I’m not going to debate whether setting them all unprotected is a good idea … I set mine to all protected, but I don’t generally use IE anyway.

If you want more help, Selenium’s documentation is great, and there’s a section on Getting Started with Selenium WebDriver which I found quite helpful (make sure your examples are in “csharp” and you can almost just copy and paste — someone should offer to do them in PowerShell).

If you want more information about the Internet Explorer driver and this problem in particular, the short answer is that “Protected Mode” is a security boundry, so if you cross over it the COM automation object doesn’t work — thus, you need to make sure you always stay on the same side. There’s a good discussion on the mailing list archive about how it works and why, as well a weird alternative documented on the Selenium JavaDocs

Posted in Huddled | Tagged Automation, Browser, GUI, PowerShell, Selenium, Testing, WatiN | 6 Responses

PowerShell 3 – Finally on the DLR!

By Joel 'Jaykul' Bennett on 05-Jan-2012

For those of you living in a cave: PowerShell 3 will be released in Windows 8, and we got a CTP at roughly the same time as the Windows 8 Developer Preview was released (at Microsoft’s new //Build/ conference in September 2011). A second CTP was released just in time for Christmas.

I’ve been playing with PowerShell 3 for a few months now, and I guess it’s long past time I started blogging about it.

There are a lot of new things coming in this release, but for me, the biggest change is the fact that PowerShell is now based on the Dynamic Language Runtime, a runtime environment that adds a set of services for dynamic languages to the Common Language Runtime (CLR), which is the core of the .NET Framework. The DLR makes it easier to develop dynamic languages to run on the .NET Framework. Of course, PowerShell is a dynamic language that runs on the .NET framework, but it was originally begun before the DLR had been released, so it’s only now that it’s finally been adapted to the DLR.

However, although PowerShell 3 is implemented using the DLR, it’s not a DLR language in every way that IronPython or IronRuby are. Let me borrow a couple of graphics from the DLR overview documentation.

The DLR Overview

DLR Overview

You can see there’s three major sections to the DLR as it’s available on CodePlex: hosting, runtime, and language. However, not all of the DLR actually shipped in the .NET Framework 4.0 CLR.

The DLR shipped in CLR 4

DLR Shipped in CLR4

PowerShell 3 takes advantage of all (or most) of what shipped in the CLR, but since the PowerShell team wasn’t willing to be responsible for shipping the rest of the DLR in the operating system, they didn’t implement the rest of it. Which is to say, PowerShell 3 is using the DLR Language Implementation code, with Shared AST and Expression trees, as well as the DynamicObject and Call Site Caching portions of the runtime, but none of the Common Hosting pieces like ScriptRuntime, ScriptScope, ScriptSource, or CompiledCode …

This means that you cannot use the same hosting APIs for PowerShell that you use for IronPython or IronRuby. However, even though you’re stuck using the same hosting APIs that you used with PowerShell 2 … you do get to use dynamic instead of PSObject when you’re working with the output in C#.

This really is a big deal

I wouldn’t care to speculate how many of the changes you’ll see in PowerShell 3 are directly due to the conversion to the DLR, but there are a few changes that you ought to be aware of. The first thing that you’ll probably notice is the difference in execution and performance. Anything you’ve learned about the relative performance of Scripts vs. Functions vs. Cmdlets and the load time of binary vs. script modules is going to go right out the window with PowerShell 3, as scripts and functions are now no longer (re)interpreted each time they’re run, but are compiled, executed, and (sometimes) cached. The result is that initial runs of scripts and imports of script modules are sometimes slower than they used to be, but subsequent runs of the same script or execution of functions from script modules run much faster, and this applies in particular to actual scripts in files and pre-defined functions in modules. Running a function repeatedly is now much faster than pasting the same code repeatedly into the console.

A more subtle, but significant difference is the change to PSObject.

In PowerShell 3, PSObject is a true dynamic object, and thus the output of cmdlets or scripts called in C# can be used with the dynamic keyword in C# instead of with the pseduo-reflection methods which are required for working with PSObject. However, this is just the tip of the iceberg, so to speak.

In PowerShell 2, all of PowerShell’s Extended Type System (ETS) was based on PSObject. New members were always added to a PSObject which is wrapped around the actual “BaseObject” — regardless of whether they came from a types.ps1xml file, or from calling Add-Member on an object. If you use Add-Member on strongly objects that are not already wrapped in a PSObject, you have to specify the -Passthru parameter and capture the output in order to have your object wrapped into a PSObject that the new member can be added to. In addition, when you cast an object to a specific type, those ETS members are mostly lost. Take this script for example:


$psObject = Get-ChildItem
$psObject.Count
$Count1 = ($psObject | where { $_.PSIsContainer }).Count

[IO.FileSystemInfo[]]$ioObject = Get-ChildItem
$ioObject.Count
$Count2 = ($ioObject | where { $_.PSIsContainer }).Count

$Count3 = ($ioObject | where { $_ -is [IO.DirectoryInfo] }).Count
 

In PowerShell 2, $Count1 and $Count3 will be the number of folders in the current directory, but $Count2 will always be ZERO, because the PSIsContainer property is actually an ETS property that’s lost when you cast the object to FileSystemInfo (and therefore it always evaluates as null and

However, in PowerShell 3 that’s no longer true. PowerShell now works with everything as dynamic objects, and Add-Member no longer needs the PSObject to keep track of these ETS members. This script will now get $Count1, $Count2, and $Count3 equal, as expected. Obviously the -Passthru switch on Add-Member is only needed when you’re trying to pipeline things, and not for simple assignments. However, there may also be other implications on when things get wrapped into a PSObject, and when it matters.

I think you’ll agree that having PowerShell on the DLR is awesome! But be aware that there are a few inconsequential breaking changes hiding in this kind of stuff. For example, after running that script above, try these three lines on PowerShell 2 and PowerShell 3 CTP2:


$Count1 -eq $Count2
$e = $ioObject[0] | Add-Member NoteProperty Note "This is a note" -Passthru
$f = $ioObject[0] | Add-Member NoteProperty Note "This is a note" -Passthru
 

In PowerShell 2, you’ll get False, and then the next two lines will work fine. In PowerShell 3 the first line will return True, and since Add-Member actually affects the underlying object even when it’s not wrapped in a PSObject, the third line will actually cause an error, because “Add-Member : Cannot add a member with the name “Note” because a member with that name already exists.”

Anyway, I’m sure I’ll have more to write about the DLR and the changes it’s bringing to PowerShell, but for now, I hope that’s enough to get you thinking ;-)

Posted in Huddled | Tagged CTP, DLR, PowerShell, PS3, Windows 8 | 15 Responses

Arrange – Act – Assert: Intuitive Testing

By Joel 'Jaykul' Bennett on 04-Oct-2011

Today I have a new module to introduce you to. It’s a relatively simple module for testing, and you can pick it up in short order and start testing your scripts, modules, and even compiled .Net code. If you put it together with WASP you can pretty much test anything ;-)

The basis for the module is the arrange-act-assert model of testing. First we arrange the things we’re going to test: set up data structures or whatever you need for testing. Then we act on them: we perform the actual test steps. Finally, we assert the expected output of the test. Normally, the expectation is that during the assert step we’ll return $false if the test failed, and that’s all there is to it. Of course, there’s plenty more to testing, but lets move on to my new module.

The module is called PSaint (pronounced “saint”), and it stands, loosely, for PowerShell Arrange-Act-Assert in testing. Of course, what it stands for isn’t important, just remember the name is PSaint :)

PSaint is really a very simple module, with only a few functions. There are two major functions which we’ll discuss in detail: Test-Code and New-RhinoMock, and then a few helpers which you may or may not even use:

Set-TestFilter

Sets filters (include and/or exclude) for the tests by name or category.

Set-TestSetup (alias “Setup”)

Sets the test setup ScriptBlock which will be run before each test.

Set-TestTeardown (alias “Teardown”)

Sets the test teardown ScriptBlock which will be run after each test.

Assert-That

Assserts something about an object (or the output of a scriptblock) and throws if that assertion is false. This function supports asserting that an exception should be thrown, or that a test is false … and supports customizing the error message as well.

Assert-PropertyEqual

This is a wrapper around Compare-Object to compare the properties of two objects.

How to test with PSaint: Test-Code

Test-Code (alias “Test”) is the main driver of functionality in PSaint, and you use it to define the tests that you want to run. Let’s jump to an example or two so you can see the usefulness of this module.

Let’s start with an extremely simple function that we want to write: New-Guid. We want a function that generates a valid random GUID as a string. We’ll start by writing a couple of tests. First we’ll test that the output of the function is a valid GUID.

test "New-Guid outputs a Guid" {
   act {
      $guid = New-Guid
   }
   assert {
      $guid -is [string]
      New-Object Guid $guid
   }
}
 

Now, to verify that the test works, you should define this function (the GUID-looking thing is one letter short) and then run that test:

function New-Guid { "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa" }
 

Another proof that it works would be that it should fail on this function too, because “x” is not a valid character in a Guid:

function New-Guid { "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }
 

So, let’s write a minimal New-Guid that actually generates a valid Guid:

function New-Guid { "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" }
 

If you run our test on that, you will see:

   Result: Pass

Result Name                          Category
------ ----                          --------
Pass   New-Guid outputs a Guid
 

If you don’t like the fact that the Category is empty, you could add a category or two to the end of our test. We should also switch to using Assert-That if we want to know which test in the assert failed. Finally, we want to write another test which would test that New-Guid doesn’t just return the same Guid every time, the way ours does right now:

test "New-Guid outputs a Guid" {
   act {
      $guid = New-Guid
   }
   assert {
      Assert-That { $guid -is [string] } -FailMessage "New-Guid returned a $($guid.GetType().FullName)"
      New-Object Guid $guid  # Throws relevant errors already
   }
} -Category Output, ValidGuid

test "New-Guid outputs different Guids" {
   arrange {
      $guids = @()
      $count = 100
   }
   act {
      # generate a bunch of Guids
      for($i=0; $i -lt $count; $i++) {
         $guids += New-Guid
      }
   }
   assert {
      # compare each guid to all the ones after it
      for($i=0; $i -lt $count; $i++) {
         for($j=$i+1; $j -lt $count; $j++) {
            Assert-That ($guids[$i] -ne $guids[$j]) -FailMessage "There were equal Guids: $($guids[$i])"
         }
      }
   }
} -Category Output, RandomGuids
 

Now, we have to actually fix our New-Guid function to generate real random Guids:

function New-Guid { [System.Guid]::NewGuid().ToString() }
 

And at that point, we should have a function, and a couple of tests that verify it’s functionality…

The finer points of assertions

One thing you’ll notice the first time you use Get-Member after loading the PSaint module is a few script properties have been added to everything. I did this because I found myself writing the same Assert-That calls over and over and decided that it would be slicker to make these extension methods than to write new functions for each one:

MustBeA([Type]$Expected,[string]$Message)
MustBeFalse([string]$Message)
MustBeTrue([string]$Message)
MustEqual([Object]$Expected,[string]$Message)
MustNotEqual([Object]$Expected,[string]$Message)
 

There’s also a MustThrow([Type]$Expected, [string]$Message) which can be used on script blocks (note that this function executes the ScriptBlock immediately, so be careful how you use it).

We can use these to tidy up our tests quite a bit, while still getting good error messages when tests fail:

test "New-Guid outputs a Guid String" {
   act {
      $guid = New-Guid
   }
   assert {
      $guid.MustBeA( [string] )
      New-Object Guid $guid # Throws relevant errors already
   }
} -Category Output, ValidGuid

test "New-Guid outputs different Guids" {
   arrange {
      $guids = @()
      $count = 100
   }
   act {
      # generate a bunch of Guids
      for($i=0; $i -lt $count; $i++) {
         $guids += New-Guid
      }
   }
   assert {
      # compare each guid to all the ones after it
      for($i=0; $i -lt $count; $i++) {
         for($j=$i+1; $j -lt $count; $j++) {
            $guids[$i].MustNotEqual($guids[$j])
         }
      }
   }
} -Category Output, RandomGuids
 

COM Objects

PSaint also has a wrapper for COM objects to help with testing them. It adds GetProperty and SetProperty methods to allow you to access COM object properties which don’t show up on boxed COM objects (a common problem when working with MSOffice, for instance). It also adds InvokeMethod for COM objects to invoke methods that don’t show up for similar reasons. These, of course, only help you if you’re already fairly literate with the COM object in question.

Mock Objects

PSaint includes New-RhinoMock, a function for generating a new mock object using RhinoMocks (which is included). Rhino Mocks is a BSD-licensed dynamic mock object framework for the .Net platform. Its purpose is to ease testing by allowing the developer to create mock implementations of custom objects and verify the interactions using unit testing.

I have to admit that this New-RhinoMock function is incomplete, and exposes only a fraction of the options and power in RhinoMocks, but it’s been sufficient for the few times when I’ve wanted to actually mock objects from PowerShell, so I’m including it here.

For those of you (developers) who want to know why RhinoMocks instead of your favorite mocking framework, the answer is astonishingly simple: it had the least number of necessary generic methods (which are impossible to call in PowerShell 2).

Posted in Huddled | Tagged 3As, PowerShell, Scripting, Testing | 1 Response

Ramblings about computer markets …

By Joel 'Jaykul' Bennett on 03-Oct-2011

This is what I call a stream of consciousness, edited. Please don’t rip my head off, call me names, etc.

Microsoft is a product of the commodity hardware era.

That is, Microsoft is a company that couldn’t have existed previously, in an era where computers cost tens of thousands of dollars (or hundreds of thousands of dollars). When the computers themselves were expensive, the hardware was the item of value.

There was only one business model.

Companies producing them were not thinking about the software running on them as valuable (and even missed patenting things like windowed interfaces, pointing devices, and networking protocols), because they through the valuable thing would be the hardware forever.

Modern business models for personal computing.

In the modern era, we have three major business models: hardware, software, service.

Apple represents the hardware model: they’re making billions selling top-end hardware (phones, laptops, computers, tablets). They’re basically giving away their software. They’re trying to commodify the software — making it trivial and making it easier for thousands of companies and even individuals to produce competing versions of every possible application on their hardware.

Microsoft represents the software model: they’re making billions selling software licenses for software that runs on any kind of hardware. They’re actively trying to commodify hardware, making their software run on any kind of hardware from hundreds of manufacturers, and driving prices of home computers, netbooks, and tablets down to a couple of hundred dollars.

RedHat (and Linux vendors in general) represents the service model: they’re making millions selling service contracts for free software that runs on that same really cheap hardware that Microsoft’s model has been driving.

How might that affect service, software, and hardware?

It wouldn’t be any surprise if service companies didn’t care much about how user friendly the software is: after all, they only make money if you need support to use it.

It would also not be surprising if software companies didn’t care how confusing the hardware choices are — they want to make sure that there are lots of hardware companies, because that’s how you maintain the commodity status of the hardware.

And of course, it wouldn’t be a surprise if hardware companies made high end hardware, released their software updates freely or cheaply, treating the software like firmware designed to run only on their specific hardware models. These companies might have the lowest barriers to switching hardware architectures and creating new hardware form factors, since they’re providing a stand-alone platform (the software is like firmware) without much regard for disruption of software platforms.

Which market is easier to enter?

It seems to me like the most sustainable of these models, and more importantly, the most favorable to the consumer … is the software model. I prefer business models which encourage competition, and I think that hardware and service models do not.

On the one hand it’s easier to grow a software company than it is to grow a new service company. It’s very hard to compete with the big guys on service because of reach (you can’t provide world-wide service), and as a result, very few service companies make it past the mom-and-pop size to medium-sized businesses employing more than 10 people.

On the other hand, it’s easier to create a new software company than it is to create a new hardware company. It’s very hard to compete with the big guys on hardware because of costs (you can’t get hardware costs down without substantial volume).

You only have to look at the past few years to see that it takes a long time for a new hardware company to grow to a useful size, and that service companies tend to consolidate long before they become competitive.

But you’re not taking X into account

Of course, this is a fairly simple interpretation of the personal computer marketplace. I know that. Tell me something I don’t know below.

Posted in Huddled | Tagged BusinessModel, Capitalism

Working with multiple versions of PowerShell Modules

By Joel 'Jaykul' Bennett on 30-Aug-2011

What follows is a brief explanation of modules, followed by an explanation of how I handle multiple versions of a module. My way isn’t the only way — and I’m rather annoyed that I have to do it — but it works, and might help someone, so here it is.

Some back-story

During development of a PowerShell module like ShowUI, I am usually not using a released version of the module, since I’m obviously working on the next version. In addition, sometimes I need to have “beta” and “stable” releases … so I might have two or three versions of the module that I need to be able to load in order to support users and actually do development.

In case you don’t know how modules work, here’s the deal: There’s an environment variable PSModulePath. It’s just like the Path environment variable, but for modules. The variable contains a semi-colon delimited list of folders paths, which PowerShell searches for modules. Some people think of a PowerShell module as basically a .dll (binary module), a .psm1 (script module), or a .psd1 (manifest module)... but it’s never just a file, it’s a *Folder* and a file. In order for PowerShell to find ShowUI when you write Import-Module ShowUI, you have to have a Folder in the PSModulePath named “ShowUI” and in it, a file (either .dll, .psm1, or .psd1) also named ShowUI.

What I would like

The Import-Module command has a -Version parameter. What I expected What I would like to be able to do is create a folder “ShowUI” and in there, create folders “1.0” and “1.1” and “1.2” and then be able to use Import-Module ShowUI -Version 1.1 to load a specific version.

Sadly, this doesn’t work. First of all, the -Version parameter on Import-Module, is a minimum only. Additionally, Import-Module requires the file and folder to match names exactly, so putting the module inside a subfolder doesn’t work even if there’s only one.

However, there are two things we can use to fix our problem:

1. Just like the PATH variable, PSModulePath works in order. PowerShell searches the listed paths one at a time and uses the first Module that meets the specified minimimum version.
2. Additionally, PowerShell allows you to specify a relative Path to a module that starts in a PSModulePath … I’ll explain more in a moment.

My actual working environment

All of my development projects are in $Home\Projects, so I put a “Modules” folder in there which is where I put modules that I’m actively working on. Then I add that folder to my PSModulePath environment variable: @$Env:PSModulePath += “;$Home\Projects” ...

Since I put it on the end, modules in there won’t be imported if there’s a module with the same name in my regular Module folder ( $Home\Documents\WindowsPowerShell\Modules ), unless I increment the version number in the psd1 metadata file and specify -Version when importing.

For example, I have two copies of WASP:

$Home\Documents\WindowsPowerShell\Modules\WASP\Wasp.psd1 (version 1.0)
$Home\Projects\Modules\WASP\Wasp.psd1 (version 2.0.0.2)

If I want to load the released version, I can just Import-Module WASP — this ensures that scripts that I have in my scripts folder generally load the released version of WASP. When I want to load the development version, I can Import-Module WASP -Version 2.0, and get the version from my Projects folder, because the released version’s version number is lower than 2.0.

More layers onion boy?

When I need to have more than one older version, this presents a problem, because I would need another folder in my path, and for the numbering scheme to work despite the fact that -Version is only a minimum value, I would need the released version (say 1.0) to come first in the path, then the beta version (say 2.0.1), then the development version (say 2.1) ... which means I’d need to add a $Home\Projects\BetaReleaseModules or something like that, and put it in my PSModulePath before the $Home\Projects\Modules folder. That would work, but you can imagine that it wouldn’t scale very well.

Here’s what I do: past the release and development versions, I start creating folders like this:

$Home\Documents\WindowsPowerShell\Modules\ShowUI1.0\ShowUI.psd1 (version 1.0)
$Home\Documents\WindowsPowerShell\Modules\ShowUI\ShowUI.psd1 (Release version: 1.1)
$Home\Documents\WindowsPowerShell\Modules\ShowUI1.1\ShowUI.psd1 (Release version: 1.1)
$Home\Documents\WindowsPowerShell\Modules\ShowUI1.2\ShowUI.psd1 (Beta version 1.2)
$Home\Projects\Modules\ShowUI\ShowUI.psd1 (Development version 1.3)

Now, if I Import-Module ShowUI, I’ll get 1.1 and if I specify Import-Module ShowUI -Version 1.2 I’ll get … version 1.3 (whoops).

To load ShowUI 1.2 with that structure I have to do this: Import-Module ShowUI1.2\ShowUI — specifying the folder name and the module name. Of course, I could use “ShowUIBeta” as the folder name instead, but usually when I get a request for help writing scripts, I can get people to give me the specific version that they’re working with, so I like the numbered folders instead.

A last important note is that I do not rename the psd1 or anything else.

I could rename the metadata file to $Home\Documents\WindowsPowerShell\Modules\ShowUI1.2\ShowUI1.2.psd1 and then I’d be able to Import-Module ShowUI1.2, but there’s a catch. If I rename it, then I’ve renamed the module. If I leave the metadata file as “ShowUI.psd1” then once I’ve imported the module I can call Get-Module ShowUI or Get-Command -Module ShowUI and they still work as they should.

In any case, this folder-naming stuff is less than ideal and will hopefully be fixed in a future release of PowerShell, but with a “Projects\Modules” folder I can have two versions, and usually a development and release version is enough. Personally, I keep old releases of major modules around in numbered folders just in case, but I rarely actually use them.

Posted in Huddled | Tagged Modules, PowerShell, Versioning | 1 Response

More ShowUI Widgets

By Joel 'Jaykul' Bennett on 20-Jul-2011

Well, I got so many tweets and comments on IRC about my ShowUI clock that I figured I’d share a few other widgets I had lying around … but first, an update to New-UIWidget:


function New-UIWidget {
[CmdletBinding()]
param(
    [ScriptBlock]$Content,
    [Alias("Refresh")]
    [TimeSpan]$Interval = "0:0:2",
    [ScriptBlock]$UpdateBlock,
    [Switch]$Show, [Switch]$AsJob
)

$WidgetValues = @{
    AllowsTransparency = $true
    WindowStyle = "None"
    ShowInTaskbar = $true
    Background = "Transparent"
    On_MouseLeftButtonDown = { $Window.DragMove() }
    On_Closing = { $Window.Resources.Timers."Clock".Stop() }
    Tag = @{"UpdateBlock"=$UpdateBlock;"Interval"=$Interval}
    SizeToContent = "WidthAndHeight"
    ResizeMode = "NoResize"
    On_SourceInitialized = {
        $Window.Resources.Timers.Clock = (New-Object Windows.Threading.DispatcherTimer).PSObject.BaseObject
        $Window.Resources.Timers.Clock.Interval = $Window.Tag.Interval
        Add-EventHandler $Window.Resources.Timers.Clock Tick $Window.Tag.UpdateBlock
        $Window.Resources.Timers.Clock.Start()
        $Window.Tag = $Null
    }
    On_ContentRendered = $UpdateBlock  
}
New-Window @WidgetValues -Content $Content -Show:$Show -AsJob:$AsJob
}
 

Basically I just switched it to use Add-EventHandler, which in ShowUI does at lot of awesome magic to make sure all the variables you would want (i.e.: variables for named controls) are defined in your event handler. It makes writing these gadgets much easier. Here are a few:

Let’s start with the simplest Widget I can dream up:

New-UIWidget { Label -Name Time -FontSize 48 } "0:0:0.2" { $Time.Content = Get-Date -f "h:mm tt"}

Yeah, just a simple text clock. To be honest, that’s a bit plain for me … but I do like digital.

Another few Clocks

My preference is for something very large, with the date and stuff on it:

If you prefer analog clocks, this one has “hands” and old school stopped movement (no click sound, but maybe you could add that)...

And finally, while I’m on the subject, the clock from yesterday could be a lot simpler if it didn’t need animation, here’s the target part of it. Feel free to explore.

And now for something completely different

My other favorite widget to implement in new UI frameworks is a weather widget. Why? Well, mostly because it requires parsing an RSS feed and a few extra pieces of work that let you actually determine how hard it’s going to be to write more advanced UI. In any case, here’s my ShowUI weather widget (note that you have to change the $woEID to get your forecast):

OK, that’s enough for one night, but I’ll take requests for tomorrow! As a side note, you know the cool thing about these gadgets? When I took the screenshots, those were all running from the same PowerShell window -AsJobs, and using less than 1% CPU.

Posted in Huddled | Tagged Clock, Gadgets, ShowUI, Widgets

The Obligatory ShowUI Clock

By Joel 'Jaykul' Bennett on 19-Jul-2011

Well, it all started when Richard Siddaway posted his PowerShell Clock using raw XAML. Then Doug posted his ShowUI Clock, and after some back and forth on our ShowUI developer mailing list, James Brundage posted a video walkthrough of building his clock …

Now I’ve dug an old clock of mine out of mothballs and updated it to run on ShowUI — I believe that I’ve never posted this script before, so I don’t feel bad at all doing so now, even though I actually wrote it last December (hey, look, I have a screenshot against my bright red Christmas wallpaper to prove it — totally random, I know). Anyway, here’s the deal: this post was supposed to be written 6 months ago, about building fun UI Widgets in PowerShell, but I never got to it, because we started working on the PowerBoots + WPK merger we call ShowUI. So here’s a fun little function for you:


function New-UIWidget {
param(
    [ScriptBlock]$Content,
    [TimeSpan]$Interval = "0:0:2",
    [ScriptBlock]$UpdateBlock,
    [Switch]$Show, [Switch]$AsJob
)

$WidgetValues = @{
    AllowsTransparency = $true
    WindowStyle = "None"
    ShowInTaskbar = $true
    Background = "Transparent"
    On_MouseLeftButtonDown = { $Window.DragMove() }
    On_Closing = { $Window.Resources.Timers."Clock".Stop() }
    Tag = $UpdateBlock,$Interval
    SizeToContent = "WidthAndHeight"
    ResizeMode = "NoResize"
    On_SourceInitialized = {
        $Window.Resources.Timers."Clock" = (New-Object Windows.Threading.DispatcherTimer).PSObject.BaseObject
        $Window.Resources.Timers."Clock".Interval = $Window.Tag[1]
        $Window.Resources.Timers."Clock".Add_Tick( $Window.Tag[0] )
        $Window.Resources.Timers."Clock".Tag = $Window
        $Window.Resources.Timers."Clock".Start()
        $Window.Tag = $Null
    }
}

New-Window @WidgetValues -Content $Content -Show:$Show -AsJob:$AsJob
}
 

That script is what I use to create little “gadget” style windows that are transparent, don’t show up on the taskbar, but are draggable, and have an automatic update based on a timer. Now, this is a very simple version of the gadgets, without using our PowerShell “DataSource” or any data binding at all. I intend to update this with more functionality one of these days, but this was enough to make my clock work. You can see that basically all it does is sets a bunch of parameters for the Window, including a SourceInitialized event handler, and all the stuff necessary to make it transparent and draggable.

Now let me post the New-TargetClock funtion, which depends on that function, and we’ll get to explaining it a bit down below. First, the screenshot, this is what it looks like (with a PowerShell window behind it, instead of that bright red wallpaper):

And then the actual function:


function New-TargetClock {
param([Switch]$ShowMarks)

New-UIWidget -AsJob -Content {
    Grid {
        $now = Get-Date;
        $Label = @{HorizontalAlignment="Center"; VerticalAlignment="Top";FontSize=25;ZIndex=1}
        # Hours
        TextBlock -Text H -Margin '0,-5,0,0' -FontWeight ExtraBold -Foreground White @Label
        Ellipse -Name Hours -Ov el -Fill Transparent -Stroke Black `
                -StrokeThickness 100 -Width 350 -Height 350 `
                -StrokeDashArray  7.85, 7.85  -Opacity 0.5 `
                -StrokeDashOffset $(7.85 - (7.85 * ((($now.Hour % 12) + ($now.Minute/60))/12))) `
                -RenderTransformOrigin "0.5,0.5" -RenderTransform { RotateTransform -Angle -90 }
            $animate = DoubleAnimation -To 0 -Duration "$(11 - ($now.Hour % 12)):$(59 - $now.Minute):$(60 - $now.Second)"  
            $el[0].BeginAnimation( [System.Windows.Shapes.Shape]::StrokeDashOffsetProperty, $animate )
       
        # Minute
        TextBlock -Text M  -Margin '0,20,0,0' -FontWeight ExtraBold -Foreground White @Label
        Ellipse -Name Minutes -Ov el -Fill Transparent -Stroke Gray `
                -StrokeThickness 75  -Width 300 -Height 300 `
                -StrokeDashArray  9.43, 9.43 `
                -StrokeDashOffset $(9.43 - (9.43 * (($now.Minute) + ($now.Second/60))/60)) `
                -RenderTransformOrigin "0.5,0.5" -RenderTransform { RotateTransform -Angle -90 }
            $animate = DoubleAnimation -To 0 -Duration "00:$(59 - $now.Minute):$(60 - $now.Second)"
            $el[0].BeginAnimation( [System.Windows.Shapes.Shape]::StrokeDashOffsetProperty, $animate )
         
        # Seconds
        TextBlock -Text S -Margin '0,45,0,0' -FontWeight ExtraBold -Foreground Gray @Label
        Ellipse -Name Seconds -Ov el -Fill Transparent -Stroke White `
                -StrokeThickness 50  -Width 250 -Height 250 `
                -StrokeDashArray 12.57,12.57 `
                -StrokeDashOffset $(12.57 - (12.57 * ($now.Second/60))) `
                -RenderTransformOrigin "0.5,0.5" -RenderTransform { RotateTransform -Angle -90 }
            $animate = DoubleAnimation -To 0 -Duration "00:00:$(60 - $now.Second)"
            $el[0].BeginAnimation( [System.Windows.Shapes.Shape]::StrokeDashOffsetProperty, $animate )

        ## These go in the center of the circles (notice the alignment)
        $Centered = @{HorizontalAlignment="Center";VerticalAlignment="Center"}
        StackPanel @Centered {
            TextBlock -Name Time -FontWeight ExtraBold -Foreground Black -FontSize 30 `
                      -Text $Now.ToString("h:mm") @Centered
            TextBlock -Name Day -FontWeight Normal -Foreground Black -FontSize 23 `
                      -Text $Now.DayOfWeek.ToString().ToUpper() @Centered
            StackPanel -Orientation Horizontal @Centered {
                TextBlock -Name Date -FontWeight ExtraBold -Foreground Black -FontSize 18 `
                          -Text $Now.Day -Margin '0,0,5,0'
                TextBlock -Name Month -FontWeight ExtraBold -Foreground White -FontSize 18 `
                          -Text $Now.ToString("MMMM").ToUpper()
            }
        }

        ## Hour marks
        if($ShowMarks) {
            Ellipse -Width 350 -Height 350 -StrokeThickness 5 -Fill Transparent -Stroke White -StrokeDashArray 0.5,17.6 -RenderTransformOrigin "0.5,0.5" -RenderTransform {RotateTransform -Angle -90.5} -Opacity 0.8
            Ellipse -Width 160 -Height 160 -StrokeThickness 5 -Fill Transparent -Stroke Black -StrokeDashArray 0.5,7.62 -RenderTransformOrigin "0.5,0.5" -RenderTransform {RotateTransform -Angle -90.5} -Opacity 0.8
        }
    }
} -UpdateBlock {
    $children = @{}
    foreach($child in Get-ChildControl -Control $this.Tag){
        $children.($child.Name) = $child
    }

   $now = Get-Date

    if($now.Second -eq 0) {
        if($children.Seconds.Tag -ne $true) {
            $children.Seconds.Tag  = $true
            $animate = DoubleAnimation -From $children.Seconds.StrokeDashArray[0] -To 0 -Duration '00:01' -RepeatBehavior ([system.windows.media.animation.RepeatBehavior]::Forever)
            $children.Seconds.BeginAnimation( [System.Windows.Shapes.Shape]::StrokeDashOffsetProperty, $animate )
        }
    } else {
        $children.Seconds.Tag  = $false
    }
   
    if($now.Minute -eq 0) {
        if($children.Minutes.Tag -ne $true) {
            $children.Minutes.Tag = $true
            $animate = DoubleAnimation -From $children.Minutes.StrokeDashArray[0] -To 0 -Duration '01:00' -RepeatBehavior ([system.windows.media.animation.RepeatBehavior]::Forever)
            $children.Minutes.BeginAnimation( [System.Windows.Shapes.Shape]::StrokeDashOffsetProperty, $animate )
        }
    } else {
        $children.Minutes.Tag  = $false
    }
   
    if($now.Hour -eq 12) {
        if($children.Hours.Tag -ne $true) {
            $children.Hours.Tag  = $true
            $animate = DoubleAnimation -From $children.Hours.StrokeDashArray[0] -To 0 -Duration '12:00' -RepeatBehavior ([system.windows.media.animation.RepeatBehavior]::Forever)
            $children.Hours.BeginAnimation( [System.Windows.Shapes.Shape]::StrokeDashOffsetProperty, $animate )
        }
    } else {
        $children.Hours.Tag  = $false
    }

    $children.Time.Text = $Now.ToString("h:mm")
    $children.Day.Text = $Now.DayOfWeek.ToString().ToUpper()
    $children.Date.Text = $Now.Day
    $children.Month.Text = $Now.ToString("MMMM").ToUpper()
}
}
 

What do you think of that?

The clever part (which, believe me, took a LOT of experimenting), is that I’m creating a StrokeDashArray and animating the StrokeDashOffsetProperty in such a way that the “stroke” of the Ellipse (the circle of the clock) gets drawn slowly over time (very slowly, in the case of the hour “hand”). Now, as you can see, I’m still setting up the animations by hand (I just didn’t convert the code to use Start-Animation), and in the UpdateBlock, I actually reset the animations if they’re at zero. I can write more about this if people have questions, but it’s nearly 2:30am, so I’ll take questions tomorrow :)

Posted in Huddled | Tagged Animation, ShowUI, WPF

Disabling Events in ShowUI

By Joel 'Jaykul' Bennett on 27-Jun-2011

Because some things just work better in WPF.

Let’s say you had a form that collected a bunch of user input, and then had a button that would fire off some work. We’ll assume that you wanted to prevent people from firing off the work again before they know the results of the first time, so you’ll disable the button while you’re working. Something like this (leaving out the boring stuff about collecting the user’s input and displaying the results):


Import-Module ShowUI

StackPanel {
   TextBlock -Name Output
   Button "Click Me" -Margin 3 -On_Click {
      $this.IsEnabled = $False
      # Update the user about what we're doing:
      $Output.Text += "We are doing some hard work...`n"
     
      # Simulate doing some hard work ...
      $times = $times + 1
      Start-Sleep -Seconds 3
     
      # Let them know about all our hard work
      $Output.Text += "Work completed $times time(s).`n"
      $this.IsEnabled = $True
   }
} -Show
 

It’s pretty simple. In fact, I think there’s only three things worth pointing out:

  1. There are variables for named controls.
  2. If you want to make sure a control has time to update it’s display while you’re executing your event handler, call UpdateLayout().
  1. You can disable and enable buttons.

Now, try doing the same thing with Windows Forms.

This was my first attempt:


[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")

# Create the form
$form = new-object System.Windows.Forms.Form
$form.Size = "250,250"

## Create text
$output = new-object system.windows.forms.label
$output.Anchor = "Top,Left,Right"
$output.AutoSize = $True

## Create button
$button = new-object system.windows.forms.button
$button.Anchor = "Bottom,Right"
$button.Text = "Click Me"
$button.Location = "150,180"

$button.Add_Click({
    $button.Enabled = $False
    $Output.Text += "We are doing some hard work...`n"
   
    ## Simulate doing work
    $times = $times + 1
    Start-Sleep -Seconds 5
     
    # Let them know about all our hard work
    $Output.Text += "Work completed $times time(s).`n"
    $button.Enabled = $True
})

$form.Controls.AddRange(@($output, $button))

## show the form
[void]$form.showdialog()
 

Now, I bet you noticed that took a bit more code than the WPF version in ShowUI, but the thing about it is that I also had to manually specify the positions of the controls (and I had to set the size of the form to make that work). And after all that, it still doesn’t actually work, and it has a few ugly behaviors:

First of all, disabling the control doesn’t work, because Windows posts all click events as messages to the button’s message queue, and it doesn’t know that it should ignore them, because it only processes one at a time — when it’s done running the Click handler the first time, it notices there’s another event … and since it’s enabled, it processes it. If you click it five times while it’s disabled, you’re going to get the work done five more times… eventually.

On top of that, although the label updates without being told to, it didn’t push the button down the screen or resize the form, so eventually it overlaps the button, passes through the bottom of the form and disappears.

The next thing I tried was to actually unhook the event handler before I disable the button … but that had no effect either, because (as I wrote earlier) the button doesn’t actually ignore the clicks while it’s doing the work — it just queues them up for processing later.

Of course, I could do the work in a background job so that the button would return immediately, but that doesn’t meet the requirements I have for not queuing up extra work before the first work is done, and in fact, then I’d have extra work to do to create a time to check and see if the remote job was finished or not.

[New] Thanks to SAPIENDavid, I’ve realized the simple fix for the disabling problem, we just have to call DoEvents to empty the event queue before we re-enable the button.

Here’s what did work:


Add-Type -Assembly System.Windows.Forms

# Create the form
$form = New-Object System.Windows.Forms.Form -Property @{
    Size = "200,70"
    AutoSize = $true
}

# Create a FlowLayout
$panel = New-Object System.Windows.Forms.FlowLayoutPanel -Property @{
    Dock = "Fill"
    FlowDirection = "TopDown"
    AutoSize = $true
}

## Create text
$output = New-Object system.windows.forms.label -Property @{
    Dock = "Fill"
    Text = "Click the button when you're ready to work.`n"
    AutoSize = $true
}

## Create button
$button = New-Object system.windows.forms.button -Property @{
    Anchor = "Bottom,Right"
    Text = "Click Me"
}

$lastButtonClick = get-date
$button.Add_Click({
    $button.Enabled = $False
    $Output.Text += "We are doing some hard work...`n"
       
    ## Simulate doing work
    $times = $times + 1
    Start-Sleep -Seconds 5
         
    # Let them know about all our hard work
    $Output.Text += "Work completed $times time(s).`n"

    #Process the pending messages before enabling the button
    [System.Windows.Forms.Application]::DoEvents()
    $button.Enabled = $True
})

$panel.Controls.AddRange(@($output, $button))
$form.Controls.AddRange(@($panel))

## show the form
[void]$form.showdialog()
 

To fix the other problems, I added a FlowLayoutPanel (which is very different from the StackPanel in WPF, but still serves the same purpose), and made all the relevant bits autosize (it’s always surprising to me when I need to drop back to WinForms, how everything has to be told to autosize and fill empty space).

That’s enough to take care of the output problem, and make the two solutions roughly equivalent (there’s still some differences, as you can see if you run them).

For what it’s worth, this was a real world question from a user at our Virtual User Group this morning, and I just couldn’t help sharing how much easier user interfaces are to write in ShowUI. Clearly a designer like PrimalForms makes laying out the controls easier — but when it comes to the little things, you still have to figure out to make them work, and actually implement your event handlers correctly.

Just for the record, here’s what it would take to implement the WPF solution without ShowUI:


Add-Type -Assembly PresentationFramework
$window = New-Object System.Windows.Window -Property @{
    SizeToContent = 'WidthAndHeight'
    Content = New-Object System.Windows.Controls.StackPanel
}
$output = New-Object System.Windows.Controls.TextBlock
$button = New-Object System.Windows.Controls.Button -Property @{
    Content = "Click Me"
    Margin = 3
}

$button.Add_Click({
    $this.IsEnabled = $False
    # Update the user about what we're doing:
    $Output.Text += "We are doing some hard work...`n"
    $Window.UpdateLayout()
    # Simulate doing some hard work ...
    $times = $times + 1
    Start-Sleep -Seconds 3
    # Let them know about all our hard work
    $Output.Text += "Work completed $times time(s).`n"
    $this.IsEnabled = $True
})

$window.Content.Children.Add($output)
$window.Content.Children.Add($button)
$window.ShowDialog()
 

Posted in Huddled | Tagged HowTo, PowerBoots, PowerShell, ShowUI, UserInterface, WinForms | 7 Responses

ShowUI: Handling Events and Producing Output

By Joel 'Jaykul' Bennett on 20-Jun-2011

Once you get past the basics of WPF and ShowUI, learning to use nested panels or grids to achieve the layouts you want, and start getting a grip on what controls are available by default, the next step to building useful user interfaces is going to be handling user interactions.

In programming, we call those interactions “events.” An event in WPF covers all forms of user interactions like clicking a button, type in a textbox, or select items from a treeview or listbox, and even less obvious things like when the right or left mouse button get’s pressed down, or let up … or when the mouse moves, of when the control gets focus or looses it, or “touch” and “stylus” events, drag-and-drop events, etc. There are also events that aren’t caused by users, like events for when databinding is updated, when the control is initialized, hidden, made visible, etc.

In ShowUI, all events are handled by assigning scriptblocks to a parameter who’s name starts with “On_” like -On_Click or -On_GotFocus or -On_MouseLeftButtonDown or -On_TextInput … and so on.

Let’s say that you want a quick dialog like this:

You’re going to need to handle the OK button click, of course, but in that scriptblock, you’re going to want to get the text from the textbox, and make sure that it gets returned when the window is closed … and you’re going to want to close the Window!

We’re here to help! Within the event handler script blocks, ShowUI defines a bunch of variables for you to help you handle the event: $this is the source of the event, $_ is the event arguments, and $window is the top-level window that contains your UI. Any named controls in your script are also exposed as variables, so if you started with this script:


StackPanel -ControlName "Prompt" -Margin "8,0,8,8" {
    Label "Please Enter Your Full Name:"
    StackPanel -Orientation Horizontal {
        TextBox -Name FullName -Width 100
        Button "OK" -IsDefault -Width 50 -Margin "8,0,0,0" -On_Click {
            # Do something to output the name!
        }
    }
} -Show
 

You’re going to be able to get the text from the TextBox using $FullName.Text because you know the TextBox has a property named “Text” (since it’s exposed as a parameter for you in the TextBox command), and because now you know that ShowUI creates a variable for all the named controls.

In order to write output from a script like that one, you have to set the Tag property on the top level control (in this case, the StackPanel, which is obviously named “Prompt”). You can do that easily by hand, or you can use the Set-UIValue function.

In order to close the window, you’re going to have to do one of two things: first, you can use the handy Close-Control function, or you can call the Close method on the window. The Close-Control function will look at the “parent” (and it’s parent) to try and find the window that needs to be closed — but if it can’t find one, it will just hide the parent, so if your button were several layers deep (unlike ours), you’d have to specify the top level control as a parameter.

Here’s two versions of what it could have looked like when I was finished:


StackPanel -ControlName "Prompt" -Margin "8,0,8,8" {
    Label "Please Enter Your Full Name:"
    StackPanel -Orientation Horizontal {
        TextBox -Name FullName -Width 100
        Button "OK" -IsDefault -Width 50 -Margin "8,0,0,0" -On_Click {
            $Prompt.Tag = $FullName.Text
            $Window.Close()
        }
    }
} -On_Loaded { $FullName.Focus() } -Show
 

StackPanel -ControlName "Prompt" -Margin "8,0,8,8" {
    Label "Please Enter Your Full Name:"
    StackPanel -Orientation Horizontal {
        TextBox -Name FullName -Width 100
        Button "OK" -IsDefault -Width 50 -Margin "8,0,0,0" -On_Click {
            Set-UIValue $Prompt -Passthru | Close-Control
        }
    }
} -On_Loaded { $FullName.Focus() } -Show
 

One thing you’ll notice right away is that I cheated and actually added another event handler too: For the “Loaded” event on the StackPanel. This event handler is called during the initialization of the user interface, and gives you a chance to do things like what I did here: set the initial keyboard focus where you want it (so the user can start typing as soon as the window pops up).

However, if you run them both, you’ll notice another thing: the output is different. In the second example I took advantage of the fact that Set-UIValue will actually call Get-UIValue if you don’t pass it a parameter! The cool thing about Get-UIValue is that if it doesn’t find a “Tag” on the specified control, it will look through the children to find one, and create a hashtable out of the values it finds. So in this case, rather than write the code to get the value from the right textbox and set it myself, I just let the built-in features of ShowUI do their thing.

A bigger example

Of course, in neither example did I need to do anything with the button or with the actual parameters that are passed in to the button’s “Click” event … so perhaps one last (more complicated) example would be useful:


New-Grid -ControlName SelectUserGroups -Columns Auto,* -Rows 4 {
    $GetGroups = {
        $user = Get-QADUuser $this.Text -SizeLimit 1
        if($User.LogonName -eq $this.Text -or $User.Email -eq $this.Text) {
            $this.Foreground = "Black"
            $Group.ItemsSource = Get-QADGroup -ContainsMember $user
            $UserName.Text = $user.LogonName
            $EmailAddress.Text = $user.Email
        } else {
            $this.Foreground = "Red"
            $Group.ItemsSource = @()        
        }
    }
   
    New-Label "Name"
    New-Textbox -name UserName -minwidth 100 -Column 1 -On_LostFocus $GetGroups
   
    New-Label "Email" -Row 1
    New-Textbox -name EmailAddress -minwidth 100  -Column 1 -Row 1  -On_LostFocus $GetGroups
   
    New-Label "Group" -Row 2
    New-Listbox -name Group -Column 1 -Row 2
   
    New-Button "OK" -Row 3 -Column 1 -On_Click { Get-ParentControl | Set-UIValue -Passthru | Close-Control }
} -Show
 

Hopefully, by now this doesn’t need a whole lot of explanation, but let’s walk through it anyway. First of all, if you’re not familiar with them, this script uses the excellent PowerShell Commands for Active Directory from Quest software. This doesn’t represent a full, complete, useful user interface — it’s more of an example that you can hopefully work from (and please, contribute ammendments on PoshCode).

You can see that I used a Grid with four rows and two Columns: with one column “Auto“sized, and one using up all the rest of the UI (you can resize the window to make the fields bigger). The first thing in the grid is the definition of a scripblock which I assigned to a variable $GetGroups. The reason I did that is because I wanted to reuse the scriptblock as the event handler for both the UserName and EmailAddress fields.

Without dwelling a whole lot on it, you can see the last line is the “OK” button which get’s the parent grid and calls Set-UIValue to invoke, as before, the hashtable collection of all the textbox values.

The interesting stuff is in that GetGroup event handler:


    $user = Get-QADUuser $this.Text -SizeLimit 1
    if($User.LogonName -eq $this.Text -or $User.Email -eq $this.Text) {
        $this.Foreground = "Black"
        $Group.ItemsSource = Get-QADGroup -ContainsMember $user
        $UserName.Text = $user.LogonName
        $EmailAddress.Text = $user.Email
    } else {
        $this.Foreground = "Red"
        $Group.ItemsSource = @()        
    }
 

You can see that first we call Get-QADUser with the Text of $this field. By using $this, we make it so the event handler will work on both Textboxes, since it will get the text of whatever triggered the event handler. Get-QADUser doesn’t return anything unless it finds a user, and setting the SizeLimit ensures that we won’t end up waiting for it to retrieve “all” the users just because the Textbox was left empty. In fact, the point of that line is to make sure that we matched a user.

On the next line, I’m making sure that either the LogonName or Email of the user that we found matches fully the text that the user typed. This makes sure that the user that we matched is a full match, so we know we’ve gotten the person typing into the form to type a full, complete username or email address.

When it does match, we set the color to Black (in case it was an error and set to Red before), and we call Get-QADGroup to get all the groups that the user is a member of. We set those groups as the source for the $Group listbox, and they’ll immediately show up for the user. And finally, we update the few fields we’re showing from the user object we retrieved earlier.

Of course, when it doesn’t match, we set the text red to indicate an error, and then we zero out the data on the group listbox.

I hope this has helped some of you figure out event handlers in ShowUI — please feel free to ask questions in the comments below or on the ShowUI discussion boards.

Posted in Huddled | Tagged HowTo, PowerBoots, PowerShell, ShowUI, UserInterface, WinForms | 2 Responses

Next »

Lijit Search

I Diigo...

    Tags

    .Net .Net 2008 Scripting Games Automation Bugs Design Development Funny Gadgets GeoShell GUI Huddled Masses Internet licensing Microsoft Modules My Software News Personal PInvoke Pipeline Politics PoshCode PoshConsole PowerBoots PowerShell PowerShell Functions PowerTips Rants Recommender Repository Scripting ShowUI Software Solutions Textile Tips User Group UserInterface WalkThrough WebHosting Windows 7 WordPress WPF Xml

    About Huddled Masses

    This is web site is dedicated to the musings of Joel Bennett (aka Jaykul) about technology, software, software development, the web, and the world.

    Any resemblance of the views expressed and the views of my employer, my terminal, or the view out my window are purely coincidental. The resemblance between them and my own views is non-deterministic. The question of the existence of views in the absence of anyone to hold them is left as an exercise for the reader.

    P.S.: I occasionally link to things I think are great. When I do, I occasionally find a "referral code" so I can make a little cash. I promise that I don't link to anything just because of that cash (I wouldn't cross the street for the amount of cash those links bring in, never mind write a whole blog post) ... but I do not promise that things I link to will stay great as time passes, nor that you will agree with me about their greatness!

    Archives

    • January 2012
    • October 2011
    • August 2011
    • July 2011
    • June 2011
    • March 2011
    • February 2011
    • January 2011
    • November 2010
    • August 2010

    Copyright © 2012 Joel Bennett.

    Powered by WordPress and Hybrid.