To use dialog boxes in windows form applications either the main thread should be set as [STAThread] or a separate STA thread needs to be created for the dialog box to run on.
Here comes the issue I could not realy understand. A started STA thread does not finish "sometimes", so the main thread keeps hanging up on the Join().
Now I overcome by using Application.DoEvents() instead of the t.Join() and now it seems working fine, but I would be still interested in what "sometimes" is depending on. In example I use the following static method to open up an openfile-/savefile dialog:
using System.Windows.Forms;
namespace Dialog
{
public class clsDialogState
{
public DialogResult result;
public FileDialog dialog;
public void ThreadProcShowDialog()
{
result = DialogResult.None;
result = dialog.ShowDialog();
}
}
public static class clsShowDialog
{
public static DialogResult STAShowDialog(FileDialog dialog)
{
clsDialogState state = new clsDialogState();
state.dialog = dialog;
System.Threading.Thread t = new System.Threading.Thread(state.ThreadProcShowDialog);
t.SetApartmentState(System.Threading.ApartmentState.STA);
t.Start();
//t.Join(); //Main thread might hang up here
while (state.result == DialogResult.None) Application.DoEvents(); //Everything is refreshed/repainted fine
return state.result;
}
}
}
So usage is simply just:
Dialog.clsShowDialog.STAShowDialog(new SaveFileDialog());
I could not figure out exactly what makes the calling thread hanging up on the join() when it is waiting for an STA thread to finish, but it looks like that it sometimes works, sometimes does not. Finaly I decided to overcome by using:
while (InvokeResult == DialogResult.None) Application.DoEvents();
instead of the Join().
Related
I faced with one interesting moment when working with multithreading.
I have two threads. In main thread I create layout and add to it control,in second thread I create another control and add to the same layout. It works fine, but second thread works a bit longer then main. So main should wait for second thread.I use for this AutoResetEvent and got DeadLock. Below I describe code what I use:
private static AutoResetEvent resetEvent = new AutoResetEvent(false);
private BackgroundWorker backgroundAdvancedViewWorker = new BackgroundWorker();
private delegate void ShowViewDelegate();
public void Run()
{
MainGeneralReportForm mainForm = ObjectFactory.GetOrCreateView<IMainGeneralReportForm>();
backgroundSimpleViewWorker.RunWorkerAsync(_mainForm);
GeneralReportFormatView formatView =
ObjectFactory.ShowView<IGeneralReportFormatView>()
resetEvent.WaitOne();
DoSomething(advancedSearchView);
}
private void backgroundAdvancedViewWorker_DoWork(object sender, DoWorkEventArgs e)
{
MainGeneralReportForm mainForm = e.Argument as MainGeneralReportForm;
if (mainForm!= null && mainForm.InvokeRequired)
{
mainForm.BeginInvoke(new ShowViewDelegate(() =>
{
advancedSearchView =
ObjectFactory.ShowView<IGeneralReportAdvancedSearchView>();
resetEvent.Set();
}));
}
}
}
If main thread doesn't wait for second thread, the application throws NullReferenceException.
Is exist any solution or workaround of this problem?
You block main thread by resetEvent.WaitOne(); and at the same time trying to schedule work item back to main thread with BeginInvoke (which indeed can't run as main thread is waiting).
Not sure what right fix would be, but blocking on main thread is not really an option.
Maybe some "state" field on the form may be enough. Or maybe running DoSomething(advancedSearchView); from BeginInvoke callback (instead of resetEvent.Set();).
Note: if you are on 4.5 you can consider using async/await instead of manual threading.
I have a problem and don't know how to solve that. I'm starting a new thread:
private void button_Click(object sender, EventArgs e)
{
Thread thrd = new Thread(new ThreadStart(loadingScreenStart));
thrd.Start();
//setting some variables, entering some methods etc...
thrd.Abort();
}
public void loadingScreenStart()
{
splashScreen splashObj = splashScreen.GetInstance();
Application.Run(splashScreen.GetInstance());
}
In another form I have:
private static splashScreen m_instance = null;
private static object m_instanceLock = new object();
public static splashScreen GetInstance()
{
lock (m_instanceLock)
{
if (m_instance == null)
{
m_instance = new splashScreen();
}
}
return m_instance;
}
That works fine but when I hit the button a second time then I get an exception that there is no access to the discarded object. Why and how to solve that? I mean after the thread gets aborted I create a new one when hitting the button again.
It's not the thread that is discarded, but the splashScreen instance. You should just create a new one, not try to reuse the old one.
Thread thrd = new Thread(new ThreadStart(loadingScreenStart));
thrd.Start();
//setting some variables, entering some methods etc...
thrd.Abort();
Why do you call thrd.Abort()? Did u know thread finish yet?
You must wait thread finished.
And use double checking here
public static splashScreen GetInstance()
{
if (m_instance == null)
{
lock (m_instanceLock)
{
if (m_instance == null)
{
m_instance = new splashScreen();
}
}
}
return m_instance;
}
And maybe when you call Run splashscreen, it was disposed. Try create and capture it to a field and pass it to your method. Try this.
Task.Run(() =>
var splashObj = splashScreen.GetInstance();
Application.Run(splashObj);
}));
Ok I solved it somehow. Don't think that this is a good solution but it works. Instead of aborting the thread I just hide the Form of the SplashScreen and check if the thread is already running. If yes then I just Show the Form. If not I create a new instance.
This is overly complicated and unsafe. If you really want to handle this using a "splash screen", why not try something like this?
using (var splashScreenForm = SplashScreen.ShowSplashScreen())
{
// Do your work
}
Where SplashScreen has methods like this:
public static SplashScreen ShowSplashScreen()
{
var form = new SplashScreen();
new Thread(() => Application.Run(form)).Start();
return form;
}
public override void Dispose(bool disposing)
{
if (disposing)
{
if (InvokeRequired)
{
Invoke(() => base.Dispose(true));
return;
}
base.Dispose(true);
}
else base.Dispose(disposing);
}
After taking another look at the whole thing I realized there's another problem here, and this is the entirely wrong direction to approach it from.
In your button_Click event you're obviously doing a lot of complicated stuff that takes a lot of time. Otherwise you wouldn't need to show a splash screen. However doing this in an event handler is a bad idea in itself. You totally lock up the UI thread and Windows will soon consider that window to be "hanged". It cannot even repaint itself!
So you should approach this from the completely opposite direction. Instead of trying to show the splash screen from another thread while using the UI thread for heavy work, just move the heavy work to the other thread! Then the splash screen won't need anything exotic like calling Application.Start() in another thread. Just a simple .ShowModal() will be enough. And when the other thread is done with its work, it can just call .Close() on your splash screen.
In a well designed application there is almost never a need for a second UI thread. It's the heavy work that needs to be moved to other threads, not UI.
Just be aware that if you want to manipulate the UI from another thread, you'll need to do some Invoke() stuff. Read more here: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx
I'm trying to make a loading screen window. I use Show() instead of ShowDialog() because I have some code to execute after showing it. When using ShowDialog() form is fine but when using Show() form is messed up. What is causing this and what is the solution? Here is how I did it:
bool closeLoadingWindow = false;
void ShowLoadingWindow()
{
LoadingWindow loadingWindow = new LoadingWindow();
loadingWindow.Show();
while (!closeLoadingWindow);
loadingWindow.Close();
return;
}
public MainWindow()
{
Thread loadingWindowThread = new Thread(ShowLoadingWindow);
loadingWindowThread.Start();
InitializeComponent();
// ...
closeLoadingWindow = true;
}
When using ShowDialog():
When using Show():
The reason ShowDialog is working is because your while loop won't be executing, once the runtime hits that line of code it will stop processing until the form is dimissed.
Your code doesn't make sense, the point of using a thread here is to keep the "busy" code (your while loop) out of the main UI thread so it doesn't block. However, you are trying to create/show your form on the same thread, and a non-UI thread at that.
You don't necessarily need to use Show here, you can use ShowDialog but it is a little bit trickier in terms of dimissing the form etc. However, to solve the problem you have at the minute I would recommend you do:
LoadingWindow _loadingWindow;
void ShowLoadingWindow()
{
if (_loadingWindow == null)
_loadingWindow = new LoadingWindow();
_loadingWindow.Show();
}
void HideLoadingWindow()
{
if (_loadingWindow != null)
{
_loadingWindow.Close();
_loadingWindow.Dispose();
}
}
void LoadSomething()
{
while (...)
{
// busy code goes here
}
// after code is finished, close the form
MethodInvoker closeForm = delegate { HideLoadingWindow(); };
_loadingWindow.Invoke(closeForm);
}
public MainWindow()
{
ShowLoadingWindow();
new Thread(LoadSomething).Start();
}
}
FYI - Depending on the nature of exactly what your trying to do in the thread it might be a better approach to use the Task Parallel Library rather than creating a dedicated thread, various benefits like continuation / cancellation support.
In my application I've created a thread which draws on a Bitmap object, and when it is done drawing it calls form.Invoke(form.Refresh) which draws the bitmap to the main form. (form being a reference to the main form of my application.)
The Problem: I've noticed that if I close the form while the other thread is waiting for the callback message from UI thread (because of the Invoke call) - the thread will wait indefinitely(?) and will essentially get stuck, preventing the application from closing.
Using BeginInvoke doesn't make sense in my program, because the time it takes to refresh the form has to be accounted for in the work of the thread.
Q: What would be the best way to prevent this from happening?
Edit: I've read in some places that its a bad idea, but it seems to me that the only way to deal with this is to call Thread.Abort(). I can't close the thread by using a flag or something similar, since its just stuck at the Invoke call.
Edit 2: To make it clear, what I'm looking for is a way to tell the worker thread that it should stop waiting for the Invoke callback and continue working.
The best answer is of course to use BeginInvoke. If you need the thread to wait until the UI thread has processed the result, then you'll have to add another synchronization method to the process.
An AutoResetEvent would work as a gate, something like this:
using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
public class MainForm : Form
{
protected override void OnLoad(EventArgs e)
{
new Thread(ProcessBitmap) { IsBackground = true }.Start(null);
base.OnLoad(e);
}
void DoneProcessingBitmap(Bitmap bitmap)
{
Trace.WriteLine("Done Processing Bitmap");
uiDoneEvent.Set();
}
AutoResetEvent uiDoneEvent = new AutoResetEvent(false);
volatile bool terminate = false;
void ProcessBitmap(object state)
{
while (!terminate)
{
Bitmap bitmap = (Bitmap)state;
Trace.WriteLine("Processing Bitmap");
Thread.Sleep(5000); // simulate processing
BeginInvoke(new Action<Bitmap>(DoneProcessingBitmap), bitmap);
Trace.WriteLine("Waiting");
uiDoneEvent.WaitOne();
}
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
Check if the form still exists before calling Invoke. Something like...
if (form != null && !form.Disposed)
form.Invoke(form.Refresh());
When my application is loading. I display a progress bar using the code below. The problem is if someone clicks on the toolbar context menu (the way to exit) it will be blocked until this the progress bar is closed. Does anyone know a better way of achieving this?
The reason I'm using ShowDialog is that when I used Show the progress bar wouldn't animate - I'm using the MarqueeStyle.
Thanks
public partial class PopUpProgessBar : Form
{
public PopUpProgessBar()
{
InitializeComponent();
}
Thread t;
private void StartAnmiation()
{
this.Update();
this.ShowDialog();
}
public void Stop()
{
if (t != null)
{
t.Abort();
t.Join();
}
}
public void Start()
{
if (t == null)
{
t = new Thread(new ThreadStart(this.StartAnmiation));
t.Start();
}
}
This code doesn't look quite right. Are you sure it doesn't throw cross-thread violations? In general, your whole metaphor here is wrong. You need to keep the GUI on the GUI thread. Load your application on the background thread and have it send progress updates to the GUI thread.
Your PopupProgressBar form shouldn't be responsible for loading itself in a new thread, that should be done in presumably your main window.
I would get rid of all the thread stuff in PopupProgressBar and make it simply start updating it's marquee. Then, in your main window (OnLoad) you tell it to do it's thing:
bool done = false;
PopupProgressBar splashForm = null;
ThreadPool.QueueUserWorkItem((x) =>
{
using (splashForm = new PopupProgressBar ())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
// do all your initialization work here
// also, during each step of your initialization you could send call a function
// in splashForm to update
done = true;