Max Ehrlich
Max Ehrlich

Reputation: 2525

WPF combo box not updating correctly

I am trying to use a WPF combobox with a databinding on its SelecteItem property. When the user chooses a new selection, the SelectedItem changes, but my model is not updated properly, and the SelectionBoxItem (item displayed in the combobox) is not changed.

More specifically, I have the following two elements:

<ListView Name="lvNodes" ItemsSource="{Binding Path=Nodes}" SelectedItem="{Binding Path=CurrentNode, Mode=TwoWay}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=Name}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>

This listview works as expected. The combobox that is having problems is defined like so

<ComboBox ItemsSource="{Binding Path=CameraViews}" SelectedItem="{Binding Path=CurrentNode.NodeInfo.Camera1, Converter={StaticResource EnsureBlank}, Mode=TwoWay}" Name="comboBoxTopCam" SelectionChanged="comboBoxTopCam_SelectionChanged">
                                <ComboBox.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=Name}"/>
                                    </DataTemplate>
                                </ComboBox.ItemTemplate>
                            </ComboBox>

So when the user selects a node from the ListView (above), the ComboBox updates to display the camera that node is using.

Now the strange behavior. If the user clicks on a new item on the combo box, I can stop with the debugger and examine the SelecteItem property. This is always set properly, however the CurrentNode.NodeInfo.Camera1 property is not changed, and the string displayed in the "SelectionBox" is unchanged.

It gets even stranger, however. If the user switches the node (using the ListView defined earlier) AFTER making such a selection on the ComboBox, the ComboBox again doesn't display the new value, but the SelectedItem is correct. If the user then changes the selected item in the ListView AGAIN, then it starts displaying the correct value, and is fine until the user messes with the combo box again.

Now I said earlier that my model is not being updated when the ComboBox changes, so I made an event handler to update the model for me (you can see this in the definition of the ComboBox). This is defined as possible

private void comboBoxTopCam_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        CameraViewInfo selCam = comboBoxTopCam.SelectedItem as CameraViewInfo;
        if (selCam != null)
        {
            OTNode selNode = lvNodes.SelectedItem as OTNode;

            if (selCam.Name == "None")
                selNode.NodeInfo.Camera1 = new CameraViewInfo();
            else
                selNode.NodeInfo.Camera1 = selCam; // <- This line affects the behavior
        }
    }

When I comment out the marked line in the above listing, the ComboBox displays the correct item in the SelectionBox, but doesn't update my model (e.g. CurrentNode.NodeInfo.Camera1 is not changed). This happens when I use lvNodes.SelectedItem OR when I use CurrentCamera (the databound property), it makes no difference which.

I know this is a pretty weird problem, and I did the best I could explaining it, if anyone wants more information (debug output from databindings, code for the converter, information about equality operators etc) I am happy to provide it, I would event be willing to upload a video showing the odd behavior if necessary, since its a little hard to make sense of from text alone.

Update:

Code for Converter

public class EnsureBlankConterter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is CameraViewInfo)
        {
            if ((value as CameraViewInfo).Name == "")
            {
                return new CameraViewInfo()
                {
                    Name = "None"
                };
            }
        }

        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is CameraViewInfo)
        {
            if ((value as CameraViewInfo).Name == "None")
            {
                return new CameraViewInfo();
            }
        }

        return value;
    }
} 

Debug Output Using PresentationTraceSource.High

At load time:

