Handling multiple dialog windows with async tasks - c#

From my main WinForm application window, I want to simultaneously open multiple modeless dialogs. When all dialog windows are opened, I will raise some events and the event handlers on individual open dialogs are supposed to take necessary actions on those events. Since user wants access to the main form all the time, I cannot open these windows as modal dialogs.
I have written following code.
With this code, the dialog windows are opened but they are immediately closed too. What's wrong with the code? Why windows don't remain open?
private async void buttonOpenWindows_Click(object sender, EventArgs e)
{
Task[] tasks = new Task[]
{
Task.Factory.StartNew(CreateWindow),
Task.Factory.StartNew(CreateWindow),
Task.Factory.StartNew(CreateWindow),
Task.Factory.StartNew(CreateWindow)
};
await Task.WhenAll(tasks);
// Raise necessary events when all windows are loaded.
}
private async Task CreateWindow()
{
// A refernce to main form is passed to the Window because all Windows will be subsribing to some events raised by the main form.
var window = new Window1(this);
window.Show();
}

What is happening:
private async Task CreateWindow()
{
// A refernce to main form is passed to the Window because all Windows will be subsribing to some events raised by the main form.
var window = new Window1(this);
window.Show();
}
This creates a new window, owned by the first available thread in the system. As there is no more blocking code to run, the Task finishes successfully, and so the Window is closed/disposed.
If you want the Window to continue to be opened, you need to open it in your main thread.
Not sure what you are expecting to benefit from by opening the Windows in an async manner, though.

Related

Change Form when application is working and freezes

I'm developing a C# WinForms application; this application will have many windows to show some reports. These windows are forms created from some DLLs by reflection, so the code is not so simple to change.
The problem is that when a form is getting data or processing some info it freeze/locks all the application, so users can't navigate to other forms in the application. So, if the user wants a report that takes 5 minutes, the app freeze 5 minutes and the user can't navigate to another window.
I have tried to call the new forms with Invoke((MethodInvoker)delegate () {}); but it freezes anyway. I tried to use MDI from Windows Forms, but also freezes. I tried to use Document Manager or Tab View from DevExpress, but the same problem.
I also have begun reading the problems to create a form in a new thread with the UI from the main thread, but I think this is not my problem because it's ok for the form to be freeze, but not to freeze all the application and user can navigate other forms.
If I try to create the forms in other process the application loads too slow because all the controls it has, so ideally it must be in the same process.
Is there any way I can call a new form in a new thread, that won't freeze all application while processing? Is there any opensource/paid control that can help me with this problem?
Thanks in advance, regards.
Simpliest way to do asynchronous programming in winforms and C# is async/wait logic.
Try something like this :
using System.Threading;
using System.Threading.Tasks;
private async void btnDoReport_Click(object sender, EventArgs e)
{
Task<bool> reportTask = new Task<bool>(ReportFunction); // if your function is void you can create it as Task reportTask = new Task(ReportFunction)
trainTask.Start(); // here your task - report is executed asynchronously and GUI does not freeze
bool reportResult = await reportTask; // wait on resposne from your reportTask
}
private bool ReportFunction()
{
// do what ever you need to in this function
return true;
}
You can also use BackgroundWorker class which is also working asynchronously and even you can raise event from your asynchronously running thread. After that this event you can catch in main thread - GUI thread, so you will be able to safely access controls of your GUI applications without errors.
using System.ComponentModel;
private BackgroundWorker bWork = new BackgroundWorker();
this.bWork.DoWork += BWork_DoWork;
this.bWork.ProgressChanged += bwork_ProgressChanged;
private void btnReport_Click(object sender, EventArgs e)
{
// this will run your code asynchronously
this.bWork.RunWorkerAsync();
}
private void BWork_DoWork(object sender, DoWorkEventArgs e)
{
// here you can create your report
// if you want you can raise event like this :
this.bWork.ReportProgress(this)
// and after raising of above event function : bWork_ProgressChanged is
//called where you can safely access main thread controls
}
private void bwork_ProgressChanged(object sender,
System.ComponentModel.ProgressChangedEventArgs e)
{
/* here you can access main thread GUI elements for example progress bar
without worrying of some problems because this function is called in
main thread space*/
}

