1

我有DataGridView一个 WPF 应用程序。检索整个数据是一项涉及密集 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 { }
        }
    }

解释

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

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

问题

当我运行应用程序时,我只得到一个(空的,需要更多地查看它以查找格式问题)行。如果我尝试单击该行,则会收到关于集合处于不连贯状态的异常(因为计数与网格不匹配)。对我来说,例外的原因是 DataGrid 不会在每次添加新项目时都得到更新。

问题

  • 我的设计有问题吗?
  • 如何在 WPF 中实现数据绑定的异步性?
4

1 回答 1

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"));
}
于 2013-05-07T20:27:32.953 回答