Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Accessing WPF Form Elements from inside async Task<T>
#1
Good day.  This code seemed to have worked before, but now I'm getting the dreaded:
 
Code:
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.


Here is a code snippet...  This is inside an async Task<bool> method....

I put the "print.it" and "return" statements in to see if I was getting an error from the static method RequestLicense.  But the exception moved to the print.it line for licenseName.Text.  Thoughts for retrieving the value from the form and passing it to the static method?

Thanks!

C# code:
        var b = new wpfBuilder("License Request Info").WinSize(400);
        b.R.Add("Full Name", out TextBox licenseName).Focus();
        b.R.Add("Email Address", out TextBox licenseEmail);
        b.R.AddOkCancel();
        b.End();
        if (!b.ShowDialog())
            return false; // quit
        
        
        var isOnline = await CheckConn();
        if (isOnline == true) { // we can communicate with the license server
            print.it(licenseName.Text);
            print.it(licenseEmail.Text);
            return false;
            var licReq = RequestLicense(licenseName.Text, licenseEmail.Text, "standard");
#2
I'm not an expert of async/await, and you can find better answers in stackoverflow etc. The info below may be incomplete or incorrect.

await tries to execute following code in the same thread as preceding code. But if it's impossible, executes in a thread pool thread.

Why impossible? For example SynchronizationContext.Current is null. It is null by default.

Functions like Application.Run and Window.ShowDialog set a synchronization context. Then await can continue in correct thread. But they restore previous context (which may be null) when returning.

Or you can explicitly set a synchronization context. Also the thread must dispatch Windows messages, or else the continuation code will never run.

Where await has no sense or can't be used, use .Result instead:
Code:
var isOnline = CheckConn().Result;

C# code:
// script ""

print.clear();
//Test1();
//Test2();
//Test3();
Test4();

void Test1() {
    F(); //continuation in bad thread
}

void Test2() {
    SynchronizationContext.SetSynchronizationContext(new System.Windows.Threading.DispatcherSynchronizationContext());    
    F(); //continuation can't run because nothing dispatches messages
}

void Test3() {
    SynchronizationContext.SetSynchronizationContext(new System.Windows.Threading.DispatcherSynchronizationContext());    
    F(); //ok, dialog.show dispatches messages
    dialog.show("");
}

void Test4() {
    new wpfBuilder("Window").AddButton("Button", _ => { F(); }).ShowDialog(); //ok while in dialog
    print.it(SynchronizationContext.Current); //null again
}

async Task F() {
    print.it(SynchronizationContext.Current);
    print.it(Thread.CurrentThread.ManagedThreadId);
    var isOnline = await Task.Run(() => true);
    print.it(Thread.CurrentThread.ManagedThreadId);
}
#3
This actually helped confirm something I was suspecting before lunch today.  I though perhaps once the OK/Cancel button was pressed access to the out vars would disappear.  So, I worked around this whole issue by adding a Button instead of ok/cancel that calls a HandleLicense function with WBButtonClickArgs...  then once the processing is completed I call args.Window.Close to clear out the input dialog.  It works now.

Unfortunately I'm stuck with a few async methods because I'm making websocket calls to AWS for licensing and that all requires async/await Sad


Forum Jump:


Users browsing this thread: 1 Guest(s)