'WPF ComboBox SelectedItem dynamic binding

I'd like to bind the SelectedItem of a ComboBox to a specific item of an ObservableCollection inside the ViewModel.

In the ViewModel I have one ObservableCollection property:

Public Property SourceList As ObservableCollection(Of CustomItem)

Then I have these two custom class

Public Class CustomItem 
    Public Property Code As String
    Public Property Source As List(Of CustomValue)
    Public Property Selection As Object
End Class

Public Class CustomValue 
    Public Property Id As Integer
    Public Property Desc As String
End Class

The ComboBox in XAML

<Border Tag="10">
    <ComboBox DisplayMemberPath="Desc">
            <ComboBox.ItemsSource>
                <MultiBinding Converter="{StaticResource comboSourceConverter}">
                    <Binding Path="SourceList"/>
                    <Binding Path="Tag" RelativeSource="{RelativeSource AncestorLevel=1, Mode=FindAncestor, AncestorType=Border}" />
                </MultiBinding>
            </ComboBox.ItemsSource>
        </ComboBox>
</Border>

The converter I use to set the ItemsSource, which works correctly.

Public Function Convert(values() As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IMultiValueConverter.Convert
    If values(0) IsNot Nothing AndAlso values(1) IsNot Nothing Then
        Return DirectCast(values(0), ObservableCollection(Of CustomItem)).Where(Function(x) x.Code = CStr(values(1))).First().Source
    End If
    Return Binding.DoNothing
End Function

Public Function ConvertBack(value As Object, targetTypes() As Type, parameter As Object, culture As CultureInfo) As Object() Implements IMultiValueConverter.ConvertBack
    Throw New NotImplementedException
End Function

I tried to do something similar for the SelectedItem. In the XAML:

<Border Tag="10">
<ComboBox DisplayMemberPath="Desc">
        <ComboBox.ItemsSource>
            <MultiBinding Converter="{StaticResource comboSourceConverter}">
                <Binding Path="SourceList"/>
                <Binding Path="Tag" RelativeSource="{RelativeSource AncestorLevel=1, Mode=FindAncestor, AncestorType=Border}" />
            </MultiBinding>
        </ComboBox.ItemsSource>
        <ComboBox.SelectedItem>
            <MultiBinding Converter="{StaticResource comboSourceConverter}">
                <Binding Path="SourceList"/>
                <Binding Path="Tag" RelativeSource="{RelativeSource AncestorLevel=1, Mode=FindAncestor, AncestorType=Border}" />
            </MultiBinding>
        </ComboBox.SelectedItem>
    </ComboBox>

The converter return the correct value chosen in the ComboBox, but this way it will raise the Set method on the SourceList property since it is the one declared as the first Path in the Multibinding.

Public Function Convert(values() As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IMultiValueConverter.Convert
    If values(0) IsNot Nothing AndAlso values(1) IsNot Nothing Then
        Return DirectCast(values(0), ObservableCollection(Of CustomItem)).Where(Function(x) x.Code = CStr(values(1))).First().Selection 
End If
    Return Binding.DoNothing
End Function

Public Function ConvertBack(value As Object, targetTypes() As Type, parameter As Object, culture As CultureInfo) As Object() Implements IMultiValueConverter.ConvertBack
    Return New Object(){value} 
End Function

Setting the binding manually everything works fine, but I'd prefer doing everything through the converter if possible.

Dim _item As CustomItem = SourceList.Where(Function(x) x.Code = Border.Tag).FirstOrDefault()
Dim _binding = New Binding("Selection")
_binding.Mode = BindingMode.TwoWay
_binding.Source = _item 
ComboBox.SetBinding(ComboBox.SelectedItemProperty, _binding)


Solution 1:[1]

You should be letting XAML do a lot more of the hard work for you. Get your CustomItem once, and bind to its properties without any fussing around.

<Border DataContext="{Binding SourceList[10]}">
    <ComboBox 
        DisplayMemberPath="Desc"
        ItemsSource="{Binding Source}"
        SelectedItem="{Binding Selection}"
        />
</Border>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1