how to bind a listbox item to the keys of a hash?

Jun 15, 2011 at 4:26 AM

I'd like to bind an item in a listbox template to the keys of a hashtable. Binding to a simple array works fine, but how to bind to the keys (and also values) of a hash?

ipmo showui

$commandList = @{
  CommandStrings = @{
    MinSessionTimestampInRange = "select 1"
    MaxSessionTimestampInRange = "Select 2"
    CountOfSessionsInRange = "select 3"
    AllSessionsInRange = "select * "
  }
  SimpleArray = 'abc', 'def'
}

$MainWindow=New-Window -Title T1  {
  #ListBox -ItemsSource { $commandList.CommandStrings } -ItemTemplate { # swap the comment symbol with the line below to see failure
  ListBox -ItemsSource { $commandList.simplearray } -ItemTemplate {
    StackPanel -HorizontalAlignment "Left" -VerticalAlignment "Top" -Orientation "Horizontal" -Name "c_sp_1" {
      Label -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_lbl_coName"
    } | ConvertTo-DataTemplate -binding @{
        # "c_lbl_coName.Content" = $_.Key # swap the comment symbol with the line below to see failure. This gives a list of dictionary entries, not the keys. can't figure out the syntax!
        "c_lbl_coName.Content" = $_
    }   
  }
} -show

Jun 15, 2011 at 4:17 PM

Try this:

$MainWindow=New-Window -Title T1  {
  ListBox -ItemsSource { $commandList.CommandStrings.Values } -ItemTemplate {
    StackPanel -HorizontalAlignment "Left" -VerticalAlignment "Top" -Orientation "Horizontal" -Name "c_sp_1" {
      Label -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_lbl_coName" 
    } | ConvertTo-DataTemplate -binding @{
        "c_lbl_coName.Content" = $_.Name
    }    
  }
} -show

Jun 16, 2011 at 4:48 AM

Thanks for your reply! It doesn't answer my need, but trying it taught me my approach was wrong.  I don't think your answer does what I need, for two reasons

1) If my understanding of the hashtable Keys and Values methods is correct, they each return an array consisting of the Keys (or values), so that -ItemsSource { $commandList.CommandStrings.Values } means the ListBox is binidng to an anonymous array returned by .Values, not to the actual $commandlist.CommandStrings variable

2) To extend the original example (I had pared it down to the bare minimum), I want the label of each item to show the Hash Key and the TextBox (see extended example below) to show the string associated with the key. Eventually, each value will be another hash (or perhaps an object), having "commandstring", and "status", and "Last execution duration", and other properties.

$MainWindow=New-Window -Title T1  {
  ListBox -ItemsSource { $commandList.CommandStrings } -ItemTemplate { # binding to the actual data structure (hashtable)
  #ListBox -ItemsSource { $commandList.simplearray } -ItemTemplate {
    StackPanel -HorizontalAlignment "Left" -VerticalAlignment "Top" -Orientation "Horizontal" -Name "c_sp" {
      Label -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_lbl_coName"
      TextBox -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_tb_coCmdStr"  ## Added a text box
    } | ConvertTo-DataTemplate -binding @{
         "c_lbl_coName.Content" = $_.Key  #  trying to bind the lable to the Key
        "c_tb_coCmdStr.Text" = $_.Value # trying to bind the textbox to the Value
    }   
  }
} -show

This, very interestingly, had a different error message:

