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)