4
votes

I want my WPF ComboBox's ItemsSource property to be bound to MyListObject's MyList property. The problem is that when I update the MyList property in code, the WPF ComboBox is not reflecting the update. I am raising the PropertyChanged event after I perform the update, and I thought WPF was supposed to automatically respond by updating the UI. Am I missing something?

Here's the CLR object:

Imports System.ComponentModel

Public Class MyListObject
    Implements INotifyPropertyChanged

    Private _mylist As New List(Of String)

    Public Sub New()
        _mylist.Add("Joe")
        _mylist.Add("Steve")
    End Sub

    Public Property MyList() As List(Of String)
        Get
            Return _mylist
        End Get
        Set(ByVal value As List(Of String))
            _mylist = value
        End Set
    End Property

    Public Sub AddName(ByVal name As String)

        _mylist.Add(name)

        NotifyPropertyChanged("MyList")

    End Sub

    Private Sub NotifyPropertyChanged(ByVal info As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
    End Sub

    Public Event PropertyChanged(ByVal sender As Object, _
            ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
            Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

End Class

Here is the XAML:

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    xmlns:local="clr-namespace:WpfApplication1"
        >

    <Window.Resources>
        <ObjectDataProvider x:Key="MyListObject" ObjectType="{x:Type local:MyListObject}"/>
    </Window.Resources>

        <Grid>

        <ComboBox Height="23"
                  Margin="24,91,53,0"
                  Name="ComboBox1"
                  VerticalAlignment="Top" 
                  ItemsSource="{Binding Path=MyList, Source={StaticResource MyListObject}}"
                  />
        <TextBox Height="23"
                 Margin="24,43,134,0"
                 Name="TextBox1"
                 VerticalAlignment="Top" />
        <Button Height="23"
                HorizontalAlignment="Right"
                Margin="0,43,53,0"
                Name="btn_AddName"
                VerticalAlignment="Top"
                Width="75">Add</Button>
    </Grid>
</Window>

And here's the simple code-behind:

Class Window1 

    Private obj As New MyListObject

    Private Sub btn_AddName_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) _ 
    Handles btn_AddName.Click

        obj.AddName(TextBox1.Text)

    End Sub
End Class

Thanks!

3

3 Answers

3
votes

You are binding to a list of strings. That list class does not implement Inotifyproperty. You should use an observablecollection instead. I also notice in your code behind you declare

Private obj As New MyListObject

This is not the static resource you bound the combo box to. So your add call would not be reflected in your view.

2
votes

The ObservableCollection is most likely the solution, but if it still gives you grief, you can directly access your static resource by calling the following code after your list gets updated:

DirectCast(Me.FindResource("MyListObject"), ObjectDataProvider).Source = _myList
0
votes

Try using a BindingList(Of T) instead of a List(Of T).

Edit: I am new to WPF and it does look like BindingList isn't a complete solution to your problem, but it might be a step in the right direction. I was able to test the MyListObject converted to BindingList in WinForm and the ListChanged event was raised to the ComboBox which then updated its list.

I found this (possible) solution to wrap your class in an ObservableCollection that might help you solve your problem

Enabling WPF Magic Using WCF - Part 1

This is the code to update your object to a BindingList. Combine your code with the code from that resource and you should be good to go.

Public Class MyListObject
    ...

    'Private _mylist As New List(Of String)
    Private _mylist As New BindingList(Of String)

    ...

    'Public Property MyList() As List(Of String)
    '    Get
    '        Return _mylist
    '    End Get
    '    Set(ByVal value As List(Of String))
    '        _mylist = value
    '    End Set
    'End Property

    Public Property MyList() As BindingList(Of String)
        Get
            Return _mylist
        End Get
        Set(ByVal value As BindingList(Of String))
            _mylist = value
        End Set
    End Property

    ...

End Class