views:

128

answers:

3

Hi there.

I'm having a big problem when calling a web service from my WPF application. The application/window locks until the process has completed. I've attempted to run this asynchronously but the problem still persists.

Currently, the web service call I'm making can last 45-60 seconds. It runs a process on the server to fetch a big chunk of data. As it take a little while I wanted to have a progress bar moving indeterminately for the user to see that the application hasn't stalled or anything (you know how impatatient they get).

So:

    private void btnSelect_Click(object sender, RoutedEventArgs e)
    {
        wDrawingList = new WindowDrawingList(systemManager);

        AsyncMethodHandler caller = default(AsyncMethodHandler);

        caller = new AsyncMethodHandler(setupDrawingList);

        // open new thread with callback method 
        caller.BeginInvoke((Guid)((Button)sender).Tag, MyAsyncCallback, null);
    }

Click a button and the app will create the form that the async stuff will be posted to and set up the async stuff calling the async method.

    public bool setupDrawingList(Guid ID)
    {
        if (systemManager.set(ID))
        {
            wDrawingList.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
            {
                wDrawingList.ShowForm();
                Hide();
            }));

            return true;
        }

        return false;
    }

This is the async method. The showForm method contains the calls to setup the new form including the monster web service call

    public void MyAsyncCallback(IAsyncResult ar)
    {
        // Because you passed your original delegate in the asyncState parameter of the Begin call, you can get it back here to complete the call.
        MethodDelegate dlgt = (MethodDelegate)ar.AsyncState;

        // Complete the call.
        bool output = dlgt.EndInvoke(ar);

        try
        {
            // Retrieve the delegate. 
            AsyncResult result = (AsyncResult)ar;
            AsyncMethodHandler caller = (AsyncMethodHandler)result.AsyncDelegate;

            // Because this method is running from secondary thread it can never access ui objects because they are created 
            // on the primary thread.

            // Call EndInvoke to retrieve the results. 
            bool returnValue = caller.EndInvoke(ar);

            // Still on secondary thread, must update ui on primary thread 
            UpdateUI(returnValue == true ? "Success" : "Failed");
        }
        catch (Exception ex)
        {
            string exMessage = null;
            exMessage = "Error: " + ex.Message;
            UpdateUI(exMessage);
        }
    }

    public void UpdateUI(string outputValue)
    {
        // Get back to primary thread to update ui 
        UpdateUIHandler uiHandler = new UpdateUIHandler(UpdateUIIndicators);
        string results = outputValue;

        // Run new thread off Dispatched (primary thread) 
        this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, uiHandler, results);
    }

    public void UpdateUIIndicators(string outputValue)
    {
        // update user interface controls from primary UI thread
        sbi3.Content = "Processing Completed.";
    }

Any help or theories are appreciated. I'm at a loss.

Thanks in advance

A: 

Hello again.

I actually answered my own question as I wrote it but I thought as I'd wrote it I may as well post along with the answer in case anyone else needs it.

I realised that the method that was causing the problems was in the dispatcher which basically overrides the main thread, so even though I was setting up a nice ascynchronous thread I was returning and running the main process on what is basically the main thread.

Or at least this is my interpretation, any further comments to help me understand further are aprrecitated.

So I removed the monster call from the ShowForm method and put it straight in the async method and BooYah! Window functionality is mine.

HTH

SumGuy
Turns out you solved it faster than I could type it. Nice one.
Simon P Stevens
A: 

You are invoking the method onto the GUI thread using the dispatcher. This means that even though you started off on a background thread, you are switching straight back onto the GUI thread so the GUI will not be able to respond.

You need to let the main work happen on the background thread and then only use the dispatcher to switch to the GUI thread when you need to update the progress bar.

Simon P Stevens
A: 

Looks like you're doing UI stuff synchronously from your asynchronous method (wat?). Also your code is way overly complex, which you may notice as you've confunded yourself with it.

I'd suggest drastically simplifying this, primarily by JUST doing your web service call asynchronously, then update the UI from the callback. Once you have that working you can see about adding back all this other stuff which makes my brain hurt.

Will
Agreed. I do definately do need to tidy it up a bit. This was just the result of a couple of hours worth of hacking and trying different things lol
SumGuy
@SumGuy ya, done it before. Nowadays I make a test app that does just what I need in order to prove the code then roll it in. Works better than trying to work new code in with what's already there when I'm lost on what direction to go in...
Will