【问题标题】:List<string> bound to ComboBoxList<string> 绑定到 ComboBox
【发布时间】:2012-12-19 06:08:50
【问题描述】:

我对绑定到ComboBox 的列表有疑问。

List:

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

ComboBox 的 XAML 代码:

<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"/>

现在我从另一个线程填写List

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

一切看起来都不错,ComboBox 改变了它的项目。 当我尝试更新 MainThread 中的ComboBox (CourseList) 时:

    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);
            }
    }

CourseList 的所有元素都已更改(我可以在 Textbox 中看到它),但在 ComboxBox 中没有任何反应。

有什么想法吗?

【问题讨论】:

  • 只是为了排除,尝试从组合框 xaml 中取消绑定 CourseSelectedItem 和 CourseSelectedIndex,看看会发生什么。有时同时设置这两个属性时,事情会变得很奇怪。

标签: c# wpf list binding combobox


【解决方案1】:

尝试将CourseList 更改为ObervableCollection&lt;T&gt;

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

Binding 仅在设置CourseList 时(分配列表时)而不是在其内容更改时通知 UI。

来自Invoke event on MainThread from worker thread的一些代码

这表明List&lt;&gt; 一旦分配就不会改变,ObservableList&lt;&gt; 会改变。

视图模型

//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.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>

这可以很容易地修改为使用 Dispatcher Invoke:Change WPF controls from a non-main thread using Dispatcher.Invoke

【讨论】:

  • 但是 ObervableCollection 只能在 Dispatcher Thread 中更改,所以我必须调用,对吗?
  • 是的,调用 Add 和 Clear 方法调用,而不是直接从“其他”线程调用它们。
猜你喜欢
  • 2011-11-11
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 2013-10-04
  • 1970-01-01
  • 2013-12-07
  • 1970-01-01
  • 2013-09-05
相关资源
最近更新 更多