Handling Button Clicks in WPF with PowerShell

In my last post, I discussed how to fire up a WPF window in PowerShell. You can make XAML look pretty amazing, but if buttons and controls never get wired up to any functionality, it gets boring pretty quick. Here is the code I had last time with a small addition.Blend allows you to name your controls. This turns into what you see in line 018 in the XAML. x:Name=”Close.”

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
[xml]$xaml = @"
    <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="Blend and PowerShell"
Width="640" Height="480" AllowsTransparency="False">
<Grid x:Name="LayoutRoot">
  <Rectangle Margin="22,8,22,0" VerticalAlignment="Top" Height="178" Stroke="#FF000000">
   <Rectangle.Fill>
    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
     <GradientStop Color="#FF000000" Offset="0"/>
     <GradientStop Color="#FF861A1A" Offset="1"/>
    </LinearGradientBrush>
   </Rectangle.Fill>
  </Rectangle>
  <Button Margin="121,0,129,96" VerticalAlignment="Bottom" Height="100" Content="Button"
    x:Name="Close"/>
</Grid>
</Window>
"@

$reader = New-Object System.Xml.XmlNodeReader $xaml
$d = [Windows.Markup.XamlReader]::Load($reader)
$d.FindName("Close").add_click({
    $d.Close()
})
$d.ShowDialog() | out-null

Once you have the name, you can call the FindName method (see line 024) on your WPF Window. This allows you to find the named control that you want to add event handling for. Using FindName, we can add the add_Click method and pass in the ScriptBlock that we want to execute on the click event.

When you run this, clicking the button will close the dialog box.

No Seattle Script Club this Month

I had previously mentioned that there would be another Script Club this month, but schedule wise, things did not quite work out.

My wife and I are in “getting ready for new baby” mode. The C-section is scheduled for the 1st week of June.

I am guessing the next one will be in July sometime.

Blend -> XAML -> PowerShell

The PowerShell team just posted a blog entry with slides and demo scripts for one of their presentations from TechEd 2009. There is a lot of cool stuff in their but one thing that piqued my interested was the GUI Demo using WPF. I noticed in the demo scripts that they had a PS1 script and a XAML file. I could try to describe XAML but I will leave that to the guys that literally wrote the book.

“XAML is an XML-based language for creating and initializing .NET objects. It’s used in WPF as a human-authorable way of describing the UI.” - “Programming WPF, by Chris Sells and Ian Griffiths, page 8

Expression Blend is a Microsoft product that allows designers to create UI’s in a sort of similar way that you could create WinForms using the WinForms designer in Visual Studio. It’s all drag ‘n drop. Blend is essentially a Winforms designer for WPF. It definitely takes some getting used to but the bottom line is that you don’t need to know how to code to get some basic windows, shapes, and controls up and running.

Here is a (not so) beautiful dialog box with a rectangle and a big button

image

Well, in Blend, if you click on XAML tab you can see the angle brackets that make up this UI.

What’s cool is that you can use this XAML that was created with Blend in PowerShell.

We can accomplish this using a [Windows.Markup.XamlReader] and call that Load static Method. Assuming we store the XAML in a here string, you just need to cast it to XML and pass it in like so

$reader = New-Object System.Xml.XmlNodeReader $xaml
$d = [Windows.Markup.XamlReader]::Load($reader)

Once you have $d you can do a bunch of things with it.

$d.ShowDialog() will display the dialog box as the script below demonstrates.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
[xml]$xaml = @"
    <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="Blend and PowerShell"
Width="640" Height="480" AllowsTransparency="False">

<Grid x:Name="LayoutRoot">
  <Button HorizontalAlignment="Right"
    Margin="0,0,153,194"
    VerticalAlignment="Bottom"
    Width="5" Height="2"
    Content="Button"/>
  <Rectangle Margin="22,8,22,0"
       VerticalAlignment="Top"
       Height="178"
       Stroke="#FF000000">
   <Rectangle.Fill>
    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
     <GradientStop Color="#FF000000" Offset="0"/>
     <GradientStop Color="#FF861A1A" Offset="1"/>
    </LinearGradientBrush>
   </Rectangle.Fill>
  </Rectangle>
  <Button Margin="121,0,129,96"
          VerticalAlignment="Bottom"
    Height="100"
    Content="Button"
    x:Name="Close"/>
