Alternative to exiting environment while messagebox is open - c#

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

Related

Await task not working when triggered by code but if by user, it works

I have application for controlling LED strip. UI has combobox with effect selection and when user selects mode, it waits for currently running effect loop to finish by calling StopTask() and then executes selected effect. It sends LED color etc. to Arduino via serial. This works.
Problem is when I trigger StopTask() by MainWindow_OnClosing (when user exits the application), it triggers StopTask() but gets stuck on await currentEffectMode. I will try to explain it more by comments inside the code
MainWindow mode selection:
private void CbMode_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Checkbox selection triggers this
_ledStrip.LightModes.ChangeMode(CbMode.SelectedIndex);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
// Trigger disconnect and wait for success - this doesn't work (explained below in code comments)
_ledStrip.LightModes.Disconnect().Wait();
}
Light modes class:
private Task _modeTask;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
// This is being triggered by change mode
internal async void ChangeMode(int mode)
{
// It waits for current loop to finish
await StopTask();
switch (mode)
{
case (int)Modes.Static:
// Then assigns new one
_modeTask = Static(_cancellationToken.Token);
break;
case (int)Modes.Breath:
_modeTask = Breath(_cancellationToken.Token);
break;
}
}
internal async Task StopTask()
{
if (_modeTask == null)
return;
// Set cancellation token to cancel
_cancellationToken.Cancel();
try
{
// and wait for task to finish. This works if triggered by user interaction BUT this is where it gets stuck when called by Disconnect() method (below). It awaits here forever
await _modeTask;
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// After sucessful await create new cts
_cancellationToken.Dispose();
_cancellationToken = new CancellationTokenSource();
}
}
// Example of LED effect loop
internal async Task Static(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
_ledStrip.FillLedsWithColor();
// Wait for strip to light up
await LightLeds();
// Delay before next loop round
await Task.Delay(15, cancellationToken);
}
}
// This is being called by window onclosing
internal async Task Disconnect()
{
//Stop current task and close serial connection. _device is serial
await StopTask();
Application.Current.Dispatcher.Invoke(() =>
{
if (_device.IsOpen())
{
_device.Clear();
_device.Close();
}
});
}
// Method for sending LED information to Arduino
internal async Task LightLeds()
{
if (!_device.IsOpen())
return;
await Task.Run(() =>
{
for (int i = 0; i < StaticValues.NumLeds; i++)
{
_device.Send((byte)i, _ledStrip.Leds[i].LedColor.R, _ledStrip.Leds[i].LedColor.G, _ledStrip.Leds[i].LedColor.B);
}
_device.LightUp();
});
}
I am beginner with Tasks and I am pretty sure that I am not using them properly (some of them are certainly unnecessary but I don't know it) and maybe it's the reason why it's not working. I tried to search and found many examples for using Tasks but I still don't understand it well.
Thank you!
Change MainWindow_OnClosing() to be async void instead, and use await Disconnect() instead of calling .Wait(). Event handlers are almost the only async methods for which this acceptable; the rest should have an async Task[<T>] signature instead. (There are some exceptions to the async part, but not the Task part, but I won’t muddy the waters with that here). This will stop you blocking (see link in Dmytro’s comment for more).
While there, change CbMode_OnSelectionChanged() to be similar (async void), make ChangeMode() async Task, and await it instead.
The only other minor thing of note is that if you move the device-closing code into your event handler instead (or refactor it into another method that you call from the event handler, after await Disconnect()), you should not need the Invoke(), as async event handlers - done correctly - give you this for free; i.e. effectively remaining on the UI thread while not blocking. (I’m assuming that’s what you trying to achieve there?)

WPF: display a progress dialog only when its background task takes longer than a specified interval

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.

Asynchronous toggle button using a semaphore to avoid service load

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.

Using the cancellationToken without Task.Wait()

I have a winforms app with a long running task and two buttons.
One button Start and one Stop.
A new task with a cancellation Token starts when I press the start button.
And if I press the Stop button the cancellation Token's Cancel Method gets called.
I want the UI to be usable during all the time so where do I put the try, catch block for this job. In all the examples I saw they put it around t.Wait();
But if I do that the UI freezes and that is the reason why I used a Task in the first place, to continue using the ui while doing the task. So where to put the try catch block without using Task.Wait.
Start button:
tokenSource2 = new CancellationTokenSource();
ct = tokenSource2.Token;
t = new Task(doStart, ct);
t.Start();
Stop button:
tokenSource2.Cancel();
You could update doStart to handle the cancellation event and exit the task gracefully so you wouldn't need to use Wait at all e.g.
public void doStart(CancellationToken token)
{
while(...)
{
...
if (token.IsCancellationRequested)
break;
}
}
Alternatively, you could wait for the task result on another thread e.g.
Thread.QueueUserWorkItem((state) =>
{
try
{
t.Wait();
}
catch(...)
{
...
}
});

Show progress only if a background operation is long

I'm developing a C# operation and I would like to show a modal progress dialog, but only when an operation will be long (for example, more than 3 seconds). I execute my operations in a background thread.
The problem is that I don't know in advance whether the operation will be long or short.
Some software as IntelliJ has a timer aproach. If the operation takes more than x time, then show a dialog then.
What do you think that is a good pattern to implement this?
Wait the UI thread with a timer, and show dialog there?
Must I DoEvents() when I show the dialog?
Here's what I'd do:
1) Use a BackgroundWorker.
2) In before you call the method RunWorkerAsync, store the current time in a variable.
3) In the DoWork event, you'll need to call ReportProgress. In the ProgressChanged event, check to see if the time has elapsed greater than three seconds. If so, show dialog.
Here is a MSDN example for the BackgroundWorker: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Note: In general, I agree with Ramhound's comment. Just always display the progress. But if you're not using BackgroundWorker, I would start using it. It'll make your life easier.
I will go with the first choice here with some modifications:
First run the possible long running operation in different thread.
Then run a different thread to check the first one status by a wait handle with timeout to wait it for finish. if the time out triggers there show the progress bar.
Something like:
private ManualResetEvent _finishLoadingNotifier = new ManualResetEvent(false);
private const int ShowProgressTimeOut = 1000 * 3;//3 seconds
private void YourLongOperation()
{
....
_finishLoadingNotifier.Set();//after finish your work
}
private void StartProgressIfNeededThread()
{
int result = WaitHandle.WaitAny(new WaitHandle[] { _finishLoadingNotifier }, ShowProgressTimeOut);
if (result > 1)
{
//show the progress bar.
}
}
Assuming you have a DoPossiblyLongOperation(), ShowProgressDialog() and HideProgressDialog() methods, you could use the TPL to do the heavy lifting for you:
var longOperation = new Task(DoPossiblyLongOperation).ContinueWith(() => myProgressDialog.Invoke(new Action(HideProgressDialog)));
if (Task.WaitAny(longOperation, new Task(() => Thread.Sleep(3000))) == 1)
ShowProgressDialog();
I would keep the progress dialog separate from the background activity, to separate my UI logic from the rest of the application. So the sequence would be (This is essentially the same as what IntelliJ does):
UI starts the background operation (in a BackgroundWorker) and set up a timer for X seconds
When the timer expires UI shows the progress dialog (if the background task is still running)
When the background task completes the timer is cancelled and the dialog (if any) is closed
Using a timer instead of a separate thread is more resource-efficient.
Recommended non-blocking solution and no new Threads:
try
{
var t = DoLongProcessAsync();
if (await Task.WhenAny(t, Task.Delay(1000)) != t) ShowProgress();
await t;
}
finally
{
HideProgress();
}
I got the idea from Jalal Said answer. I required the need to timeout or cancel the progress display. Instead of passing an additional parameter (cancellation token handle) to the WaitAny I changed the design to depend on Task.Delay()
private const int ShowProgressTimeOut = 750;//750 ms seconds
public static void Report(CancellationTokenSource cts)
{
Task.Run(async () =>
{
await Task.Delay(ShowProgressTimeOut);
if (!cts.IsCancellationRequested)
{
// Report progress
}
});
}
Use it like so;
private async Task YourLongOperation()
{
CancellationTokenSource cts = new CancellationTokenSource();
try
{
// Long running task on background thread
await Task.Run(() => {
Report(cts);
// Do work
cts.Cancel();
});
}
catch (Exception ex) { }
finally {cts.Cancel();}
}

Categories

Resources