New-Window : Exception calling "ShowDialog" with "0" argument(s): "Two-way binding requires Path or XPath."
At C:\Users\whertzing\Documents\WindowsPowerShell\Untitled5.ps1:13 char:23
+ $MainWindow=New-Window <<<<  -Title T1  {
    + CategoryInfo          : NotSpecified: (:) [New-Window], MethodInvocationException
    + FullyQualifiedErrorId : EmbeddedProcessRecordError,AutoGenerateCmdlets1791088748.NewWindowCommand

Which tells me that the example I studied and used for inspiration, Samples\Data-Template.ps1, is a one-way binding, and I'll have to go back to studying and learning to figure out how to create a listbox that contains an item template of many (nested) controls, with two-way binding between a data structure which is a hash of nested hashtables and the nested controls. 

Thanks for your response. I'll have to go hold my head some more, now... Can you suggest an example I should go focus on, for two-way binding using Path?

FYI - This is the ListBox itemtemplate of nested controls I've built so far:

ListBox -ItemsSource { $commandList.CommandStrings } -Row 1 -ItemTemplate { # this is embedded in a Grid
      StackPanel -HorizontalAlignment "Left" -VerticalAlignment "Top" -Orientation "Horizontal" -Name "c_sp" {
        Label -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_lb_coName" -Content "Connection-Name1"
        StackPanel -HorizontalAlignment "Left"   -VerticalAlignment "Center"  -Orientation "Horizontal" {
          Label -HorizontalAlignment "Left" -VerticalAlignment "Top" -Name "c_lb_runtime" -Content "LastRunTime"
          Image -HorizontalAlignment "Left" -VerticalAlignment "Top" -Name "c_img_status"
          Button -HorizontalAlignment "Left" -VerticalAlignment "Top" -Name "c_btn_update" -Content "Update"
        }
        StackPanel -HorizontalAlignment "Left"   -VerticalAlignment "Top" -Orientation "Vertical" {
          StackPanel -HorizontalAlignment "Left"   -VerticalAlignment "Top" -Orientation "Horizontal" {
            Label -HorizontalAlignment "Stretch" -VerticalAlignment "Top" -Name "c_lbl_cmdstr" -Content "CommandStringHere"
            Button -HorizontalAlignment "Right" -VerticalAlignment "Top" -Name "c_btn_editcmd" -Content "..."
          }
          StackPanel -HorizontalAlignment "Left"   -VerticalAlignment "Top" -Orientation "Horizontal" {
              TextBox  -HorizontalAlignment "Stretch" -Name "c_tb_cmdstr" -Text  " CommandString with Find/Replace replacement"
          }
        }
    } | ConvertTo-DataTemplate -binding @{
        "Dummy.Content" = $_.Key  ## I have to use dummy names here, or the window is blank
        "Id.Content" = $_.Value  ## using dummy names, at least the "static" parts of the item template are displayed, one item for each member of the $commandList.CommandStrings hashtable
    }   
}

Which produces the middle part of this screenshot (we can't attach images to discussions here,can we?):

http://dl.dropbox.com/u/1189766/Public%20Images/Screenshots/Update-Excel-Connections_2011-06-15_23-36-56.png

 

 

Coordinator
Jun 16, 2011 at 4:47 PM

Sorry I didn't reply before, I got lost in the the example, and you were so very close at the beginning, that when russellds spoke up I didn't even read his answer :)

Try this:

$commandList = @{
  CommandStrings = @{
    MinSessionTimestampInRange = "select 1"
    MaxSessionTimestampInRange = "Select 2"
    CountOfSessionsInRange = "select 3"
    AllSessionsInRange = "select * "
  }
}

ListBox -ControlName Selector -ItemsSource $commandList.CommandStrings -ItemTemplate {
   StackPanel -HorizontalAlignment "Left" -VerticalAlignment "Top" -Orientation "Horizontal" -Name "c_sp_1" {
      Label -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_lbl_coName"
   } | ConvertTo-DataTemplate -Binding @{
      "c_lbl_coName.Content" = "Key"
   }   
} -On_SelectionChanged { 
   Set-UIValue $this $this.SelectedItem.Value
} -show

I added a SelectionChanged handler just so we're all clear on how you would access the "select *" value of the selected item ... If you run that and select something and then close the window, the selected item's value will be output to the pipeline.

 

 

Jun 17, 2011 at 4:50 AM

Thank you very much - it works now! Based on your example, I've bound the Key, turned the original example's Values into Hashs, and bound more controls to the elements of the value hash. I'm getting closer to what I really want. I'm not trying to create a selectable list box - I want a "Repeater" control that outputs a set of child controls, and binds them to a data object. I found WPF documentaion on the ItemsControl class, but when I try to use New-ItemsControl in a New-Window - it doesn't work (says ItemControl is not a blah-blah-blah). We can't use a ItemsControl in ShowUI?

 

Here is a working example of the ListBox with nested controls, bound to a complex hash. 

ipmo showui

$commandList = @{
  CommandStrings = @{
    MinSessionTimestampInRange = @{visualorder=1;cmdstr="select 1";lastupdateduration="Not Yet Updated"} # not using visualorder yet - that's the next step, to figure out how to tell the ListBox to enumerate the CommandStrings in the proper visualorder
    MaxSessionTimestampInRange = @{visualorder=2;cmdstr="select 2";lastupdateduration="Not Yet Updated"}
    CountOfSessionsInRange = @{visualorder=3;cmdstr="select 3";lastupdateduration="Not Yet Updated"}
    AllSessionsInRange = @{visualorder=4;cmdstr="Change ME!";lastupdateduration="Not Yet Updated"}
  }
}

$MainWindow=New-Window -Title T1  {
  ListBox -ItemsSource { $commandList.CommandStrings } -ItemTemplate {
    StackPanel -HorizontalAlignment "Left" -VerticalAlignment "Top" -Orientation "Horizontal" -Name "c_sp" {
      Label -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_lbl_coName"
      Label -HorizontalAlignment "Left" -VerticalAlignment "Top" -Name "c_lbl_runtime"
      TextBox -HorizontalAlignment "Left" -VerticalAlignment "Center" -Name "c_tb_coCmdStr"
    } | ConvertTo-DataTemplate   -binding @{
        "c_lbl_coName.Content" = "Key"
        "c_lbl_runtime.Content" = "Path=Value[lastupdateduration]"
        "c_tb_coCmdStr.Text" = "Path=Value[cmdstr]"
    }  
  }
} -show

$commandList.CommandStrings.AllSessionsInRange

Coordinator
Jun 17, 2011 at 6:44 AM

I'll have to look into why that one got excluded ... but you can probably do what you want with ListView.

If you had to, you could new one up using New-Object, but I'm afraid that it's rather tricky to set the DataTemplates you would need (since ItemsControl is almost entirely lookless) without help from the functions.