WPF: How to make Calls to Dispatcher.Invoke() Synchronous? - wpf

In my MVVM based Wix Managed Bootstrapper application, while handling different events, I'm trying to show the user a view to get some input. It looks like Burn events are executing Asynchronously because using Dispatcher.Invoke(), it is skipping or passing by the view and hitting the last event, i.e not waiting for this input task to finish.
Here is the event handler which needs to finish before hitting last one:
Please note that when MessageBox.Show is popped, it waits until we close it. While debugging, I see it actually switched to MissingSourceView and loaded MissingSourceViewModel, but then while skipping it, and executes ApplyComplete();
BootstrapperApplication.ResolveSource += (sender, e) => {
System.Windows.Forms.MessageBox.Show("Inside ResolveSource");
WixBootstrapperData.CurrentDispatcher.Invoke(((Action)(() =>
{
WixBootstrapperData.CurrentViewModel = new MissingSourceViewModel(WixBootstrapperData, InfoMessage);
})));
};
BootstrapperApplication.ApplyComplete += (sender, e) =>
{
WixBootstrapperData.BootstrapperApplicationModel.FinalResult = e.Status;
WixBootstrapperData.CurrentDispatcher.Invoke((Action)(() =>
{
InfoMessage = AppResource.MsgFinished;
WixBootstrapperData.CurrentViewModel = new FinishViewModel(WixBootstrapperData, InfoMessage);
}
));
};
I guess, I should use await and async with ResolveSource(), but I face errors like:
Error CS1061 'BaseViewModel' does not contain a definition for
'GetAwaiter' and no extension method 'GetAwaiter' accepting a first
argument of type 'Task' could be found (are you missing a using
directive or an assembly reference?)
Any idea how to make it wait for finishing inside ResolveSource() and then jump to wherever it wants?

Use this, and please tell if this solves your problem.
WixBootstrapperData.CurrentDispatcher.Invoke(((Action)(() =>
{
Task.Factory.StartNew(() => {
WixBootstrapperData.CurrentViewModel = new MissingSourceViewModel(WixBootstrapperData, InfoMessage);
}).RunSynchronously();
})));

Related

Creating a new UI thread while calling methods on the original thread

I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context

async await not waiting

Tried to find something similar and read all the answers given but couldn`t find something that will explain it to me.
Here is a sample code for opening a dialog popup (WPF). I want after the ShowOverlayView turns to True, that the UI will be accessible (this is why the async-await) and the program to wait until it is finished when the user clicks "Close".
Small clarification:
ShowOverlayViewModel sets a boolean to true/false for the Visibility property of a ContentControl. Since this is the case then I have nothing to wait for "the regular way".
Currently when the view is being "visible" the MessageBox is immediately shown.
Seems like it doesn`t wait for the AutoResetEvent.
Small update: It seems to be relevant specific to the MessageBox. I tried to change the Message property after the await code line and it occurred only after the are.Set(). I would still love to know why did the MessageBox act as it did.
private void CommandAction()
{
ShowOptionsDialog();
MessageBox.Show("");
}
private async void ShowOptionsDialog()
{
var are = new AutoResetEvent(false);
var viewmodel = new DialogPopupViewModel();
viewmodel.Intialize("some title", "some message", DialogPopupViewModel.YesNoCancelButtons);
SetOverlayViewModel(viewmodel);
viewmodel.SetCloseViewAction(() =>
{
HideOverlayView();
are.Set();
});
ShowOverlayView = true;
await Task.Factory.StartNew(() =>
{
are.WaitOne();
//return viewmodel.DialogResult;
});
//return DialogResultEnum.Cancel;
}
Thanks for the help
Classic async-void bug. Research what async void does and why it's bad practice. Since ShowOptionsDialog() does not return a task that is awaited execution continues immediately. Change the return type to Task and await the result of the method call.
You can replace the event with a TaskCompletionSource<object> and say await myTcs.Task. A TCS is a more TPL-friendly event.

FromCurrentSynchronizationContext block throwing nullreferenceexception on Windows Form close

