0

I have a problem with a list, which is bound to a ComboBox.

The List:

private List<string> _CourseList = new List<string>();
public List<string> CourseList
        {
            get { return _CourseList; }
            set
            {
                _CourseList = value;
                OnPropertyChanged("CourseList");
            }
        }

XAML code of the ComboBox:

<ComboBox x:Name="cbxCourse" Height="23" MinWidth="100" Margin="5,1,5,1" VerticalAlignment="Top" ItemsSource="{Binding Path=CourseList}" IsEnabled="{Binding Path=CanExport}" SelectedIndex="{Binding Path=CourseListSelectedIndex}" SelectedItem="{Binding Path=CourseListSelectedItem}" SelectionChanged="cbxCourse_SelectionChanged"/>

Now i fill the List from another thread:

void Database_LoadCompleted(object sender, SqliteLoadCompletedEventArgs e)
{
    foreach (DataTable Table in DataSetDict[CampagneList[0]].Tables)
    {
        CourseList.Add(Table.TableName);
    }
}

Everything looks good, and the ComboBox changed its items. When I try to update the ComboBox (CourseList) in the MainThread with:

    private void cbxCampagne_SelectionChanged(object sender, EventArgs e)
    {
        if (cbxCampagne.SelectedItem != null)
        {
            CourseList.Clear();
            foreach (DataTable Table in DataSetDict[CampagneList[_CampagneListSelectedIndex]].Tables)
            {
                CourseList.Add(Table.TableName);
            }
    }

all Elements of CourseList changed (I can see it in a Textbox) but in the ComboxBox nothing happens.

Any ideas?

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
derWilly
  • 455
  • 3
  • 15
  • Just to rule out, try unbinding your CourseSelectedItem and CourseSelectedIndex from the combobox xaml and see what happens. Sometimes when setting both those properties, things can get weird. – TMan Jan 04 '13 at 19:27

1 Answers1

1

Try changing CourseList an ObervableCollection<T>

http://msdn.microsoft.com/en-us/library/ms668604.aspx

The Binding only notifies the UI when CourseList is set (when the list is assigned) and not when its contents change.

Some Code from Invoke event on MainThread from worker thread

This demonstrates that the List<> will not change once assigned and the ObservableList<> will change.

ViewModel

//Viewmodel
public class WindowViewModel : INotifyPropertyChanged
{
    private volatile bool _canWork;
    private List<string> _items;
    private ObservableCollection<string> _obervableItems;

    public WindowViewModel()
    {
        //Queue some tasks for adding and modifying the list
        ThreadPool.QueueUserWorkItem(AddItems);
        ThreadPool.QueueUserWorkItem(ModifyItems);

        //Create a background worker to do some work and then we can bind the output to
        //our ObservableList
        var obervableWorker = new BackgroundWorker();
        obervableWorker.DoWork += ObervableWorkerOnDoWork;
        obervableWorker.RunWorkerCompleted += ObervableWorkerOnRunWorkerCompleted;

        obervableWorker.RunWorkerAsync();
    }

    private void ObervableWorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
    {
        var items = ObservableItems as ObservableCollection<string>;

        var workerItems = runWorkerCompletedEventArgs.Result as List<string>;

        foreach (var workerItem in workerItems)
        {
            items.Add(workerItem);
        }

        for (int i = 50; i < 60; i++)
        {
            var item = items.First(x => x == i.ToString());
            items.Remove(item);
        }
    }

    private void ObervableWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
    {
        Thread.Sleep(100);
        int count = 0;
        var items = new List<string>();
        while (100 > count++)
        {
            items.Add(count.ToString());
        }

        doWorkEventArgs.Result = items;
    }

    private void ModifyItems(object state)
    {
        while (!_canWork)
        {
            Thread.Sleep(100);
        }
        var items = Items as List<string>;
        for (int i = 50; i < 60; i++)
        {
            items.RemoveAt(i);
        }
    }

    private void AddItems(object state)
    {
        Thread.Sleep(100);
        int count = 0;
        var items = Items as List<string>;
        while (100 > count++)
        {
            items.Add(count.ToString());
        }
        _canWork = true;
    }

    public IEnumerable<string> Items
    {
        get { return _items ?? (_items = new List<string>()); }
        set { _items = new List<string>(value);
            OnPropertyChanged();
        }
    }

    public IEnumerable<string> ObservableItems
    {
        get { return _obervableItems ?? (_obervableItems = new ObservableCollection<string>()); }
        set { _obervableItems = new ObservableCollection<string>(value); OnPropertyChanged();}
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Window

//Window.Xaml
<Window x:Class="ComboBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:comboBox="clr-namespace:ComboBox"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext><comboBox:WindowViewModel /></Window.DataContext>
    <Grid>
        <ComboBox Width="200" Height="22" ItemsSource="{Binding Items}"></ComboBox>
        <ComboBox Margin="0,44,0,0" Width="200" Height="22" ItemsSource="{Binding ObservableItems}"></ComboBox>
    </Grid>
</Window>

This can be easily modified to use the Dispatcher Invoke: Change WPF controls from a non-main thread using Dispatcher.Invoke

Community
  • 1
  • 1
Dustin Kingen
  • 20,677
  • 7
  • 52
  • 92