Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Chained tasks. 2nd task does not refresh grid

I want to load 2 datagrids on one wpf screen. The first one will contain shipment header info. The second will contain the details and potentially many records. I want to load the first grid in the background and then display it. It should show up fairly quickly. I want to load the second grid after the first is loaded. That way users can browsing the headers while details are being loaded.

I can make this working using BackgroundWorker, but I'm trying to switch to using Tasks, which I understand is the newer/better way to do this.

Here is the code to chain the tasks together.

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

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

            LoadShipmentsTask.Start();

Here are the methods called by the tasks:

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


        });
    }

I had to use the ...Dispatcher.Invoke to get the UI Thread to update the grids.

Both observable collections load fine, but only the first grid is refreshed.

Strangely, this code worked once or twice, but not consistently.

The Datacontext is set once before either of the threads finish.
The Itemsource of the datagrids are set to the observable collections.

I'm guessing I have to notify the UI thread somehow, but not sure what I'm missing.

UPDATE - Working Code:

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

See Comments below for explanation of reasons that my code was not work.

like image 250
pStan Avatar asked Mar 13 '26 21:03

pStan


1 Answers

The recommended way to use tasks is via Factory.

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

Also, if you are using lambda expressions for delegates in first part why do you use (Action)delegate in the other instead of:

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

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

        }));

Everything else looks fine to me.

like image 74
VidasV Avatar answered Mar 15 '26 09:03

VidasV



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!