the Form.ShowDialog() method causes the code to be halted until the newly called form is closed. I need the code to continue running after the ShowDialog() method is called. I googled and read about using backgroundworker? But that is the first time i have heard of that and never used it before.
Form2 form2this = new Form2();
form2this.ShowDialog();
MessageBox.Show("Something");
This code gets executed after clicking a button, how can i still call ShowDialog to prevent the user from interacting with the main form but still allowing the main form to continue with its work?
Sorry if its been discussed but everything i found seems extremely difficult to perform such a simple task. I am actually surprised its not included in the SHowDialog method. for instance ShowDialog().Continue would be cool.
If you just want the code to continue on instead of blocking until the popup is closed consider using Show instead of ShowDialog.
If you have some action that you want to have the parent form doing while the child form is up, then yes, it could be appropriate to use a BackgroundWorker (or just manually starting a new Thread/Task). It would be helpful to know more about what that task is though. If you need to interact with the main form, or the child form, then that seems like trouble to me; if you just need to do some background task with no UI interaction then this is the right line of thought.
Another possibility is that what you want to do really just should be something done in the child form, rather than the parent form.
As long as you do asynchronous operations during the time that the modal dialog is opened, you can do it as simply as shown below, assuming button1_Click() is the event handler for a button.
private async void button1_Click(object sender, EventArgs e)
{
// create and display modal form
Form2 modalForm = new Form2();
BeginInvoke((Action)(() => modalForm.ShowDialog()));
// do your async background operation
await DoSomethingAsync();
// close the modal form
modalForm.Close();
}
private async Task DoSomethingAsync()
{
// example of some async operation....could be anything
await Task.Delay(10000);
}
I found that when I used the solution that suggested to use Show(), I could end up in cases where the dialog I wanted to be modal would end up behind the main form, after switching back and forth between apps. That never happens when I use the solution above.
This is my way, so ugly but i have no better idea.
private void AppUiMain_Shown(object sender, EventArgs e)
{
var loading = new AppUiLoading();
loading.Shown += (o, args) =>
{
bool isLoading = true;
loading.Top = (int)(loading.Top * 1.16);
Application.DoEvents();//refresh ui
EventHandler ehr = null;
EventHandler ehe = null;
ehr = (ss, ee) =>
{
App.Instance.Ready -= ehr;
App.Instance.Error -= ehe;
isLoading = false;
};
ehe = (ss, ee) =>
{
loading.Text = "Error";
loading.ShowAbortButton("Error occur");
};
App.Instance.Error += ehe;
App.Instance.Ready += ehr;
InitApp();
//HACK: find a better way to `refresh' main form
Application.DoEvents();
this.Height++;
this.Height--;
//HACK: find a better way to keep message looping on ShowDialog
while (isLoading)
Application.DoEvents();
loading.Close();
};
loading.ShowDialog(this);
}
To continue code execution without closing modal dialog WindowsFormsSynchronizationContext.Current.Post(-=> {"Your code"}, null); can be used. Here you can find more detail -
http://newapputil.blogspot.in/2015/05/continue-executing-code-after-calling.html
Run an async call to show modal. Here an example in wpf:
private Window waitView;
/// <summary>
/// Closes a displayed WaitView from code.
/// </summary>
public void CloseWaitView()
{
if(waitView != null)
{
// Work on the gui Thread of waitView.
waitView.Dispatcher.Invoke(new Action(() => close()));
}
}
/// <summary>
/// Closes a displayed WaitView and releases waitView-Instance.
/// </summary>
private void close()
{
waitView.Close();
waitView = null;
}
/// <summary>
/// Showes a modal WaitView (Window).
/// </summary>
public void ShowWaitView()
{
// instance a new WaitViewWindow --> your Window extends Window-Class
waitView = new WaitViewWindow();
// prepare a operation to call it async --> your ShowDialog-call
var asyncCall = new Action(() => waitView.Dispatcher.Invoke(
new Action(() => waitView.ShowDialog())
));
// call the operation async
// Argument 1 ar:
// ar means IAsyncResult (what should be done, when come back from ShowDialog -->
// remove view memory with set waitView to null or ... dispose
// the second argument is an custom parameter you can set to use in ar.AsyncState
asyncCall.BeginInvoke(ar => waitView = null, null);
// all from here is done during ShowDialog ...
}
I suppose next solution for async ShowDialog:
public bool DialogResultAsync
{
get;
private set;
}
public async Task<bool> ShowDialogAsync()
{
var cts = new CancellationTokenSource();
// Attach token cancellation on form closing.
Closed += (object sender, EventArgs e) =>
{
cts.Cancel();
};
Show(); // Show message without GUI freezing.
try
{
// await for user button click.
await Task.Delay(Timeout.Infinite, cts.Token);
}
catch (TaskCanceledException)
{ }
}
public void ButtonOkClick()
{
DialogResultAsync = true;
Close();
}
public void ButtonCancelClick()
{
DialogResultAsync = false;
Close();
}
And in main form you must use this code:
public async void ShowDialogAsyncSample()
{
var msg = new Message();
if (await msg.ShowDialogAsync())
{
// Now you can use DialogResultAsync as you need.
System.Diagnostics.Debug.Write(msg.DialogResultAsync);
}
}
Related
I have an application that connects to a REST API using async methods. I have this set up using async/await pretty much everywhere that connects to the API, however I have a question and some strange behavior that I don't completely understand. What I want to do is simply return a license in certain scenarios when the program shuts down. This is initiated by a window closing event; the event handler is as follows:
async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
...other synchronous code...
//Check for floating licensing
if (KMApplication.License != null && KMApplication.License.Scope != Enums.LicenseScope.Standalone)
{
for (int i = 0; i < 3; i++)
{
try
{
await KMApplication.License.ShutDown(KMApplication.Settings == null
? Enums.LicenseReturnModes.PromptOnShutdown
: KMApplication.Settings.LicenseReturnMode)
.ConfigureAwait(false);
break;
}
catch (Exception ex)
{
_logger.Warn("Exception in license release, attempt " + i, ex);
}
}
}
await KMApplication.ApiService.Disconnect().ConfigureAwait(false);
_logger.Info("Shutdown Complete");
Application.Current?.Shutdown();
}
When this runs I can step through in the debugger and it gets to the first license shutdown call which is the first async awaited call. Then when I press F10 to step to the next line of code it just shuts down and is gone. I verified that the license release that is supposed to be happening in that line is in face happening so it appears to run to completion of that line but then shuts down or crashes or something. I also looked at the logs and it never gets to the Shutdown Complete line and I don't believe it's getting to the ApiService.Disconnect either.
I also tried running this as a sync method using Task.Run(() => ...the method...).GetAwaiter().GetResult() but that just deadlocks on the first call.
How do I handle this and have it run the async release, wait for it to be done, then shut down?
The fundamental problem in what you're trying to do is that async/await assumes the main application thread continues running. This assumption directly conflicts with the shutdown action, whose job is to terminate all running tasks.
If you examine the documentation on Window_Closing, it states the following (and only the following):
Occurs directly after Close() is called, and can be handled to cancel window closure.
This is important. The only thing this is supposed to do is allow you to programmatically cancel the window closure, thus prompting some additional user action.
Your expectations are befuddled because of how async/await works. Async/await appears to run in a linear fashion; however, what actually happens is that control is passed back to the caller at the first await. The framework assumes at that point that you do not wish to cancel the form close, and the program is allowed to terminate, taking all other actions with it.
Fundamentally, all C-style programs have a main entry point, which runs a loop. It's been that way since the early days of C, and continues that way in WPF. However, in WPF, Microsoft got a bit clever, and decided to hide this from the programmer. There are a couple of options to deal with things that need to happen after main window closing:
Re-hijack the main loop from your program, and put the code there. The details on how to do this may be found here.
Set an explicit shutdown mode, and kick off the task to initiate that. Call Application.Shutdown() as the very last line of code you need to execute.
Here is an async version of the FormClosing event. It delays the closing of the form until the completion of the supplied Task. The user is prevented from closing the form before the completion of the task.
The OnFormClosingAsync event passes an enhanced version of the FormClosingEventArgs class to the handling code, with two additional properties: bool HideForm and int Timeout. These properties are read/write, much like the existing Cancel property. Setting HideForm to true has the effect of hiding the form while the async operation is in progress, to avoid frustrating the user. Setting Timeout to a value > 0 has the effect of abandoning the async operation after the specified duration in msec, and closing the form. Otherwise it is possible that the application could be left running indefinitely with a hidden UI, which could certainly be a problem. The Cancel property is still usable, and can be set to true by the handler of the event, to prevent the form from closing.
static class WindowsFormsAsyncExtensions
{
public static IDisposable OnFormClosingAsync(this Form form,
Func<object, FormClosingAsyncEventArgs, Task> handler)
{
Task compositeTask = null;
form.FormClosing += OnFormClosing; // Subscribe to the event
return new Disposer(() => form.FormClosing -= OnFormClosing);
async void OnFormClosing(object sender, FormClosingEventArgs e)
{
if (compositeTask != null)
{
// Prevent the form from closing before the task is completed
if (!compositeTask.IsCompleted) { e.Cancel = true; return; }
// In case of success allow the form to close
if (compositeTask.Status == TaskStatus.RanToCompletion) return;
// Otherwise retry calling the handler
}
e.Cancel = true; // Cancel the normal closing of the form
var asyncArgs = new FormClosingAsyncEventArgs(e.CloseReason);
var handlerTask = await Task.Factory.StartNew(
() => handler(sender, asyncArgs),
CancellationToken.None, TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default); // Start in a thread-pool thread
var hideForm = asyncArgs.HideForm;
var timeout = asyncArgs.Timeout;
if (hideForm) form.Visible = false;
compositeTask = Task.WhenAny(handlerTask, Task.Delay(timeout)).Unwrap();
try
{
await compositeTask; // Await and then continue in the UI thread
}
catch (OperationCanceledException) // Treat this as Cancel = true
{
if (hideForm) form.Visible = true;
return;
}
catch // On error don't leave the form hidden
{
if (hideForm) form.Visible = true;
throw;
}
if (asyncArgs.Cancel) // The caller requested to cancel the form close
{
compositeTask = null; // Forget the completed task
if (hideForm) form.Visible = true;
return;
}
await Task.Yield(); // Ensure that form.Close will run asynchronously
form.Close(); // Finally close the form
}
}
private struct Disposer : IDisposable
{
private readonly Action _action;
public Disposer(Action disposeAction) => _action = disposeAction;
void IDisposable.Dispose() => _action?.Invoke();
}
}
public class FormClosingAsyncEventArgs : EventArgs
{
public CloseReason CloseReason { get; }
private volatile bool _cancel;
public bool Cancel { get => _cancel; set => _cancel = value; }
private volatile bool _hideForm;
public bool HideForm { get => _hideForm; set => _hideForm = value; }
private volatile int _timeout;
public int Timeout { get => _timeout; set => _timeout = value; }
public FormClosingAsyncEventArgs(CloseReason closeReason) : base()
{
this.CloseReason = closeReason;
this.Timeout = System.Threading.Timeout.Infinite;
}
}
Since OnFormClosingAsync is an extension method and not a real event, it can only have a single handler.
Usage example:
public Form1()
{
InitializeComponent();
this.OnFormClosingAsync(Window_FormClosingAsync);
}
async Task Window_FormClosingAsync(object sender, FormClosingAsyncEventArgs e)
{
e.HideForm = true; // Optional
e.Timeout = 5000; // Optional
await KMApplication.License.ShutDown();
//e.Cancel = true; // Optional
}
The Window_FormClosingAsync handler will run in a thread-pool thread, so it should not include any UI manipulation code.
Unsubscribing from the event is possible, by keeping a reference of the IDisposable return value, and disposing it.
Update: After reading this answer, I realized that it is possible to add a real event FormClosingAsync in the form, without creating a class that inherits from the form. This can be achieved by adding the event, and then running an initialization method that hooks the event to the native FormClosing event. Something like this:
public event Func<object, FormClosingAsyncEventArgs, Task> FormClosingAsync;
public Form1()
{
InitializeComponent();
this.InitFormClosingAsync(() => FormClosingAsync);
this.FormClosingAsync += Window_FormClosingAsync_A;
this.FormClosingAsync += Window_FormClosingAsync_B;
}
Inside the initializer, in the internal handler of the native FormClosing event, all the subscribers of the event can be retrieved
using the GetInvocationList method:
var eventDelegate = handlerGetter();
if (eventDelegate == null) return;
var invocationList = eventDelegate.GetInvocationList()
.Cast<Func<object, FormClosingAsyncEventArgs, Task>>().ToArray();
...and then invoked appropriately. All this adds complexity, while the usefulness of allowing multiple handlers is debated. So I would probably stick with the original single-handler design.
Update: It is still possible to have multiple handlers using the original method OnFormClosingAsync. It is quite easy actually. The Func<T>
class inherits from Delegate, so it has invocation list like a real event:
Func<object, FormClosingAsyncEventArgs, Task> aggregator = null;
aggregator += Window_FormClosingAsync_A;
aggregator += Window_FormClosingAsync_B;
this.OnFormClosingAsync(aggregator);
No modification in the OnFormClosingAsync method is required.
Ok here is what I ended up doing. Basically the window closing kicks off a task that will wait for the release to happen and then invoke the shutdown. This is what I was trying to do before but it didn't seem to work in async void method but it seems to be when done this way. Here is the new handler:
void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
...other sync code...
Task.Run(async () =>
{
await InvokeKmShutdown();
(Dispatcher ?? Dispatcher.CurrentDispatcher).InvokeShutdown();
});
}
And the shutdown method looks like this:
async Task InvokeKmShutdown()
{
...other sync code...
await KMApplication.ApiService.Disconnect();
//Check for floating licensing
if (KMApplication.License != null && KMApplication.License.Scope != License.Core.Enums.LicenseScope.Standalone)
{
for (int i = 0; i < 3; i++)
{
try
{
await KMApplication.License.ShutDown(KMApplication.Settings == null
? Enums.LicenseReturnModes.PromptOnShutdown
: KMApplication.Settings.LicenseReturnMode);
break;
}
catch (Exception ex)
{
_logger.Warn("Exception in license release, attempt " + i, ex);
}
}
}
}
Hope it helps someone.
EDIT
Note that this is with an WPF app set to ShutdownMode="OnExplicitShutdown" in App.xaml so it won't shut down the actual app until I call the shutdown. If you are using WinForms or WPF is set to shut down on last window or main window close (main window close is the default I believe) you will end up with the race condition described in the comments below and may get the threads shut down before things run to completion.
In a multithreading environment.
I'm using ShowDialog in a progress form to block all user activities on other forms.
After finish part of the process I hide the progress form, I do some tasks and I show (ShowDialog) it again.
Everything's works fine but the progress form flickers.
Is there a way to continue from a ShowDialog without hiding the window or, probably better, is there a way to transform a Show to a ShowDialog and back again?
EDIT
My code is similar to this
class frmProgress : Form
{
// Standard stuff
public void DoSomeWorks()
{
async Task.Run(() => RunWork1());
ShowDialog();
if (_iLikeTheResultOfWork1)
{
async Task.Run(() => RunWork2());
ShowDialog();
}
}
void RunWork1()
{
// Do a lot of things including update UI
Hide();
}
void RunWork2()
{
// Do a lot of things including update UI
Hide();
}
}
EDIT 2
Thanks to everyone for the answers and the suggestions.
At the end I adopted the solution to Group some small works in one bigger work that I use in UI while in unit tests I still test the small works.
Actually it was not the solution I was looking for, I hoped to find a solution based on Handling the message pump or doing something similar (see System.Windows.Forms.Application source code starting from RunDialog(.) and all the called methods where I got lost before posting this question).
You can't do that. At least there is not an easy way that I am aware of. One way to tackle that is to change your code as follows:
//C#-ish pseudocode based on OPs example; doesn't compile
class frmProgress : Form
{
// Standard stuff
public void DoSomeWorks()
{
async Task.Run(() => RunWork1());
ShowDialog();
}
void RunWork1()
{
// Do a lot of things including update UI
if (_iLikeTheResultOfWork1)
{
async Task.Run(() => RunWork2());
}
else
{
Hide();
}
}
void RunWork2()
{
// Do a lot of things including update UI
Hide();
}
}
EDIT For those complaining the code doesn't compile, they are right. But this is the best the OP will get with that code sample and that's the reason I guess he is mercilessly downvoted.
But to make the answer more relevant to others, my suggestion is don't hide the progress form between the the 2 tasks, hide it when you are sure that the tasks have ended. All these by respecting the threading model of the context you are working on, something the OP's code doesn't do. Manipulating the UI in Winforms from methods that will eventually run in other threads won't work.
I suppose you have some methods like following:
void Task1()
{
Debug.WriteLine("Task1 Started");
System.Threading.Thread.Sleep(5000);
Debug.WriteLine("Task1 Finished");
}
void Task2()
{
Debug.WriteLine("Task2 Started");
System.Threading.Thread.Sleep(3000);
Debug.WriteLine("Task2 Finished");
}
If you are going to run them in parallel:
private async void button1_Click(object sender, EventArgs e)
{
this.Enabled = false;
//Show a loading image
await Task.WhenAll(new Task[] {
Task.Run(()=>Task1()),
Task.Run(()=>Task2()),
});
//Hide the loading image
this.Enabled = true;
}
If you are going to run them one by one:
private async void button1_Click(object sender, EventArgs e)
{
this.Enabled = false;
//Show a loading image
await Task.Run(()=>Task1());
await Task.Run(()=>Task2());
//Hide the loading image
this.Enabled = true;
}
Referencing Async/Await - Best Practices in Asynchronous Programming
I advise you take advantage of async event handlers and tasks to allow for non blocking calls while the form is in modal via show dialog
class frmProgress : Windows.Form {
// Standard stuff
public void DoSomeWorks() {
Work -= OnWork;
Work += OnWork;
Work(this, EventArgs.Empty);//raise the event and do work on other thread
ShowDialog();
}
private CancellationTokenSource cancelSource;
private event EventHandler Work = delegate { };
private async void OnWork(object sender, EventArgs e) {
Work -= OnWork; //unsubscribe
cancelSource = new CancellationTokenSource(); //to allow cancellation.
var _iLikeTheResultOfWork1 = await RunWork1Async(cancelSource.Token);
if (_iLikeTheResultOfWork1) {
await RunWork2Async(cancelSource.Token);
}
DialogResult = DialogResult.OK; //just an example
}
Task<bool> RunWork1Async(CancellationToken cancelToken) {
// Do a lot of things including update UI
//if while working cancel called
if (cancelToken.IsCancellationRequested) {
return Task.FromResult(false);
}
//Do more things
//return true if successful
return Task.FromResult(true);
}
Task<bool> RunWork2Async(CancellationToken cancelToken) {
// Do a lot of things including update UI
//if while working cancel called
if (cancelToken.IsCancellationRequested) {
return Task.FromResult(false);
}
//Do more things
//return true if successful
return Task.FromResult(true);
}
}
Note the use of cancellation token to allow tasks to be cancelled as needed.
The UI is now unblocked and the async functions can continue the work. No flickering as the form remain shown without any interruptions.
I could be missunderstood, here what I did, handleDialog with wndProc. I created an extra form and used Show(); method. After I showed form, async tasks are started. After I showed the form, I handled that form by wndProc.
protected override void WndProc(ref Message m) {
if((f2.IsDisposed || !f2.Visible)) {
foreach(var control in controlList) {
control.Enabled = true; // enable all controls or other logic
}
}
if(m.Msg == 0x18 && !f2.IsDisposed) { // notify dialog opens and double checks dialog's situation
foreach(var control in controlList.Where(ctrl => ctrl.Name != "button1")) {
control.Enabled = false; // disable except cancel button
}
}
base.WndProc(ref m);
}
Hope helps,
From the code you posted, you are calling Hide() at the end of your RunWork() methods, and then ShowDialog() right afterwards. If I understand correctly, you want to call Hide() first inside your RunWork() methods, which makes the main UI window accessable while the UI updates are occurring. After everything finishes, then the ShowDialog() method occurs and blocks the main UI thread again.
class frmProgress : Form
{
public bool _iLikeTheResultOfWork1 = true;
// Note: changed to async method and now awaiting the task
public async void DoSomeWorks()
{
await Task.Run(() => RunWork1());
ShowDialog();
if (_iLikeTheResultOfWork1)
{
await Task.Run(() => RunWork2());
ShowDialog();
}
}
// Hide window and wait 5 seconds. Note that the main window is active during this 5 seconds.
// ShowDialog() is called again right after this, so the dialog becomes modal again.
void RunWork1()
{
Hide();
Task.Delay(5000).Wait();
// Do a lot of things including update UI
}
void RunWork2()
{
Hide();
Task.Delay(5000).Wait();
// Do a lot of things including update UI
}
}
My code above will call RunWork1(), which hides the dialog for 5 seconds. During this time the main UI window will be active. Afterwards, the method returns and ShowDialog() is called. Close that window to call RunWork2() and start the process over again.
Is this what you are attempting to achieve? I just tested this code and it works with no visual "flickers". If this is what you want, does the "flickering" occur with this method?
"the form will always be closed when it leaves its modal message loop, the one that was started by ShowDialog(). The best you can do is simulate modality, display the form with Show() and disable all other windows."
https://social.msdn.microsoft.com/Forums/sharepoint/en-US/3f6c57a1-92fd-49c5-9f46-9454df80788c/possible-to-change-modality-of-a-dialog-box-at-runtime?forum=winforms
So you could enumerate all your app's Windows and enable/disable them when needed, plus set your dialog window to be always on top when simulating modality. I think always on top means it comes above all windows in the OS though, so you may need a timer instead to bring your window to the front (among your app windows) if it is set to simulate modality
I am using the following code to apply a delay in my form load method before it becomes visible (after a splash screen is shown). I have defined my form load as async:
private async void MainForm_Load(object sender, EventArgs e)
And this is my delay function at the end of MainForm_Load:
Task startTimer = Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
});
await startTimer;
splash.Close();
this.Visible = true;
But my delay function is not working, my splash screen is immediately closed and my form becomes visible. What am I doing wrong?
Update:
Here is the code for my form load. But again I have removed some blocks from it so I won't give you the headache:
private async void MainForm_Load(object sender, EventArgs e)
{
this.Visible = false;
// Check license
// Load two user controls
Splash splash = new Splash();
splash.Show();
RefreshPostbagFolder();
InitiateGeneralSettings();
InitiateRunSelectFile();
InitiateRunSelectManualCampaignType();
InitiateImageList();
RefreshManageTab();
RefreshProgramLog();
RefreshServiceLog();
await Task.Factory.StartNew(() => { Thread.Sleep(5000); });
//await startTimer;
splash.Close();
this.Visible = true;
this.BringToFront();
}
This is interesting, and I currently don't have an explanation for it. I'm sure there's more to how WinForms works than simply setting these properties. But this basic example does replicate the issue:
private async void Form1_Load(object sender, EventArgs e)
{
this.Hide();
await Task.Factory.StartNew(() => { Thread.Sleep(5000); });
this.Show();
}
Stepping through the code in the debugger, what's actually happening isn't that the rest of the code isn't being awaited, but that the Hide() isn't doing what we think. The form isn't actually displayed until after Form1_Load executes. Since it executes asynchronously, the internal components which serve to display the form are able to execute during that await. But that's too late to hide the form.
This shows promise:
private async void Form1_Load(object sender, EventArgs e)
{
BeginInvoke(new MethodInvoker(delegate
{
this.Hide();
}));
await Task.Factory.StartNew(() => { Thread.Sleep(5000); });
this.Show();
}
What this does is invoke Hide() after the components have shown the form. However, the form is still shown for a fraction of a second. So it's not ideal.
It's been a long time since I've done anything with WinForms (and even then I didn't do much), so I'm not sure how else to load a form without it being shown. But this at least gets to the issue of why it's not "awaiting" in your case. It is awaiting, but during that await is when the form is initially shown. So hiding the form before that point has no effect.
Your main form gets automatically shown and activated inside Application.Run here, note applicationContext.MainForm.Visible = true.
It becomes visible as soon as the execution point returns from Form.Load event handler to the code which fired the event (in your case, that's where it hits the await inside MainForm_Load), so the asynchronous part doesn't affect its visibility.
To avoid flickering, you can initially show the form as minimized and without the taskbar icon, like this:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form {
WindowState = FormWindowState.Minimized,
ShowInTaskbar = false };
form.Shown += delegate
{
Debug.Print("form.Shown");
};
form.Load += async delegate
{
Debug.Print("form.Load");
var splashForm = new Form { Text = "Splash!" };
splashForm.ShowInTaskbar = false;
splashForm.Show();
await Task.Delay(5000);
splashForm.Hide();
form.WindowState = FormWindowState.Normal;
form.ShowInTaskbar = true;
form.Show();
};
Application.Run(form);
}
}
}
Instead of modifying Program.Main, you can initially set Form.WindowState and Form.ShowInTaskbar in the VS form designer or your MainForm's constructor.
On a side note, use Task.Delay instead of Thread.Sleep wrapped with Task.Run or Task.Factory.StartNew.
I have a generic modal dialog view, which is getting called from viewmodels like below:
NotificationMessageViewModel vm = new NotificationMessageViewModel()
{
MessageContent = jobExistMsg,
MessageHeader = "Warning",
MessageType = NotificationMessageType.Warning,
Type = ModalType.OkCancel
};
mainEntity.ModalContent = vm;
vm.CloseRequested += ImportJobConfirmation_CallBack;
The issue with this approach is, I cannot use this code inside while loop since the code execution ignores the CloseRequested event. The current workaround is, I have callback function for CloseRequested and I call the same function again inside callback function for other items in loop. So its kind of recursive calling.
Is there any better solution to handle this situation?
Edit
I have edited the code a little.
Say I have 10 records, I want this modal popup for every record that exists while processing in loop. If I will write this code inside a while loop, I will see only one pop-up for 10 records and the close request will be fired only once, where as I should get the modal for every existing record(say 5 existing records).
You can use an async / await combination to get what you want. I have just implemented this with a plain window, but you could easily adapt it to use the view model.
In the secondary window (the view model in your case), provide an awaitable method to display the window:
public partial class Window1 : Window
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
public Window1()
{
InitializeComponent();
this.Closed += Window1_Closed;
}
void Window1_Closed(object sender, EventArgs e)
{
tcs.SetResult(null);
}
public async Task ShowAsync()
{
Show();
await tcs.Task;
}
}
Then in the calling window, you can successively show any number of windows and wait for them to close like so:
async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; ++i)
{
var secondaryWindow = new Window1();
await secondaryWindow.ShowAsync();
}
}
If you like, you could also expose the tcs.Task in a property of Window1. This would allow you more flexibility. E.g.:
var secondaryWindow = new Window1();
secondaryWindow.Show();
await secondaryWindow.WaitForCloseTask;
where
//Window1
public Task WaitForCloseTask { get { return tcs.Task; } }
I've an existing WPF application, which has several sections. Every section is a UserControl, that implements an interface.
The interface specify two methods: void LoadData([...]) and bool UnloadData().
Those method are called by the UI thread, so we need to do our work in backgroundworker if it's time consuming.
No problems with LoadData since we can update the UI asynchronously. The problem is with UnloadData().
This should return if we can really leave the current view.
This is computed with the current status of data(Saved/modified/Invalid):
Saved return true,
Invalid asks if you want to stay to save some
correct data or leave without saving
Modified tell you that you can
either cancel your change(return true), either continue to
edit(return false), either save you current data(return true)
The problem is with the "Modified -> Save". This is a time consuming method, so to respect the philosophy of the application, we should run this in a background thread(with a busy indicator).
But if we just launch the thread and go to the next section, it will return "true" to the method call, and we will directly launch the next view.
In my case, loading the next view before our local data is saved can be a problem.
So:
Is there a way to wait on the background thread to finish before returning "true", WITHOUT blocking the UI?
public bool UnloadData(){
if(...){
LaunchMyTimeConsumingMethodWithBackgroundWorker();
return true;//Only when my time consuming method ends
}
//[...]
}
Important EDIT
Maybe I wasn't clear enought: I know how to use a BackgroundWorker, or TPL. My problem is that the parent class(the one which call the UnloadData()" is a class that I cannot edit(for multiple reasons: It's in another DLL that will not be reloaded, it already works with 70+ userControls, all in separate projects(dll), loaded by reflection.
This wasn't my choice, I don't find it good, but I've to deal with it now. I'm mostly looking for way to make my method wait on the return of my method. I'm not sure if it is possible. But I'm looking for a workaround, it will spare me weeks of works.
Ok now I'm excited, because I think I may have discovered something on my own...
So, what you do is this: You create a DispatcherFrame, push that frame onto the Dispatcher, and in the RunWorkerCompleted you set the Continue of the Frame to false.
This is the code so far:
public void Function()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += TimeConsumingFunction;
var frame = new DispatcherFrame();
worker.RunWorkerCompleted += (sender, args) =>
{
frame.Continue = false;
};
worker.RunWorkerAsync();
Dispatcher.PushFrame(frame);
}
private void TimeConsumingFunction(object sender, DoWorkEventArgs doWorkEventArgs)
{
Console.WriteLine("Entering");
for (int i = 0; i < 3; i++)
{
Thread.Sleep(1000);
}
Console.WriteLine("Exiting");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Function();
Console.WriteLine("Returns");
}
You should implement a dependency property "IsBusy" of type bool, that you set to TRUE before starting the BackgoundWorker, and then to FALSE when the work is complete.
On the UI, you bind to that property whatever functionality you want disabled during the processing(like the button for loading the next view, etc.); or maybe showing a "Cancel" button.
You should not "wait" for the operation to complete, you can retrieve the result in an additional variable, that the BackgroundWorker will set:
BackgroundWorker _bw;
bool _returnValue = false;
private void button_Click(object sender, RoutedEventArgs e)
{ // if starting the processing by clicking a button
_bw = new BackgroundWorker();
IsBusy = true;
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
_bw.RunWorkerAsync();
}
void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IsBusy = false;
// retrieve the result of the operation in the _returnValue variable
}
void _bw_DoWork(object sender, DoWorkEventArgs e)
{
_returnValue = UnloadData();
}
private bool UnloadData()
{
if (...)
{
LaunchTimeConsumingMethod();
return true;
}
else
return false;
//etc ...
}
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
// Using a DependencyProperty as the backing store for IsBusy. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register( ... )
You may be able to try using the new "await" features of .NET 4.5.
The await keyword allows you to await the completion of a Task object, without blocking the UI.
Try this modification:
public async bool UnloadData()
{
if(...)
{
await Task.Factory.StartNew(() =>
{
LaunchMyTimeConsumingMethod();
});
return true;//Only when my time consuming method ends
}
//[...]
}
Treat UnloadData as a async operation and let the async/await features handle both the case when it completes synchronously and when it needs to complete asynchronously:
public async Task<bool> UnloadData(){
if(...){
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool
await Task.Factory.StartNew(()=>LaunchMyTimeConsumingMethodWithBackgroundWorker());
// The return statement is the continuation and will run in the UI thread after the consuming method is executed
return true;
}
// If it came down this path, the execution is synchronous and is completely run in the UI thread
return false;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
bool result = await this.UnloadData();
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
EDIT
If you can't modify the UnloadData, then just execute it on the ThreadPool, as #BTownTKD noted:
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool, whether it takes the long or the short path
bool result = await The Task.Factory.StartNew(()=>this.UnloadData());
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
You probably should use TPL if your framework version is 4.0:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // this will work only if you're running this code from UI thread, for example, by clicking a button
Task.Factory.StartNew(() => UnloadData()).ContinueWith(t => /*update ui using t.Result here*/, uiScheduler);
Hope this helps.
You have to implement a callback function (RunWorkerCompleted), this is called when the background worker finishes.
Check out an example here:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx