3
votes

I have two separate binding problems with listboxes with an itemtemplate which contains a texbox.

1) One listbox binds to a list of strings. How can I display each string inside the textboxes created and allow two way binding at the same time? Two way binding isn't allowed without specifying a Path or XPath.

<ListBox Height="231" HorizontalAlignment="Left" Margin="0,167,0,0" Name="listBoxKeys" VerticalAlignment="Top" Width="219" ItemsSource="{Binding Path=SelectedPlatform.Keys}" SelectedItem="{Binding Path=SelectedKey,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
         <ListBox.ItemTemplate>
              <DataTemplate>
                  <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
                     <TextBox Text="{Binding Mode=OneWay}" Margin="0,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
                   </StackPanel>
               </DataTemplate>
          </ListBox.ItemTemplate>
 </ListBox>

And 2) I use another listbox which binds to some generic list of a custom KeyValuePair class. The itemtemplate contains a textbox and combobox. The textbox text is bound to the key property of each KeyValuePair object and the combobox selecteditem to the value property. My problem is that I want the combo to get filled by a list of strings declared in my viewmodel which will be changing on runtime. The window's datacontext is the viewmodel where the list is declared. I don't know the exact syntax I need to use to bind the combobox itemssource there. Here's my code :

<ListBox Height="393" HorizontalAlignment="Left" Margin="0,72,0,0" Name="listBoxActions" VerticalAlignment="Top" Width="254" ItemsSource="{Binding Path=SelectedPlayer.ControlProfile.MappedActions}">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
          <TextBox Text="{Binding Key, Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
          <ComboBox Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center" ItemsSource="{Binding ?}" SelectedItem="{Binding Value, Mode=TwoWay}"/>
         </StackPanel>
        </DataTemplate>
       </ListBox.ItemTemplate>
</ListBox>
2
This really should have been two separate questions as you're going to find some people can answer one and not the other so you might get two people correctly answering. This will make it difficult to mark the answer as correct.James Hay

2 Answers

4
votes

The problem is that the two-way binding on the source itself cannot work because it would mean that the whole object (string), for which the data template is created, must be replaced when user changes the text in the text box. Obviously, this will not work. Two-way binding will work only on a writable property of the bound object.

In your case I would suggest creating a view model for the items in the list box (basically a view model for your strings) and expose a Value property on it and bind to it in the data template:

<ListBox Height="231" HorizontalAlignment="Left" Margin="0,167,0,0" 
         Name="listBoxKeys" VerticalAlignment="Top" Width="219" 
         ItemsSource="{Binding Path=SelectedPlatform.KeyViewModels}" 
         SelectedItem="{Binding Path=SelectedKey,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
        <ListBox.ItemTemplate>
             <DataTemplate>
                 <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
                    <TextBox Text="{Binding Value, Mode=TwoWay}" Margin="0,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
                  </StackPanel>
              </DataTemplate>
         </ListBox.ItemTemplate>
</ListBox>
3
votes

1) Pavlo Glazkov seems to have a good answer to me

2) This is down to the DataContext for the ComboBox now being the key value pair rather than the ViewModel. There may be other ways to do this but the one that I've used before is to set the bindings RelativeSource source back to it's parent ItemsControl.

RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}

Something like:

<ListBox Height="393" HorizontalAlignment="Left" Margin="0,72,0,0" Name="listBoxActions" VerticalAlignment="Top" Width="254" ItemsSource="{Binding Path=SelectedPlayer.ControlProfile.MappedActions}">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
          <TextBox Text="{Binding Key, Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
          <ComboBox Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center" ItemsSource="{Binding DataContext.Keys,  RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" SelectedItem="{Binding Value, Mode=TwoWay}"/>
         </StackPanel>
        </DataTemplate>
       </ListBox.ItemTemplate>
</ListBox>