locked
ComboBox in ListView: Existing SelectedValue should drive SelectedItem? RRS feed

  • Question

  • Context:

    ListView with a GridView

    A GridViewColumn CellTemplate has a DataTemplate that switches between "display and "edit" modes.

    In "display" mode, the column is a TextBlock; in "edit" mode, a ComboBox. See Detail A below.

    Problem:

    When the ComboBox is displayed, I expect an existing value in the property bound to SelectedValue to drive the SelectedItem of the ComboBox. Instead, the ComboBox is displayed with no value selected (blank).

    Should the SelectedValue be able to drive the SelectedItem?


    Detail A: GridViewColumn

    The CellTemplate is a ContentControl, and I use the trick of switching out the ContentControl template based on a DataTrigger triggering on an "IsSelected" property in the bound viewmodel.

    The ContentControl template alternates between a TextBlock and a ComboBox based on this property.

    Detail B describes the ComboBox.


    <GridViewColumn Header="Batch"   
                    Width="100"   
                    > 
        <GridViewColumn.CellTemplate> 
            <DataTemplate> 
                <ContentControl x:Name="tbDisplay">  
                    <ContentControl.Template> 
                        <ControlTemplate> 
                            <TextBlock Style="{StaticResource TextBlockStatePointStyle}" 
                                       Text="{Binding Path=AssemblyDM.BatchID, Mode=OneWay}" 
                                       /> 
                        </ControlTemplate> 
                    </ContentControl.Template> 
                </ContentControl> 
                <DataTemplate.Triggers> 
                    <DataTrigger Binding="{Binding Path=IsSelected}" Value="True">  
                        <Setter TargetName="tbDisplay" Property="Template">  
                            <Setter.Value> 
                                <ControlTemplate> 
                                    <ComboBox Style="{StaticResource ComboBoxInListViewStatePointStyle}"   
                                              IsEditable="False" 
                                              HorizontalAlignment="Stretch" 
                                              ItemsSource="{Binding Path=CycleAssemblysVM.Batchs, Mode=OneWay}" 
                                              ItemContainerStyle="{StaticResource ComboBoxItemStatePointStyle}" 
                                              ItemTemplate="{StaticResource ComboBoxBatchItemTemplate}" 
                                              SelectedValuePath="BatchID" 
                                              SelectedValue="{Binding Path=AssemblyDM.BatchID, Mode=TwoWay}" 
                                              > 
                                    </ComboBox> 
                                </ControlTemplate> 
                            </Setter.Value> 
                        </Setter> 
                    </DataTrigger> 
                </DataTemplate.Triggers> 
            </DataTemplate> 
        </GridViewColumn.CellTemplate> 
    </GridViewColumn> 
     
     

    Detail B: ComboBox

    The ItemsSource is a list of Batch objects. All I want out of the ComboBox is one of the fields, the BatchID, so I bind the SelectedValue to where I want the BatchID to go and set SelectedValuePath to get to it.

    Except that if there is already a value in the bound property, I expect it to work the other way. The bound value will drive the SelectedItem of the ComboBox. This is not happening.

    The ItemTemplate is intended to provide the user with more information than just the BatchID. So it shows two properties from each Batch in a StackPanel.

    <DataTemplate x:Key="ComboBoxBatchItemTemplate" DataType="{x:Type nl:CycleBatch}">  
        <StackPanel Orientation="Horizontal">  
            <TextBlock Style="{StaticResource TextBlockStatePointStyle}"   
                       Text="{Binding Path=BatchID, Mode=OneWay}" 
                       /> 
            <TextBlock Style="{StaticResource TextBlockStatePointStyle}"   
                       > 
                <TextBlock.Text> 
                    <Binding Path="Enrichment" Mode="OneWay" 
                             Converter="{StaticResource DoubleToStringPercent}" 
                             ConverterParameter="p3" 
                             /> 
                </TextBlock.Text> 
            </TextBlock> 
        </StackPanel> 
    </DataTemplate> 
     

    However, when the ComboBox is initially displayed, if the property bound to SelectedValue already has a BatchID defined, I expect the SelectedValue to drive the ComboBox to select the corresponding item in the ItemsSource. That is, the Batch that has that BatchID. Instead, even though there is a valid SelectedValue (due to the binding), no item is selected in the ComboBox.

    This is very confusing to the user because a value that was sitting there in a TextBlock gets covered over by a blank ComboBox.

    Saturday, December 27, 2008 11:01 AM

