I have a Windows Phone client with a skinned toggle button, which is acting as a "favourite" button. The checked property is then two-way bound to the ViewModel (standard MVVM pattern).
<ToggleButton IsChecked="{Binding DataContext.IsFavouriteUser, ElementName=PageRoot, Mode=TwoWay}">
When the bound boolean is changed, I want to initiate an asynchronous network call to the service.
public bool IsFavouriteUser
{
get { return _isFavouriteUser; }
set
{
if (SetProperty(ref _isFavouriteUser, value))
{
// Dispatches the state change to a RESTful service call
// in a background thread.
SetFavouriteState();
}
}
}
If the user presses the button multiple times, then many Add / Remove asynchronous service calls could be made - Hypothetically these take 2 seconds to do the network round-trip and service processing.
In the past I have used something like:
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
// I would probably dispatch this call to a background thread in the real client
public async Task<bool> SetFavouriteState()
{
try
{
await _semaphore.WaitAsync();
bool result;
if (IsFavouriteUser)
{
result = await ServiceClient.AddAsync(x);
}
else
{
result = await ServiceClient.RemoveAsync(x);
}
return result;
}
catch
{
// I wouldn't use an empty catch in production code
return false;
}
finally
{
_semaphore.Release();
}
}
However this could endlessly queue up user input; whereas the service is only interested in the latest user event - on or off - and the UI should remain responsive to user input.
What is the best way to ensure that the client doesn't send "Add/Remove/Add/Remove" if the user repeatedly hits the button. i.e. I want to ignore the middle two events and only send "Add, wait for response to complete, Remove".
Is there a better way to bind to this boolean property in an asynchronous way?
What is the best way to lock my model so that only one request in this context is ongoing at any point?
What is the best way to inform the user that something is happening while we wait for the call to happen (and maybe fail)?
There are several good patterns to deal with async re-entrancy, i.e. what happens if a user action invokes an async method while it's already in-flight. I wrote an article with several patterns here:
http://blogs.msdn.com/b/lucian/archive/2014/03/03/async-re-entrancy-and-the-patterns-to-deal-with-it.aspx
I think your problem is a special case of pattern 5 (code below).
However, please note an oddity in your specification. It's possible that the user clicks quickly enough that you get the sequence Add followed by Add (e.g. if the intervening Remove didn't get a chance to even start executing before the second click to Add arrived). So please protect against this in your own scenario-specific way.
async Task Button1Click()
{
// Assume we're being called on UI thread... if not, the two assignments must be made atomic.
// Note: we factor out "FooHelperAsync" to avoid an await between the two assignments.
// without an intervening await.
if (FooAsyncCancellation != null ) FooAsyncCancellation.Cancel();
FooAsyncCancellation = new CancellationTokenSource ();
FooAsyncTask = FooHelperAsync(FooAsyncCancellation.Token);
await FooAsyncTask;
}
Task FooAsyncTask;
CancellationTokenSource FooAsyncCancellation;
async Task FooHelperAsync( CancellationToken cancel)
{
try { if (FooAsyncTask != null ) await FooAsyncTask; }
catch ( OperationCanceledException ) { }
cancel.ThrowIfCancellationRequested();
await FooAsync(cancel);
}
async Task FooAsync( CancellationToken cancel)
{
...
}
I would suggest disabling the ToggleButton button and showing indeterminate ProgressBar when the request is fired and hiding the ProgressBar and enabling the ToggleButton when it finishes.
Related
I am executing a potentially long running operation in the background thread of a modal dialog. The problem is that, when the operation takes a short time, the dialog is shown and closed almost instantaneously, which annoys the users.
I would like to show the dialog only if the operation takes longer than, say, 2s.
The dialog is a WPF Window and the long running operation code is in the ViewModel. The ViewModel creates a Task that runs the operation in the background.
Here is a relevant snippet:
public Task StartAction() {
var mainTask = Task.Factory.StartNew(InternalAction);
MainTask = mainTask;
mainTask.ContinueWith(_ => { IsFinished = true; });
return mainTask;
}
InternalAction is the potentially long running operation.
This is how I am trying to introduce the delay. I am using Sriram Sakthivel's suggestions from a different answer, but the code is not exactly the same:
var viewModel = ... // Creates the ViewModel
var dialogWindow = ... // Creates the Window and starts the operation by calling viewModel.StartAction();
var delayTask = Task.Delay(2000);
if (viewModel.MainTask != null) {
Task.WaitAny(delayTask, viewModel.MainTask);
}
if (viewModel.IsFinished) {
return;
}
ShowDialog(dialogWindow); // this code calls dialogWindow.ShowDialog() eventually
I am not using await because I do not want to yield control to the caller (COM) because the caller expects the result to be ready when it gets the control back.
I have been experimenting with different timeouts, e.g., 5000ms, and I do not see any difference in the behavior. The dialog windows still "blink" (are shown and closed immediately). I am sure I am doing something wrong, but I cannot understand my mistake.
You're waiting on MainTask, but MainTask isn't the task that sets IsFinished. You may be returning from WaitAny after InternalAction completes but before the IsFinished = true continuation completes.
Try setting MainTask to the continuation rather than its antecedent:
public Task StartAction() {
var mainTask = Task.Factory.StartNew(InternalAction);
var continuation = mainTask.ContinueWith(_ => { IsFinished = true; });
MainTask = continuation;
return mainTask;
}
Note that continuation cannot begin until mainTask has completed, so with this change you'll be waiting on mainTask and continuation.
Note, however, that if IsFinished is being read from the UI thread, you'll want to also set it from the UI thread. That, or make it backed by a volatile field.
There used to be a 3rd party Library called "Busy Indicator". Maybe you could enable it to only appear if the busy condition is met for a certain time? (https://github.com/xceedsoftware/wpftoolkit/wiki/Xceed-Toolkit-Plus-for-WPF).
Basically it comes down to the ViewModel exposing a "busy" property (or any property that can be converted into a boolean value representing "busy"). And the View reacting to the change on a delay (if any).
I am not sure if XAML itself can do that, as you need to show a window. A bit of code behind might be nesseary here. How about you register a custom ChangeNotification handler that starts a timer, with the timer re-checking if the condition is still met in the "tick" event?
Here is some code, made largely from memory:
//custom ChangeNofiticationHander
busyChangeHanlder(object sender, PropertyChangedEventArgs e){
if(e.PropertyName == "BusyBoolean"){
if(BusyBoolean)
//Start the timer
else
//Stop the timer
}
}
timerTickHandler(object sender, TimerTickEventArgs e){
if(BusyBoolean){
//create and Dispaly the Dialog here
}
}
var mainTask = Task.Delay(5000); // your long running task
if(Task.WaitAny(mainTask, Task.Delay(2000)) == 1){ // if the delay enden first, show dialog
showDialog();
await mainTask;
closeDialog();
}
await mainTask; // this will just skip, if mainTask is already done
Try this approach - it will only show dialog window, if the operation takes longer that 2s. You can also wrap all that in another task, then the caller can await the whole thing with no difference whether the dialog was shown or not.
Throughout this question, I've included some links which show that I've done some work searching for a solution.
I'm developing a UWP app with touchscreen and GPIO.
UI has a stop button, a reset button, and a textblock. GPIO is used for a physical start button, a motor, and 2 limit switches. Motor can rotate until it runs into a limit switch.
Code to control the hardware (e.g., Motor.Forward()) has been written and tested, but is excluded from this question for brevity. Code for the stop button is excluded for the same reason.
If the steps in these methods would perform synchronously... desired behavior might be described by the following code:
//Event handler for when physical start button is pushed
private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
Start();
}
private void Start()
{
//Update UI
stopButton.IsEnabled = true;
resetButton.IsEnabled = false;
textBlock.Text = "Motor turning";
Motor.Forward();
while(Motor.HasNotHitEndLimit())
Motor.Off();
//Update UI
stopButton.IsEnabled = false;
resetButton.IsEnabled = true;
textBlock.Text = "Task complete";
}
//Event handler for reset button
private void btnReset_Click()
{
//Update UI
stopButton.IsEnabled = true;
resetButton.IsEnabled = false;
textBlock.Text = "Motor turning";
Motor.Back();
while(Motor.HasNotHitStartLimit())
Motor.Off();
//Update UI
stopButton.IsEnabled = false;
resetButton.IsEnabled = true;
textBlock.Text = "Reset complete";
}
If I recall correctly, UI updates within "private void btnReset_Click()" work, but they are not synchronous... I.e., all of the UI updates were completing right after "btnReset_Click()" finished.
From reading answers to similar questions... it seems that UI updates within "Start()" fail because I'm not on the UI thread ("The application called an interface that was marshalled for a different thread.").
It seems that Task Asynchronous Pattern is a common answer to these types of questions. However, my attempts to do this have yielded strange results...
The code below is the closest I've come to the desired result. I added async tasks that use CoreDispatcher to handle UI updates.
//Task for updating the textblock in the UI
private async Task UpdateText(string updateText)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { textBlock.Text = updateText; }));
}
//Task for enable/disable a given button
private async Task UpdateButton(Button btn, bool shouldThisBeEnabled)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { btn.IsEnabled = shouldThisBeEnabled; }));
}
//Event handler for when physical start button is pushed
private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
Start();
}
private void Start()
{
//Update UI
UpdateButton(stopButton,true).Wait();
UpdateButton(resetButton,false).Wait();
UpdateText("Motor turning").Wait();
Motor.Forward();
while(Motor.HasNotHitEndLimit())
Task.Delay(1).Wait();
Motor.Off();
//Update UI
UpdateButton(stopButton,false).Wait();
UpdateButton(resetButton,true).Wait();
UpdateText("Task complete").Wait();
}
//Event handler for reset button
private async void btnReset_Click()
{
//Update UI
await UpdateButton(stopButton,true);
await UpdateButton(resetButton,false);
await UpdateText("Motor turning");
await Task.Delay(1);
Motor.Back();
while(Motor.HasNotHitStartLimit())
await Task.Delay(1);
Motor.Off();
//Update UI
await UpdateButton(stopButton,false);
await UpdateButton(resetButton,true);
await UpdateText("Reset complete");
}
Problems/idiosyncrasies with the code above (besides any beginner mistakes I might be making due to just starting out with C#... and the fact that it seems overly complicated and confusing):
-In "Start()" I use .Wait() on the tasks (because it seems to work, I don't really understand why...), and in btnReset_Click() it worked best to await them...
-btnReset_Click() is not synchronous. UI updates appear to be "one step behind"... I.e., in debug mode, the stop button enables when I step over "await UpdateButton(resetButton,false)", reset button disables when I step over "await UpdateText("Motor turning")", and so on.
-Regarding btnReset_Click()... The while loop lasts MUCH longer than 1 millisecond in real time, yet if I remove all "await Task.Delay(1)" then the UI updates are "one step behind". With "await Task.Delay(1)" included, the UI updates get "caught up" to where they should be. Why does "await Task.Delay(1)" affect UI updates this way?
If any knowledgeable folks are willing to address some/all of this question and maybe let me prod them for details about their answer(s), I'd be very grateful!
Bonus question.... I also have a "Toggle Test Mode" button on the touchscreen which enables one list of UI buttons and disables another (based on a static bool "testmode"). I don't need to use TAP to update the UI here, but recall that I want to do this synchronously (even though it seems pointless in this example).
private async void btnTestMode_Click(object sender, RoutedEventArgs e)
{
testMode = !testMode;
if (testMode == true)
{
await UpdateButtons(TestModeButtons,true);
await UpdateButtons(NormalModeButtons,false);
return;
}
await UpdateButtons(TestModeButtons,true);
await UpdateButtons(NormalModeButtons,false);
}
private async Task UpdateButtons(List<Button> btns, enable)
{
foreach (var btn in btns)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { btn.IsEnabled = enable; }));
}
}
As it's written above, this behaves like btnReset_Click()... where the UI updates are "one step behind". However, if I add ".ConfigureAwait(false)" to each await in the event handler, then it becomes synchronous. I've done some reading on this topic, but don't fully understand it yet, and I would love for someone with a better understanding to help me understand it as it relates to my project.
You should not be doing any of that Dispatcher.Run and similar...
First stop and think and understand what your problem is and why the UI does not update.
Create a new thread where you control your motors (separate from the UI thread).
On the button clicks, call method on your motors thread.
When there are events on the motors thread where you need to update the UI, call (synchronously?) methods on the UI thread.
In a nutshell, consider these tips as you build your app:
Never call .Wait() or Result or otherwise try to block on an asynchronous operation on a UI dispatcher thread
If you do want to create worker threads to do blocking operations, try await Task.Run(...) for its simplicity (you can create raw threads but it's more work). Same for busy-waits like while(notDone) ; // empty
From a background thread (such as one created by Task.Run), if you want to update the UI then you would use Dispatcher.RunAsync(...) but only to set the properties of your UI
To disable your UI while a background thread does work, set IsEnabled=false or add a top-most emi-transparent interaction shield etc.
Also, try starting with something simple (eg, no hardware access; just use Task.Delay(...).Wait() to simulate blocking on the hardware). Once you have the UI basically working you can plug in the hardware calls.
Example:
Once I display a messagebox, I'd like to call into a function to automatically exit the environment AFTER 5 seconds of the messagebox being displayed.
Is there a better approach towards doing so other than using a timer (starting the timer before the messagebox is displayed)?
Thanks
Using a timer is definitely a valid solution in this case, but you can also leverage Tasks and async/await in order to gain more control over the execution flow. Here's a Task-based implementation which calls Environment.Exit(0) after the predefined time interval, or immediately after the user dismisses the message box:
static void ShowMessageBoxAndExit(string text, TimeSpan exitAfter)
{
using (CancellationTokenSource cts = new CancellationTokenSource())
{
Func<Task> exitTaskFactory = async () =>
{
try
{
await Task.Delay(exitAfter, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Expected if the user dismisses the
// message box before the wait is completed.
}
finally
{
Environment.Exit(0);
}
};
// Start the task.
Task exitTask = exitTaskFactory();
MessageBox.Show(text);
// Cancel the wait if the user dismisses the
// message box before our delay time elapses.
cts.Cancel();
// We don't want the user to be able to perform any more UI work,
// so we'll deliberately block the current thread until our exitTask
// completes. This also propagates task exceptions (if any).
exitTask.GetAwaiter().GetResult();
}
}
I have a ViewModel, which makes use of Commanding. In this ViewModel I want to access the MediaPlugin
. With this plugin I have to call Initialize(). Because it uses async calls I have some timing problems.
This is my code:
public ICommand CameraCommand
{
get { return _cameraCommand ?? (_cameraCommand = new Command(async () => await ExecuteCameraCommand(), () => CanExecuteCameraCommand())); }
}
public bool CanExecuteCameraCommand()
{
// Check if initialized before calling properties
if (!this.initialized)
InitMedia();
if (!this.initialized || !CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
return false;
}
return true;
}
public async Task ExecuteCameraCommand()
{
// Assure that it is initialized before calling method
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions {});
// ...
}
private void InitMedia()
{
CrossMedia.Current.Initialize();
this.initialized = true;
}
With this code the app crashes with
You must call Initialize() before calling any properties.
at Plugin.Media.MediaImplementation.get_IsCameraAvailable()
When I start the initialization in the constructor like with this code
public MyViewModel()
{
InitData();
}
private async Task InitData()
{
// ...
await InitMedia();
}
private async Task InitMedia()
{
await CrossMedia.Current.Initialize();
this.initialized = true;
}
public bool CanExecuteCameraCommand()
{
// Check if initialized before calling properties
if (!this.initialized)
return false;
// ...
}
CanExecuteCameraCommand() is called before the initialization has finished. As a consequence false is returned and the button in the UI is disabled.
I'm testing this code in a Xamarin.Forms environment on a Windows 10 Mobile device (Windows 10 Universal).
You have not provide a Minimal, Complete, and Verifiable code example, and in particular you have not shown your implementation of the Command class. So I'm going to assume here that it's the Command class described in the Xamarin article you referenced.
The basic strategy for asynchronous initialization of your view model is this:
Disable any commands that depend on the result of the initialization, clear any properties (or set to appropriate default value) that depend on the result of the initialization.
Begin the asynchronous initialization.
Use some continuation mechanism (i.e. don't block the thread) to wait for the asynchronous initialization to complete.
When the initialization completes, re-enable previously disabled commands and update any relevant properties.
Based on the code you posted, it appears to me that the main thing missing is step #4. I.e. you're not doing anything to re-enable the previous disabled commands, even though you have a convenient place to do so:
private async Task InitMedia()
{
await CrossMedia.Current.Initialize();
this.initialized = true;
_cameraCommand.ChangeCanExecute();
}
The Command.ChangeCanExecute() method raises the CanExecuteChanged event, so that controls bound to the command can be notified when the result of the CanExecute() method will be different. By calling this method when initialization is done, this should address the problem of the button remaining disabled even after initialization has completed.
One additional note: I would not call the InitMedia() method, or do any initialization at all, from the CanExecuteCameraCommand() method. Because initialization is asynchronous, it's not like you'll be able to successfully initialize before returning from that method anyway, and you've already got a call to the initialization from the constructor. The ICommand.CanExecute() implementation should be very simple, and strictly limited to checking the current state of things and returning the bool value required according to those results.
If the above does not address your question, please improve the question by providing a good MCVE, and explain in more precise details what is going wrong, what you've tried to fix it, and what specifically you are having trouble figuring out.
I have a WebBrowser control and it has InvokeScript method, which you should call only after WebBrowser is loaded.
So I've tried something like this:
private readonly ManualResetEventSlim browserLoaded = new ManualResetEventSlim(false);
private void BrowserLoaded(object sender, NavigationEventArgs navigationEventArgs)
{
browserLoaded.Set();
}
private async Task<object> InvokeScript(string invoke, object[] parameters = null)
{
return await Task.Factory
.StartNew(() =>
{
if (!browserLoaded.Wait(TimeSpan.FromSeconds(10)))
{
throw new Exception("Timeout for waiting browser to load.");
}
})
.ContinueWith(task => parameters == null
? browser.InvokeScript(invoke)
: browser.InvokeScript(invoke, parameters), TaskScheduler.FromCurrentSynchronizationContext());
}
It does not look very nice to me, but works ok when called asynchronously.
Problem appears, when I try to read result value synchronously - app just hangs:
private string GetEnteredText()
{
return (string)InvokeScript("getEnteredText").Result;
}
I know, that I should go all the way async, but I'm wondering what to do with properties:
public override string UserText
{
get
{
return GetEnteredText();
}
set
{
SetEnteredText(value);
}
}
Or async is wrong way to go in this case at all?
Update
Property is a 'glue' between input field value in browser's page and view model in WPF, so I don't see a good way to make it as separate methods, especially because it is a part of the bigger framework (notice override keyword on it).
Once browser control is loaded, execute logic should not take long, I guess less than 10 milliseconds, that is why I would be ok with sync execution in this case. And usually browser control loads fast enough, the only reason here to delay is to make sure InvokeScript is not called before load, not because it taking long time or smth.
app just hangs
We'll, you said it yourself. You know why that happens. You're blocking on the call using Task.Result, that is what causes the deadlock.
Or async is wrong way to go in this case at all?
We don't have async properties. The reason we don't have them are because properties aren't asynchronous by nature. Stephan Cleary describes it nicely:
If your “property” needs to be asynchronously evaluated every time
it’s accessed, then you’re really talking about an asynchronous
operation.The best solution is to change the property to an async
method. Semantically, it shouldn’t be a property.
Instead of a property, make it an asynchronous method which you can await properly.
In regards to using a ManualResetEvent, I would use a TaskCompletionSource<bool> instead. It works "nicer" with TPL:
private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
private void BrowserLoaded(object sender, NavigationEventArgs navigationEventArgs)
{
tcs.TrySetResult(true);
}
private async Task<object> InvokeScript(string invoke, object[] parameters = null)
{
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(10));
if (timeoutTask == await Task.WhenAny(tcs.Task, timeoutTask))
{
// You've timed out;
}
return Task.Run(() =>
{
parameters == null ? browser.InvokeScript(invoke)
: browser.InvokeScript(invoke, parameters)
});
}
I also see that you used TaskScheduler.FromCurrentSynchronizationContext() in your continuation. If you need this to execute on the UI thread, there is no point in using a threadpool thread at all.
#Noseratio adds that WebBrowser is an STA object. His answer here might help.