WPF 链式任务。第二个任务不刷新网格

WPF Chained tasks. 2nd task does not refresh grid

我想在一个 wpf 屏幕上加载 2 个数据网格。第一个将包含装运 header 信息。第二个将包含详细信息和可能的许多记录。我想在后台加载第一个网格,然后显示它。它应该很快出现。我想在加载第一个网格后加载第二个网格。这样用户可以在加载详细信息时浏览 headers。

我可以使用 BackgroundWorker 来完成这项工作,但我正在尝试切换到使用 Tasks,据我所知,这是 newer/better 执行此操作的方法。

这是将任务链接在一起的代码。

            Task LoadShipmentsTask = new Task(() =>
            {
                LoadShipments();
            } );

            Task LoadShipmentDetailsTask = LoadShipmentsTask.ContinueWith((t) => 
            {
                LoadShipmentDetails();
            } );

            LoadShipmentsTask.Start();

以下是任务调用的方法:

    void LoadShipments()
    {
        App.Current.Dispatcher.Invoke((Action)delegate
        {
            ocShipments = Shipments.RetrieveObservableCollection(false);

            txtBlockRecordCount.Text = string.Format("Load of shipments has COMPLETED.  {0} records. ", ocShipments.Count.ToString());

        });
    }

    void LoadShipmentDetails()
    {
        App.Current.Dispatcher.Invoke((Action)delegate
        {
            ocShipmentDetails = Shipments.RetrieveShipmentDetailObservableCollection();

            txtBlockShipmentDetailRecordCount.Text = string.Format("Loading of Shipment Details Complete.  {0} detail records loaded.", ocShipmentDetails.Count.ToString());


        });
    }

我必须使用 ...Dispatcher.Invoke 来获取 UI 线程来更新网格。

两个可观察的 collections 加载正常,但只有第一个网格被刷新。

奇怪的是,这段代码工作了一两次,但并不始终如一。

Datacontext 在任一线程完成之前设置一次。
数据网格的 Itemsource 设置为可观察的 collections。

我想我必须以某种方式通知 UI 线程,但不确定我遗漏了什么。

更新 - 工作代码:

                Task.Factory.StartNew(LoadShipments).ContinueWith(
                    w =>
                    {
                        ocShipments.Clear();

                        // Load from a temporary collection populated in background... 
                        // Forces the UI to update since CollectionChanged is fired
                        foreach (Shipment s in ocShipmentsTemp)
                        {
                            ocShipments.Add(s);
                        }

                        txtBlockRecordCount.Text = string.Format("Load of shipments has COMPLETED.  {0} records. ", ocShipments.Count.ToString());

                        txtBlockShipmentDetailRecordCount.Text = string.Format("Loading Shipment Details.");

                        LoadShipmentDetails();

                        ocShipmentDetails.Clear();

                        // Forces the UI to update since CollectionChanged is fired
                        foreach (ShipmentDetail sd in ocShipmentDetailsTemp)
                        {
                            ocShipmentDetails.Add(sd);
                        }

                        txtBlockShipmentDetailRecordCount.Text = string.Format("Loading of Shipment Details Complete.  {0} detail records loaded.", ocShipmentDetails.Count.ToString());

                    }
                    , token, TaskContinuationOptions.None, scheduler);

查看下面的评论,了解我的代码无效的原因。

推荐的任务使用方式是通过 Factory。

System.Threading.Tasks.Factory.StartNew(() =>
{
    // my routine
}).
ContinueWith(()=>
{
    // my chained routine
});

此外,如果您在第一部分中对委托使用 lambda 表达式,为什么在另一部分中使用 (Action)delegate 而不是:

App.Current.Dispatcher.Invoke(new Action(() =>
        {
            ocShipments = Shipments.RetrieveObservableCollection(false);

            txtBlockRecordCount.Text = string.Format("Load of shipments has COMPLETED.  {0} records. ", ocShipments.Count.ToString());

        }));

我觉得其他一切都很好。

如果您只是在内部调用 Dispatcher,为什么要使用 Task,您将直接返回到 UI 线程并一个接一个地发送 2 条消息。您应该使用 Dispatcher.BeginInvoke 并放松任务。他们毫无意义。请尝试以下操作:

App.Current.Dispatcher.BeginInvoke((Action)delegate
            {
                //LoadShipments
                ocShipments = Shipments.RetrieveObservableCollection(false);
                txtBlockRecordCount.Text = string.Format("Load of shipments has COMPLETED.  {0} records. ", ocShipments.Count.ToString());
                //LoadShipmentDetails
                ocShipmentDetails = Shipments.RetrieveShipmentDetailObservableCollection();
                txtBlockShipmentDetailRecordCount.Text = string.Format("Loading of Shipment Details Complete.  {0} detail records loaded.", ocShipmentDetails.Count.ToString());

            });

更新 顺便说一句,您的问题也可能发生,因为您正在设置集合。当使用 DataBinding 时,你不应该做 Collection = new Collection 你应该总是做 :

Collection.Clear();
Collection.AddRange(new collection);

由于您的网格绑定到集合,如果您将 属性 设置为新集合,您将失去绑定,因为您的网格将保持绑定到旧集合引用并且不会接收更新。

我认为您应该使用以下任务(更新 UI-线程上的网格)

//Get UI-Thread Scheduler
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

Task.Factory.StartNew<IEnumerable<Shipments>>(() =>
    {
        return Shipments.RetrieveObservableCollection(false);
    }).ContinueWith((shipmentsTask) =>
    {
        txtBlockRecordCount.Text = string.Format("Load of shipments has COMPLETED.  {0} records. ", shipmentsTask.Result.Count.ToString());
    }, scheduler);

很明显,您可以将其与您的第二个任务再次链接起来。请记住在进入后台任务之前检索调度程序

要了解解决方案,您必须知道 运行 任务在后台运行,并且需要在 UI-线程(或另一个线程,如果他们需要进一步处理)。

你的情况只能在延续任务中设置数据到Datagrid,不能在后台任务