XAML window inside winforms program

When I use a button on a form for Winforms to open a window made in XAML the first time it works with no issues, then I close the window and re-click the button, but on the InitializeComponent() for the window being created I get an exception with the message, "The Application object is being shut down".
//code on button press is this
var rs = new RestoreSettings();
rs.Show();
I have tried hosting it as a user control inside an element host still the same issue.
In a normal application both the WinForms and WPF environment is kickstarted by an Application type, which is typically called from the Main method.
Here this does not happen for the WPF environment, which causes problems. If you want to show a WPF window from a WinForms application you need to boot up the dispatcher. Best to do this in a new thread so it can be shut down as many times as you want:
private void ShowWpfWindow()
{
// This delegate is executed in new thread
ThreadStart showWindow = () =>
{
var window = new RestoreSettingsWindow(); // your window to show
// making sure that the thread can exit when the window is closed
window.Closed += (sender, e) => Dispatcher.InvokeShutdown();
window.Show();
// Starts the dispatcher in the new thread and does not let the thread exit.
// This call is returned when the window is closed (due to the Closed event handler)
System.Windows.Threading.Dispatcher.Run();
};
// Creating and starting an STA thread for the WPF window
Thread wpfThread = new Thread(showWindow);
wpfThread.SetApartmentState(ApartmentState.STA);
wpfThread.Priority = ThreadPriority.Normal;
wpfThread.Start();
}

How to terminate a thread after a window on this thread is closed in WPF c#?

I am new to multi-threading. I am doing a project using wpf.
Brief introduction to my project:
One mainwindow
One method in mainwindow creates another thread that creates another window, which is called window 2.
Whenever window 2 is visible, mainwindow is suspended(so i used join() method to suspend the main thread)
The problem is whenever I closed the window 2, the new thread doesn't terminate. so the main thread is forever suspended. So how to solve this problem.
The following code is where i create a new thread for window 2 in MainWindow Class:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Thread addThread = new Thread(CreateCourseWindow);
addThread.SetApartmentState(ApartmentState.STA);
addThread.Start();
Thread.Sleep(TimeSpan.FromSeconds(1));
addThread.Join();
if (addcourse.Saved) CreateCourseButton(myCourses.Count(), myCourses.Last());
}
The following code is the entry function for the new thread
private void CreateCourseWindow()
{
addcourse = new AddCourse();
addcourse.Activate();
addcourse.Show();
addcourse.Topmost = true;
System.Windows.Threading.Dispatcher.Run();
}
The following code is where i want to terminate the thread
private void Button_Click_2(object sender, RoutedEventArgs e)
{
this.Close();
}
AddCourse is the window 2 class.
First off, don't create multiple UI threads. It creates a real mess that's very hard to deal with. Only ever have on UI thread in your application unless you're sure that you need more, and that you really know what you're doing.
As for this specific case, you don't need to worry about asynchrony, multiple threads, or any of it:
ChildWindow child = new ChildWindow();
this.Hide();
child.ShowDialog();
this.Show();
By using ShowDialog instead of Show you remove all the pesky asynchrony issues.
If you want the parent window visible but not enabled you can remove the show/hide but leave the ShowDialog, which will ensure the parent is disabled.
It is not advised to suspend a window by hanging the thread. What you get is a complete non-responsive window, that doesn't handle any Windows event at all. For example, it will not react to move events, or set cursor events, or other things that you would expect even a disabled window to have. Furthermore, if there are any COM STA objects on this thread they will hang (and sometimes there can be such object even if you didn't explicitly create them).
How are you opening the window from the second thread? It sounds like this thread is running a loop which doesn't terminate when the window is closed. If you post some code it can help.

How to show a WinForms Modal Dialog from a WPF App Thread