</Grid>
</Window>
"@

$reader = New-Object System.Xml.XmlNodeReader $xaml
$d = [Windows.Markup.XamlReader]::Load($reader)
$d.ShowDialog() | out-null

 

 

 

When I run this, I get the following

image

You may notice I tweaked the XAML a little bit. I changed the Window title on line 6. Blend also adds a class to its <Window> tag that is associated with the project in Blend. I had to remove that to get it to work in PowerShell. Other than that, we basically have a WPF editor that can create XAML that can be used in PowerShell.

My next post will deal with events. I do suppose it would be nice if clicking the button actually did something, huh.

Until next time..

Andy

Scott Hanselman&#8217;s Tools List

I just started rebuilding a laptop with Windows 7 RC and thought I would do a bit of a refresh on my typical tool list that gets added to my machine immediately after a clean install. Notepad2 with PowerShell support is the latest must add for me.

Scott Hanselman has a phenomenal list of tools that he posted back in 2007 but they are still worth a look through. Most are free and the ones that are not are worth paying for.

Scott is mainly a developer so a lot of the tools are dev focused, but a the top 10 would apply to anyone that wants to interact with their computer in a more effective way.

I have heard him mention on his podcast that people are starting to complain that he hasn’t refreshed the list in a while. Maybe we will have the pleasure of the 2009 list soon. We’ll see.

PowerShell and ActiveWords

I have been trying out some new software called ActiveWords. I really think this is one of those pieces of software that could potentially change the way you interact with a computer. It basically provides you with the ability to launch any app or replace any text anywhere in any context on your computer. I would recommend checking out their online video demos to see what you could do with this.

Heres a quick little video showing me using a keyword I created to add parameters to advanced functions in the ISE. I am sure you will be able to tell what is me typing versus the text replacement that ActiveWords does.

 

And here’s another video of me using the same keyword in Notepad2, which I use quite often for quick text editing.

The actions that can be taken in ActiveWords are activated using a hotkey. You can also enable the use a double space-bar which I prefer.

They have a free 60 day trial.  Just enough time to get you hooked. But there are tons of possibilities.

Script Club Update

On Tuesday we had our first Script Club in Seattle. There was even mention of the Script Club on This Week on Channel 9. The night went really well. There were lots of good discussions between PowerShell experts and beginners alike.

Some people are already asking when the next meeting will be. Mark Tuesday, May 26th on your calendars. It will be at 818 Stewart in downtown Seattle at 7:00 just like last time.

Hope to see you there!

Andy

Inconsistency about parsing Mandatory Parameters

Yesterday, I came across what I thought was a bug in PowerShell. Consider the following function:

Function Demo {            
param([parameter(mandatory=$true)][int64]$size)            
$size            
}            
This seems quite basic. It simply returns the argument that you enter for size.

75 >  demo 5
5

Now if you don’t provide a value you will get a prompt,and in ISE it will prompt like this

image

and you get the following output

77 >  demo
20

Simple stuff so far. But now, check this out. PS has some built in shortcuts for KB,MB,GB, and TB

79 >  1mb
1048576

So now we can do Demo 20MB and get the following

82 >  Demo 20MB
20971520

However, if we just run Demo with no parameters and get prompted as before, we get an error if we enter 20MB

83 >  demo
Demo : Cannot process argument transformation on parameter 'size'. Cannot convert value "20MB" to type "System.Int64".

The PowerShell Team closed my bug on this and marked it “By Design.” The reasoning is around security with the following explanation:

“The prompting mechanism is inherently insecure and we do not want to introduce a code execution bug similar to the mailer bug in the old ATT UnixPC. If the user is running an application elevated which uses script and an error results in a prompt, the user's ability to exploit that prompt should be as restricted as possible.”