Answers

  • OK, I got it to work with an ObjectDataProvider, but for one minor problem.

    First I defined an ODP in Resources

    <ObjectDataProvider x:Key="odpBatchs" MethodName="GetBatchs"/>  
     
     

    Then in code-behind I set the ObjectInstance

    Private Sub CycleAssembly_DataContextChanged(ByVal sender As ObjectByVal e As System.Windows.DependencyPropertyChangedEventArgs) Handles Me.DataContextChanged  
        Dim odp As ObjectDataProvider = CType(Me.FindResource("odpBatchs"), ObjectDataProvider)  
        odp.ObjectInstance = e.NewValue  
    End Sub 
     

    Then set the ItemsSource on the ComboBox as

    ItemsSource="{Binding Source={StaticResource odpBatchs}}" 
     
     

    And it works beautifully.

    Only problem is I get this minor error when the UserControl is instantiated

    System.Windows.Data Error: 32 : ObjectDataProvider needs either an ObjectType or ObjectInstance.

    But at instantiation there's nothing to set the ObjectInstance to, since it needs the UserControl DataContext which hasn't been set yet.

    • Marked as answer by RokShox Wednesday, December 31, 2008 2:23 AM
    Saturday, December 27, 2008 1:38 PM
  • I added the following Setter to the Style applied to the ComboBox

            <Setter Property="ItemsPanel">  
                <Setter.Value> 
                    <ItemsPanelTemplate> 
                        <StackPanel Orientation="Vertical" 
                                    VerticalAlignment="Center" 
                                    HorizontalAlignment="Stretch" 
                                    /> 
                    </ItemsPanelTemplate> 
                </Setter.Value> 
            </Setter> 

    And the problem appears to be resolved.

    • Marked as answer by RokShox Wednesday, December 31, 2008 2:23 AM
    Saturday, December 27, 2008 6:23 PM

All replies

  • I think the problem is related to the ItemsSource specification on the ComboBox

    ItemsSource="{Binding Path=CycleAssemblysVM.Batchs, Mode=OneWay}"  

    Somehow the ItemsSource is not ready at the time the ComboBox wants to look up the SelectedValue.

    Now, the CycleAssemblysVM.Batchs property is just a known, existing ObservableCollection. There should be some way to
    make it into a StaticResource using, say, an ObjectDataProvider. But I can't figure out how to specify the ObjectInstance to get at CycleAssemblysVM. Even though the object pointed to by CycleAssemblysVM is actually the DataContext of the ListView.


    Saturday, December 27, 2008 12:03 PM
  • OK, I got it to work with an ObjectDataProvider, but for one minor problem.

    First I defined an ODP in Resources

    <ObjectDataProvider x:Key="odpBatchs" MethodName="GetBatchs"/>  
     
     

    Then in code-behind I set the ObjectInstance

    Private Sub CycleAssembly_DataContextChanged(ByVal sender As ObjectByVal e As System.Windows.DependencyPropertyChangedEventArgs) Handles Me.DataContextChanged  
        Dim odp As ObjectDataProvider = CType(Me.FindResource("odpBatchs"), ObjectDataProvider)  
        odp.ObjectInstance = e.NewValue  
    End Sub 
     

    Then set the ItemsSource on the ComboBox as

    ItemsSource="{Binding Source={StaticResource odpBatchs}}" 
     
     

    And it works beautifully.

    Only problem is I get this minor error when the UserControl is instantiated

    System.Windows.Data Error: 32 : ObjectDataProvider needs either an ObjectType or ObjectInstance.

    But at instantiation there's nothing to set the ObjectInstance to, since it needs the UserControl DataContext which hasn't been set yet.

    • Marked as answer by RokShox Wednesday, December 31, 2008 2:23 AM
    Saturday, December 27, 2008 1:38 PM
  • Also, when I select a different row in the ListView I get dozens of these errors:

    System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ComboBoxItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')

    Nice. Great. If it's not one thing it's another. Well, let's spend another three days tracking down this problem.


    Saturday, December 27, 2008 2:24 PM
  • The fix is apparently to hide the Immediate Window when showing the application to your co-workers.

    Failing that, practice saying "Oh, ignore that. It's normal for WPF to shit all over itself".

    Saturday, December 27, 2008 3:20 PM
  • I added the following Setter to the Style applied to the ComboBox

            <Setter Property="ItemsPanel">  
                <Setter.Value> 
                    <ItemsPanelTemplate> 
                        <StackPanel Orientation="Vertical" 
                                    VerticalAlignment="Center" 
                                    HorizontalAlignment="Stretch" 
                                    /> 
                    </ItemsPanelTemplate> 
                </Setter.Value> 
            </Setter> 

    And the problem appears to be resolved.

    • Marked as answer by RokShox Wednesday, December 31, 2008 2:23 AM
    Saturday, December 27, 2008 6:23 PM
  • I'm trying to find solution for such elementary problem like two way binding combobox in gridview - still not found. Very bad.
    Friday, January 11, 2019 1:47 PM