I have a WPF application. The main window of this application has a button. I am opening a WinForms modal dialog in a separate thread when this button is clicked. The trouble I am having is that the dialog does not behave like a modal i.e it is still possible to switch focus to the main window, whereas, I require to allow focus on the newly opened dialog and it should not be possible to select the main window.
Note: I cannot move the modalDialog.ShowDialog(); outside of the delegate because the dialog form creates controls dynamically and this means that these controls must remain on the thread that it was created. To be more clear, if I move the modalDialog.ShowDialog(); outside I will get an exception like so:
Cross-thread operation not vaild: Control 'DynamicList' accessed from a thread other than the one it was created on.
Any ideas as to how I might make the form behave as a modal?
Here is the code:
private void button1_Click(object sender, RoutedEventArgs e)
{
DoSomeAsyncWork();
}
private void DoSomeAsyncWork()
{
var modalDialog = new TestForm();
var backgroundThread = new Thread((
delegate()
{
// Call intensive method that creates dynamic controls
modalDialog.DoSomeLongWaitingCall();
modalDialog.ShowDialog();
}
));
backgroundThread.Start();
}
You should always create controls on the UI thread. If you do that, calling ShowDialog() through Dispatcher should work.

How to ensure a Windows Form is "fully" closed?

I have a form in a Windows form application that I want to display on top of a main form, close it, and then immediately show a dialog box using MessageBox.Show(). But the first form is still showing when the message box is shown, and it does not disappear until I click OK on the message box. I tried waiting to show the message box in an event handler for the form's VisibleChanged event and even calling Refresh() on both the form and the main form. Is there a way I can determine when the first form has fully disappeared before displaying the message box?
Edit:
Here is some code that demonstrates how the forms are being shown.
static class Program
{
// The main form is shown like this:
static void Main()
{
Application.Run(new MainForm());
}
}
public class Class1
{
// _modalForm is the first form that is displayed that won't fully go away
// when it is closed.
ModalForm _modalForm;
BackgroundWorker _worker;
public Class1()
{
_modalForm = new ModalForm();
_worker = new BackGroundWorker();
_worker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted
}
public void Method1()
{
_worker.RunWorkerAsync();
// The first form is shown.
_modalForm.ShowDialog();
}
// This code runs in the UI thread.
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_modalForm.VisibleChanged += new EventHandler(_modalForm_visibleChanged);
_modalForm.Close();
}
void _modalForm_visibleChanged(object sender, EventArgs e)
{
// When the message box is shown, the other form is still visible
// and remains so until I click OK.
MessageBox.Show("The other form was just closed.");
// Note: I originally tried to use the FormClosed event instead of
// VisibleChanged. Then I tried Deactivate, in attempt to use an event
// that occurred later thinking that might do the trick. VisibleChanged
// is the latest event that I found.
//
}
I'll guess that you are running your code on Windows XP or Vista/Win7 with Aero turned off. Closing a form does not make the pixels on the screen disappear instantly. The Windows window manager sees that the window for the form got destroyed and that this revealed parts of other windows underneath it. It will deliver a WM_PAINT message to let them know that they need to repaint the parts of the window that got revealed.
This will not work properly if one or more of those windows isn't actively pumping a message loop. They can't see the WM_PAINT message. They won't repaint themselves, the pixels of the closed form will remain on the screen.
Find out why these windows are not responding. Hopefully it is your window and the debugger can show you what the UI thread is doing. Make sure it isn't blocking on something or stuck in a loop.
After seeing the edit: there's indeed blocking going on, of a different kind. The MessageBox.Show() call is modal, it prevents the VisibleChanged event from completing. That delays the closing of the form.
Use System.Diagnostics.Debug.WriteLine() or Console.WriteLine() to get diagnostics in a Window Forms app. You'll see it in the Output window. Or simply use a debugger breakpoint.
The Form.FormClosed event is raised when the form completes closing. At this point, all Form.FormClosing event handlers have been run, and none of them canceled the close.
Form.FormClosed replaced Form.Closed (which is deprecated) in the .NET 2.0 framework.
Form.Closed Event
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.closed(VS.71).aspx

Categories