I am developing a windows form application. Following is the code segment that is executed on a button click event.
var taskExecute= Task<Datatable>.Run(() =>
{
return = GetDt(); // time consuming method
})
.ContinueWith(task=>
{
this.datagridVal.Datasource = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
this works fine. But If I start the thread and then immediately click on the cross(x) button of the form then the following line of code throws NullReferenceException (most probably because the form has been already disposed and so is the datagrid control)
this.datagridVal.Datasource = task.Result;
How can I modify the code so that this block of code does not throw any exception if the form is closed when the thread is running? Also please suggest if there is any better way to handle the scenario using
If the problem indeed comes from the fact that the form is disposed, then you could handle that with this.IsDisposed:
var taskExecute= Task<Datatable>.Run(() =>
{
return = GetDt(); // time consuming method
})
.ContinueWith(task=>
{
if (!this.IsDisposed)
{
this.datagridVal.Datasource = task.Result;
}
}, TaskScheduler.FromCurrentSynchronizationContext());

How to ensure wcf service client finishs his works in silverlight?

I use wcf service client to submit changes of data for a silverlight project. The correlative codes like this:
public class DispatcherCollection : UpdatableCollection<DocumentDispatcher>
{
public override void SubmitChanges()
{
DocumentServiceClient client = new DocumentServiceClient();
client.NewDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.UpdateColumnCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.RemoveDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
foreach (DocumentDispatcher d in this)
{
if (d.IsNew)
{
// d=>object[] data
client.NewDocumentAsync(data);
d.IsNew=false;
}
else
{
foreach (string propertyName in d.modifiedProperties)
{
client.UpdateColumnAsync(d.ID, GetPropertyValue(propertyName));
}
dd.ClearModifications();
}
}
foreach (DocumentDispatcher dd in removedItems)
{
client.RemoveDocumentAsync(dd.ID);
}
removedItems.Clear();
}
}
Class UpdatableCollection derives from ObserableCollection, and I implemtent logics in class DocumentDispatcher and UpdatableCollection to buffer the changes of data such as new created, property modified and removed. I use SubmitChanges method to submit all changes to server.
Now I am stuck:
1. I am at a loss when to close the client after a bunlde fo async calls. I don't know which callback is the last one.
2. What will happen when a user closes the IE immediately right after clicking the save button (it seems to be done because it runs async but in fact the updating threads are industriously running.)?
You can keep a counter or use an isbusy function to monitor the callbacks from your Async calls - to make sure they all finished.
If the user fires off a request to the WCF service, the WCF service will complete but there will be no call back - as the application will be closed.
I think that there is no wait handle for silverlight asynchornized call brings inconvenience. Here is my experence. I want to check and submit modifications of data which are not expicitly submitted when browser is closing. I have implemented codes in App_Exit like this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null) new ServiceClient().SubmitChangesAsync(doc);
}
provided that in the SubmitChangesAsync method, not submitted modifications of doc are found out and submitted. Therefore, because of the asynchronized running features, while the service invoking is being sent, the application is yet immediately closed. And that will dispose related resouces of the application, including Service Invoking Tasks. So the codes above work not. I hope so eagerly that somewhere exists a mechanism, which can export a wait handle from silverlight asynchronized call, so that I can update the above codes whith this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null)
{
Task t = new TaskFactory().StartNew(() => new ServiceClient().SubmitChangesAsync(doc));
t.Wait();
}
}
With wait operation I can really be sure that all modifications are really definitely submitted. So is there any similar pattern that can be used in silverlight?
It's for me a good news, as you put it, that calls could work like the mode "requesting and forgetting". So I needn' to worry too much about data losing during submitting.
To ensure all service calls are sent out before application is closed, I think, counter is a simple and effient idea. I will try to implement it in my project.
Thank you for your help!

Waiting for XAML layout to complete

I am making some changes to a page (by adding/removing controls) and I want to continue with my code only when the layout is settled (all elements measured and arranged etc).
How do I do this? Is there some Task I can await on that will fire when layout is complete?
(Right now using Yields and other tricks, but they all make me feel dirty)
You can build a Task around any event by using TaskCompletionSource<T>.
In your case, it sounds like UIElement.LayoutUpdated may be the event you want (not entirely sure about that - WPF layout details are not my strong point).
Here's an example:
public static Task LayoutUpdatedAsync(this UIElement element)
{
var tcs = new TaskCompletionSource<object>();
EventHandler handler = (s, e) =>
{
element.LayoutUpdated -= handler;
tcs.SetCompleted(null);
};
element.LayoutUpdated += handler;
return tcs.Task;
}
Then you can use this method to await the next instance of that event:
await myUiElement.LayoutUpdatedAsync();

Resources