WPK – 2: Some thoughts, and my first really small databound app
My second article on using WPF from PowerShell. You can download WPK as part of the Windows 7 Resource Kit PowerShell Pack. When you can navigate to the folder <My Documents>\WindowsPowerShell\Modules you see the modules that are installed. The folder WPK contains the WPK module.
On the Modules level a few documents are installed:
- Readme1st.txt – information on installing, using, uninstalling the PowerShellPack
- About the Windows 7 Resource Kit PowerShell Pack.docx – an overview of the available modules
- Writing User Interfaces with WPK.docx - the first place to get started when working with WPK
Especially the Readme1st.txt contains two interesting pieces of information:
- The file starts with the following text:
Readme for the Windows 7 Resource Kit PowerShell Pack
by James Brundage
Copyright (c) 2009 by Microsoft Corporation
Portions copyright (c) 2009 James Brundage
All Rights Reserved
I thought James Brundage is an employee of Microsoft, why does he own portions of the copyright? - The disclaimer contains the following text:
The Windows 7 Resource Kit PowerShell Pack included on the companion CD is
unsupported by Microsoft and is provided to you as-is, with no warranty or
guarantee concerning its functionality. For the latest news and usage tips
concerning this PowerShell Pack, see the Windows PowerShell Team Blog at
http://blogs.msdn.com/powershell/.
So no support from Microsoft’s side. I wonder how issues will be resolved and new releases will be published.
Ok, and now on to some programming. In the document Writing User Interfaces with WPK.docx we find a nice example of a process viewer that updates the list of processes every 15 seconds.
- New-ListView -Width 350 -Height 350 -DataBinding @{
- ItemsSource = New-Binding -IsAsync -UpdateSourceTrigger PropertyChanged -Path Output
- } -View {
- New-GridView -AllowsColumnReorder -Columns {
- New-GridViewColumn "Name"
- New-GridViewColumn "Id"
- }
- } -DataContext {
- Get-PowerShellDataSource -Script {
- Get-Process | ForEach-Object { $_ ; Start-Sleep -Milliseconds 25 }
- }
- } -On_Loaded {
- Register-PowerShellCommand -Run -In "0:0:15" -ScriptBlock {
- $window.Content.DataContext.Script = $window.Content.DataContext.Script
- }
- } -asjob
See the document for a great explanation on how it works.
When looking at this example I had a few questions:
- What if I don’t want to do a timed update, but just bind to some existing data?
- All examples do the data collection in the Get-PowerShellDataSource script block, is it possible to have the data already somewhere in a variable?
- Can I skip the binding stuff, I know its really powerful, but I want to start simple?
- Retrieve data in a background job is really cool, but what if we just want to load data and go?
My first simple try after a lot of testing and tweaking is the following:
- New-ListView -Show -DataBinding @{
- ItemsSource = New-Binding -Path Output
- } -View {
- New-GridView -Columns {
- New-GridViewColumn "Name"
- New-GridViewColumn "Age"
- }
- } -DataContext {
- Get-PowerShellDataSource -Script {
- $list = @()
- $list += New-Object Object |
- Add-Member NoteProperty Name "Serge" -passthru |
- Add-member NoteProperty Age "43" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Dinah" -passthru |
- Add-member NoteProperty Age "42" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Scott" -passthru |
- Add-member NoteProperty Age "8" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Dean" -passthru |
- Add-member NoteProperty Age "4" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Tahne" -passthru |
- Add-member NoteProperty Age "1" -passthru
- $list
- }
- }
I still use binding to the output, and in the datacontext script block I write the elements to bind to to the output. I still don’t bind to pre-calculated data in a variable.
After a lot more testing I came to the following code:
- $list = @()
- $list += New-Object Object |
- Add-Member NoteProperty Name "Serge" -passthru |
- Add-member NoteProperty Age "43" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Dinah" -passthru |
- Add-member NoteProperty Age "42" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Scott" -passthru |
- Add-member NoteProperty Age "8" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Dean" -passthru |
- Add-member NoteProperty Age "4" -passthru
- $list += New-Object Object |
- Add-Member NoteProperty Name "Tahne" -passthru |
- Add-member NoteProperty Age "1" -passthru
- New-ListView -Name mylistview -Show -View {
- New-GridView -Columns {
- New-GridViewColumn "Name"
- New-GridViewColumn "Age"
- }
- } -On_Loaded {
- $mylistview = $window | Get-ChildControl mylistview
- $mylistview.ItemsSource = $list
- }
In the above code I create a variable with a list of objects with two properties, Name and Age, and bind the ItemsSource property of the ListView to this variable.
I bind the data in the Loaded event, the complete control tree is in place when this event fires.
I have named the ListView control ‘mylistview’, and with the code in line 24 I can find the control by name. The $window variable points to the implicitly created Window control surrounding the ListView, and is always available.
Note that if we add the –AsJob parameter to the New-ListView command, the creation and binding is done in a background job on another thread, and the $list variable is not visible, not even if it is defined as a global variable (as $global:list)
Diving into this simplification kept me busy for a while and gave me as a WPF nono some insights in how things are working in WPF when doing this from PowerShell.
One question I still have in this simple example: is there an easier way to fill the $list variable so its still useful for databinding.I tried $list = @{ Name="Serge"; Age="43" }, @{ Name="Dinah"; Age="42" } but that does not work:-(
Let me know if this is of any help to you.
Happy coding!