System.Windows.Data Warning: 54 : Created BindingExpression (hash=45045097) for Binding (hash=10919470)
System.Windows.Data Warning: 56 :   Path: 'CurrentNode.NodeInfo.Camera1'
System.Windows.Data Warning: 60 : BindingExpression (hash=45045097): Attach to System.Windows.Controls.ComboBox.SelectedItem (hash=34909671)
System.Windows.Data Warning: 65 : BindingExpression (hash=45045097): Resolving source 
System.Windows.Data Warning: 68 : BindingExpression (hash=45045097): Found data context element: ComboBox (hash=34909671) (OK)
System.Windows.Data Warning: 76 : BindingExpression (hash=45045097): Activate with root item ConfigurationWindow (hash=22010384)
System.Windows.Data Warning: 105 : BindingExpression (hash=45045097):   At level 0 using cached accessor for ConfigurationWindow.CurrentNode: RuntimePropertyInfo(CurrentNode)
System.Windows.Data Warning: 102 : BindingExpression (hash=45045097): Replace item at level 0 with ConfigurationWindow (hash=22010384), using accessor RuntimePropertyInfo(CurrentNode)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 0 from ConfigurationWindow (hash=22010384) using RuntimePropertyInfo(CurrentNode): <null>
System.Windows.Data Warning: 104 : BindingExpression (hash=45045097):   Item at level 1 is null - no accessor
System.Windows.Data Warning: 101 : BindingExpression (hash=45045097): Replace item at level 2 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=45045097): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 86 : BindingExpression (hash=45045097): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 87 : BindingExpression (hash=45045097): TransferValue - using final value <null>
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 0 from ConfigurationWindow (hash=22010384) using RuntimePropertyInfo(CurrentNode): OTNode (hash=59090892)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 0 from ConfigurationWindow (hash=22010384) using RuntimePropertyInfo(CurrentNode): OTNode (hash=59090892)
System.Windows.Data Warning: 106 : BindingExpression (hash=45045097):   At level 1 - for OTNode.NodeInfo found accessor ReflectPropertyDescriptor(NodeInfo)
System.Windows.Data Warning: 102 : BindingExpression (hash=45045097): Replace item at level 1 with OTNode (hash=59090892), using accessor ReflectPropertyDescriptor(NodeInfo)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 1 from OTNode (hash=59090892) using ReflectPropertyDescriptor(NodeInfo): OTNodeInfo (hash=34742292)
System.Windows.Data Warning: 106 : BindingExpression (hash=45045097):   At level 2 - for OTNodeInfo.Camera1 found accessor ReflectPropertyDescriptor(Camera1)
System.Windows.Data Warning: 102 : BindingExpression (hash=45045097): Replace item at level 2 with OTNodeInfo (hash=34742292), using accessor ReflectPropertyDescriptor(Camera1)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 2 from OTNodeInfo (hash=34742292) using ReflectPropertyDescriptor(Camera1): CameraViewInfo (hash=29245900)
System.Windows.Data Warning: 78 : BindingExpression (hash=45045097): TransferValue - got raw value CameraViewInfo (hash=29245900)
System.Windows.Data Warning: 80 : BindingExpression (hash=45045097): TransferValue - user's converter produced CameraViewInfo (hash=29245900)
System.Windows.Data Warning: 87 : BindingExpression (hash=45045097): TransferValue - using final value CameraViewInfo (hash=29245900)

After making a selection on the combo box that wasn't displayed in the selection box:

System.Windows.Data Warning: 88 : BindingExpression (hash=45045097): Update - got raw value CameraViewInfo (hash=7936647)
System.Windows.Data Warning: 90 : BindingExpression (hash=45045097): Update - user's converter produced CameraViewInfo (hash=7936647)
System.Windows.Data Warning: 92 : BindingExpression (hash=45045097): Update - using final value CameraViewInfo (hash=7936647)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 0 from ConfigurationWindow (hash=22010384) using RuntimePropertyInfo(CurrentNode): OTNode (hash=59090892)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 1 from OTNode (hash=59090892) using ReflectPropertyDescriptor(NodeInfo): OTNodeInfo (hash=22129877)

Switching the ListView to a new node, the Selection Box still didn't update:

System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 0 from ConfigurationWindow (hash=22010384) using RuntimePropertyInfo(CurrentNode): OTNode (hash=63206919)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 0 from ConfigurationWindow (hash=22010384) using RuntimePropertyInfo(CurrentNode): OTNode (hash=63206919)
System.Windows.Data Warning: 106 : BindingExpression (hash=45045097):   At level 1 - for OTNode.NodeInfo found accessor ReflectPropertyDescriptor(NodeInfo)
System.Windows.Data Warning: 102 : BindingExpression (hash=45045097): Replace item at level 1 with OTNode (hash=63206919), using accessor ReflectPropertyDescriptor(NodeInfo)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 1 from OTNode (hash=63206919) using ReflectPropertyDescriptor(NodeInfo): OTNodeInfo (hash=35582358)
System.Windows.Data Warning: 106 : BindingExpression (hash=45045097):   At level 2 - for OTNodeInfo.Camera1 found accessor ReflectPropertyDescriptor(Camera1)
System.Windows.Data Warning: 102 : BindingExpression (hash=45045097): Replace item at level 2 with OTNodeInfo (hash=35582358), using accessor ReflectPropertyDescriptor(Camera1)
System.Windows.Data Warning: 99 : BindingExpression (hash=45045097): GetValue at level 2 from OTNodeInfo (hash=35582358) using ReflectPropertyDescriptor(Camera1): CameraViewInfo (hash=49590542)
System.Windows.Data Warning: 78 : BindingExpression (hash=45045097): TransferValue - got raw value CameraViewInfo (hash=49590542)
System.Windows.Data Warning: 80 : BindingExpression (hash=45045097): TransferValue - user's converter produced CameraViewInfo (hash=49590542)
System.Windows.Data Warning: 87 : BindingExpression (hash=45045097): TransferValue - using final value CameraViewInfo (hash=49590542)

After switching the node using the list view that DID update the selection box:

System.Windows.Data Warning: 99 : BindingExpression (hash=51217614): GetValue at level 0 from ConfigurationWindow (hash=63245828) using RuntimePropertyInfo(CurrentNode): OTNode (hash=8023662)
System.Windows.Data Warning: 99 : BindingExpression (hash=51217614): GetValue at level 0 from ConfigurationWindow (hash=63245828) using RuntimePropertyInfo(CurrentNode): OTNode (hash=8023662)
System.Windows.Data Warning: 106 : BindingExpression (hash=51217614):   At level 1 - for OTNode.NodeInfo found accessor ReflectPropertyDescriptor(NodeInfo)
System.Windows.Data Warning: 102 : BindingExpression (hash=51217614): Replace item at level 1 with OTNode (hash=8023662), using accessor ReflectPropertyDescriptor(NodeInfo)
System.Windows.Data Warning: 99 : BindingExpression (hash=51217614): GetValue at level 1 from OTNode (hash=8023662) using ReflectPropertyDescriptor(NodeInfo): OTNodeInfo (hash=21425964)
System.Windows.Data Warning: 106 : BindingExpression (hash=51217614):   At level 2 - for OTNodeInfo.Camera1 found accessor ReflectPropertyDescriptor(Camera1)
System.Windows.Data Warning: 102 : BindingExpression (hash=51217614): Replace item at level 2 with OTNodeInfo (hash=21425964), using accessor ReflectPropertyDescriptor(Camera1)
System.Windows.Data Warning: 99 : BindingExpression (hash=51217614): GetValue at level 2 from OTNodeInfo (hash=21425964) using ReflectPropertyDescriptor(Camera1): CameraViewInfo (hash=6049320)
System.Windows.Data Warning: 78 : BindingExpression (hash=51217614): TransferValue - got raw value CameraViewInfo (hash=6049320)
System.Windows.Data Warning: 80 : BindingExpression (hash=51217614): TransferValue - user's converter produced CameraViewInfo (hash=6049320)
System.Windows.Data Warning: 87 : BindingExpression (hash=51217614): TransferValue - using final value CameraViewInfo (hash=6049320)

Upvotes: 0

Views: 2780

Answers (3)

Max Ehrlich
Max Ehrlich

Reputation: 2525

Ok so I ended up getting rid of the databindings altogether and using a very winforms style event model to update my data. I still cannot figure out why the databindings don't work, if anyone has a solution using databindings I will accept that as the answer.

Upvotes: 0

hbarck
hbarck

Reputation: 2944

Your converter creates a new object, while the text in the ItemTemplate's TextBlock stays bound to the original object. So, obviously it will not change when you change the new object's Name property.

Upvotes: 0

Bevan
Bevan

Reputation: 44307

I think your problem is with the way you're binding to SelectedItem:

<ComboBox ...
    SelectedItem="{Binding Path=CurrentNode.NodeInfo.Camera1, 
        Converter={StaticResource EnsureBlank}, Mode=TwoWay}"  ...

If memory serves correctly, SelectedItem is one property (Text is another) that (by default) only pushes updates through when focus is lost.

Add UpdateSourceTrigger="PropertyChanged" to the binding to force the update to happen more frequently. You shouldn't need your event.

Upvotes: 1

Related Questions