That is fine, but my issue is that this provides an inconsistent user experience. I would think that typing a parameter argument or entering one from an input box should provide the same result. Maybe both should error out.

PowerShell Script Club in Seattle

The first Seattle PowerShell Script Club will be held on Tuesday, April 21st from 7:00 to 9:00 PM. You can Sign up now!

What is a PowerShell Script Club?

Script Clubs are like a hands on lab with no set topic or teacher. You bring an idea for a script, and ask your fellow PowerShell users for help getting the script written.

James Brundage, from the PowerShell at team at Microsoft, will be joining us for the evening.

About Seattle PowerShell Script Club

1. You Always Talk About Script club
2. You Always Talk About Script Club
3. If Someone asks for Help, And You Can Help, You Help
4. Two People Help One Person at One Time
5. One Module Per Person Per Night
6. All Scripts, All PowerShell
7. Scripts will be as short as they can be
8. If This is your First time at Script Club, You Have to Script

Remoting Out of Memory Exception

A colleague of mine was using some remoting features in a dev lab and came across an interesting issue. He was using some native commands to build out a SharePoint environment. One of the commands he was using kept throwing a OutOfMemory Exception error. The system had plenty of memory available, so it was definitely an issue with the remoting client. Poking around in the WSMAN: Provider we found a config option called MaxMemoryPerShellMB in WSMAN:\localhost\Shell\

The default value is 150, which seems well and good for most commands. However, this is the second time I have run into it with different commands so I think it is worth noting. After upping the config to 512, all was right with the world again.

set-item wsman:localhost\Shell\MaxMemoryPerShellMB 512

I have to say i love the new WSMAN: provider. It makes something that was incredibly difficult to manipulate a snap.

Hope that helps,

Andy

A Method to the Add-Member Madness

I have been writing a script where, for all intents and purposes, I basically needed to be able to create a class. I wanted to be able to repeatedly create a new-object of a certain type that had a certain set of properties and methods. Even in PowerShell V2, there is still no "New-Class" cmdlet, but we can fake it well enough for practical purposes. That is at least for my practical purposes. There is a lot of information on the web about using Add-Member and essentially creating property bags as shown in the code below.

001
002
003
004
005
006
007
008
009
$obj = new-object psobject 
                                                                     
$obj | Add-Member -MemberType noteproperty `
                  -Name FirstColor `
                  -Value orange                            

$obj | Add-Member -MemberType noteproperty `
                  -Name SecondColor `
                  -Value Yellow

From here we get a PSObject with two properties, FirstColor and SecondColor. So what can we do for methods? The Add-Member cmdlet has a parameter called MemberType. In the example above, I used two NoteProperties. There are several other possibilities that can be used here as well. I would argue, the second most useful MemberType would be ScriptMethod. ScriptMethods have a name, just like a NoteProperty, and a value, but the value is of type [ScriptBlock].

One thing to be aware of when using ScriptMethods is the automatic variable $this. The $this variable refers to the current object. It is similar to the "this" keyword in C#. Probably the best way to explain is with an example. First I will build a ScriptBlock using a here string and call the [ScriptBlock]::Create() Method. Finally, the ScriptBlock can be added as a value for the new member that is called "ChangeFirstColor"

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
$code = @'
param($color)
$this.FirstColor = $color
'@


$sblock = [scriptblock]::create($code)

$obj = new-object psobject 
                                                                     
$obj | Add-Member -MemberType noteproperty `
                  -Name FirstColor `
                  -Value orange                            

$obj | Add-Member -MemberType noteproperty `
                  -Name SecondColor `
                  -Value Yellow
                 
$obj | Add-Member -MemberType scriptmethod `
                  -Name ChangeFirstColor `
                  -Value $sblock

So now we have a method called ChangeFirstColor. Yes, we could just set the property to a new color, but suppose you wanted to put some logic and verification steps into it first. This is merely an example of what you could do. The other thing that is pretty cool is that ScriptMethods can take parameters. Notice I use the param statement in the ScriptBlock. You could also use $args but that drives me nuts and leads to confusion for the person that has to read code 6 months from now.

image