在WPF中异步填充DataGrid

时间:2013-05-07 20:19:36

标签: wpf data-binding datagridview observablecollection collectionviewsource

我在WPF应用程序中有一个DataGridView。检索整个数据是一项长期运行的任务,涉及密集的I / O.

我认为以异步方式填充网格是个好主意,即一旦元素被解码,它就会被实时添加到列表中。

在我的具体情况下,我正在从文件系统加载电子邮件,以便在与Outlook主窗口非常相似的网格上显示。

代码

目前我已经定义了ViewModel(此处为简化版)

public class EmailViewModel: INotifyPropertyChanged {
    public string From, To, Subject, Size;
    public DateTime Date;
    //THIS IS A SIMPLIFIED VERSION. THE REAL CODE REALLY IMPLEMENTS INOTIFYPROPERTYCHANGED
}

然后我使用以下片段:

//Window fragment
<Window.Resources>
    <CollectionViewSource x:Key="Emails" Source="{Binding}"/>
</Window.Resources>

//DataGrid fragment
<DataGrid Name="dataGridEmails" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource ResourceKey=Emails}}">
     <DataGrid.DataContext>
            <EmailReader:EmailViewModel/>
        </DataGrid.DataContext>

//Code behind
void onClick(object sender, RoutedEventArgs e){
     ObservableCollection<EmailViewModel > collection = new ObservableCollection<EmailViewModel>();
     DataContext = collection;
     _binderThread = new Thread(o => EmailController.GetFromDirectory((string)o, collection));
     _binderThread.Start(rootPath);
}
 //GetFromDirectory
 public static void GetFromDirectory(string directoryPath, ICollection<EmailViewModel> targetCollection)
    {
        string[] files = Directory.GetFiles(directoryPath, "*.eml", SearchOption.AllDirectories);

        foreach (string file in files)
        {
            try
            {
                targetCollection.Add(LoadFromFile(file));
            }
            catch { }
        }
    }

解释

由于无法从外部线程访问Window的DataContext,因此我创建了新集合的实例,将其分配给DataContext并传递其引用到工人线程。如果我检查调试模式,我可以看到DataContext对象被填充。

工作线程只是将新电子邮件添加到它认为是通用集合的内容中,而是添加ObservableCollection,它应该在添加新对象时触发事件。该事件应该被UI捕获以更新DataGrid

问题

当我运行应用程序时,我只得到一个(空的,需要在格式问题上查看更多)行。如果我尝试单击该行,我会得到一个关于集合处于非相干状态的异常(因为计数与网格不匹配)。对我来说,异常是因为DataGrid没有在每个新项目的添加上更新。

问题

  • 我的设计出了什么问题?
  • 如何使用WPF实现数据绑定的异步?

1 个答案:

答案 0 :(得分:0)

尝试为collection.CollectionChanged添加一个监听器:

collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);

void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("collection"));
}