2
votes

The following XAML produces a run-time Binding error when I click on an item in the ListBox:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="WpfApplication1.MainWindow"
    x:Name="Window"
    Title="MainWindow"
    Width="640" Height="480">
    <Window.Resources>
        <x:Array x:Key="strings" Type="{x:Type sys:String}">
            <sys:String>one</sys:String>
            <sys:String>two</sys:String>
            <sys:String>three</sys:String>
            <sys:String>four</sys:String>
        </x:Array>
    </Window.Resources>
    <Grid>
        <ListBox
            DataContext="{StaticResource strings}"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding}"
            SelectedValuePath="{Binding /Length}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.Resources>
                            <Style TargetType="{x:Type Label}">
                                <Setter Property="Background" Value="Yellow"/>
                                <Setter Property="Margin" Value="0,0,4,0"/>
                                <Setter Property="Padding" Value="0"/>
                            </Style>
                        </Grid.Resources>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <!-- Row 0 -->
                        <Label Grid.Column="0" Grid.Row="0">String:</Label>
                        <TextBlock
                            Grid.Column="1"
                            Grid.Row="0"
                            Text="{Binding}"/>
                        <!-- Row 1 -->
                        <Label Grid.Column="0" Grid.Row="1">Length:</Label>
                        <TextBlock
                            Grid.Column="1"
                            Grid.Row="1"
                            Text="{Binding Length, Mode=Default}"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

This is the run-time Binding error message:

System.Windows.Data Error: 39 : BindingExpression path error: '3' property not found on 'object' ''String' (HashCode=1191344027)'. BindingExpression:Path=3; DataItem='String' (HashCode=1191344027); target element is 'ListBox' (Name=''); target property is 'NoTarget' (type 'Object')

I would like the selected value of the ListBox to be the Length of the selected String object. What is wrong with my SelectedValuePath Binding syntax? Are there any related issues with IsSynchronizedWithCurrentItem?

1

1 Answers

5
votes

Short answer

Replace

SelectedValuePath="{Binding /Length}"

with

SelectedValuePath="Length"

Long answer

SelectedValuePath is a string that gives the path from an object to the selected value. By writing SelectedValuePath="{Binding /Length}" you are binding SelectedValuePath (not SeletedValue) to the "Length" property of the selected item, so if the length of the selected item is 3 then the value of the SelectedValuePath property is set to the string "3". Then WPF tries to compute SelectedValue by finding a property named "3" on the string. Since the string object doesn't have a property named "3" you get the error.

You might think that SelectedValue={Binding /Length}" would do the trick, and indeed it does express the concept of what you are actually trying to do. But it does not actually work because Selector has code that overwrites SelectedValue whenever SelectedItem changes.

Another way to look at this is that setting SelecteValuePath to the value "abcd" is effectively equivalent to setting SelectedValue to "{Binding /abcd}" (but only if IsSynchronizedWithCurrentItem="true").