Is there a way to show a form in a background thread but you can always see it in front of UI thread as modeless?
For example the UI thread run with parentForm, and there is a backgroundworker thread run with childForm on top. May I work with parentForm with childForm on top modeless, which means I can always see my childForm but not block my parentForm.
It seems childForm.ShowDialog(parentForm) will block UI thread and I don't want to Invoke childForm in UI thread.
I'm not sure what do you mean but you can always try to run Show() within a particular form if you would like to show the form without blocking the main UI
Example
Form2 _Form2 = new Form2();
_Form2.Show();
Alternatively, if you would like to run a new form as the main form of the application asynchronously, you may try to create a new Thread and run the form within it
Example
public void RunThread()
{
Thread thread = new Thread(new ThreadStart(RunForm)); //Create a new thread to execute RunForm()
thread.Name = "NewForm"; //Name the new thread (Not required)
thread.Start(); //Start executing RunForm() in the new thread
}
public void RunForm()
{
try
{
Application.Run(new Form2()); //Run Form2() as the main form of the application
}
catch (Exception ex)
{
//DoSomething
//MessageBox.Show(ex.Message);
}
}
Thanks,
I hope you find this helpful :)
Related
I am trying to start a winForm from a thread, but when i do so, the form show but none of the labels are loaded ( the background where they should be is white ) and the form is frozen.
I've tried it with some other winForms that i know that work just fine and it still doesn't seem to work ? Has anyone encountered this problem ?
I know the question is vague but there isn't really any specific code that I could give to help understand the problem.
That is because the Message Loop runs on UI thread only. And when a control or window is created in any other thread, it cannot access that message loop. And thus, cannot process user input.
To solve this, try creating a window from UI thread and create a thread from that window to do whatever you want to do in different thread.
UI thread is supposed to be one.
Then, I suggest you to open your form calling a method of your original form thread, like in the example below:
(To test it just create an empty form called MainForm and paste this code in it)
public delegate void OpenFormDelegate(string txt);
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
var button1 = new Button();
button1.Text = "Run for 5 secs and open new window";
button1.Dock = DockStyle.Top;
this.Controls.Add(button1);
button1.Click += new EventHandler(button1_Click);
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Run));
t.Start();
}
public void Run()
{
Thread.Sleep(5000); // sleep for 5 seconds
this.BeginInvoke(new OpenFormDelegate(OpenNewForm), "Hello World !");
}
public void OpenNewForm(string text)
{
Form f = new Form();
f.Text = text;
f.Show();
}
}
It is related to thread access, when the new form is created it will not be able to access the UI thread. Use the main thread to create the form and new thread to process the infomation.
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().
My application fetches data from a live feed, processes it and displays the results. This data is updated every 5 seconds. In the Load event of Main form I've created a thread to show the splash screen which is shown until the first data cycle is run .
The data fetching and processing thread (RecieverThread) calls RecieveFeed. The isue I'm facing is that form2 which displays data fetched in RecieveFeed is shown before the first cycle is run completely. How do I ensure that form2 is loaded only after the first cycle has completed fetching data.
Code in the Main form:
private void frmMain_Load(object sender, EventArgs e)
{
Hide();
// Create a new thread from which to start the splash screen form
Thread splashThread = new Thread(new ThreadStart(StartSplash));
splashThread.Start();
//Thread to call the live feed engine. This thread will run for the duration of application
ReceiverThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReceiveFeed));
ReceiverThread.Start();
frmSecondForm form2 = new frmSecondForm();
form2.MdiParent = this;
form2.WindowState = FormWindowState.Maximized;
Show();
form2.Show();
}
public frmRaceRace()
{
InitializeComponent();
this.splash = new SplashScreen();
}
private void StartSplash()
{
splash.Show();
while (!done)
{
Application.DoEvents();
}
splash.Close();
this.splash.Dispose();
}
private void ReceiveFeed()
{
while (!StopReceivingData)
{
foreach (...)
{
//Fetches data from live engine
DLLImportClass.GetData1();
//Manipulates and stores the data fetched in datatables
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { StoreData(); }))
rowsProcessed++;
if (!done)
{
this.splash.UpdateProgress(100 * rowsProcessed / totalRows);
}
}
done = true;
Thread.Sleep(5000);
}
}
I think what you need to use here is System.Threading.AutoResetEvent. Basically, add a member of this to your form class:
private AutoResetEvent waitEvent_ = new AutoResetEvent(false); // create unininitialized
After showing your splash, you want to wait for this event to be signalled:
private void StartSplash()
{
splash.Show();
// this will time out after 10 seconds. Use WaitOne() to wait indefinitely.
if(waitEvent_.WaitOne(10000))
{
// WaitOne() returns true if the event was signalled.
}
} // eo StartSplash
Finally, in your processing function, when you're done, simply call:
waitEvent_.Set();
Looks like you've got some race conditions in your code.
When doing threading with WinForms and most (if not all) UI frameworks, you can ONLY access the UI objects (forms and controls) from a single thread.
All other threads can only access that thread using .InvokeRequired() and .BeginInvoke(). These calls can be used to run a delegate in the UI thread. See:
{REDACTED: StackOverflow will only allow me to post 1 hyperlink. Google these}
There is a builtin shortcut for this in the BackgroundWorker class.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Simply do this (Psuedocode):
public void StartSplash()
{
Splash.Show();
BackgroundWorker bgw = new BackgroundWorker();
// set up bgw Delegates
bgw.RunWorkerAsync();
}
public void bgw_DoWork( ... etc
{
// do stuff in background thread
// you cannot touch the UI from here
}
public void bgw_RunWorkerCompleted( ... etc
{
Splash.close();
// read data from background thread
this.show(); // and other stuff
}
Now, you're guaranteed not to close the SplashScreen and not to start the main window before your data is delivered.
Other considerations: you'll probably need to use locks to secure the data you might access in the background thread. You should never access data in more than 1 thread without locking it.
Change your frmMain_Load to this:
private void frmMain_Load(object sender, EventArgs e)
{
Hide();
//Thread to call the live feed engine. This thread will run for the duration of application
ReceiverThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReceiveFeed));
ReceiverThread.Start();
frmSecondForm form2 = new frmSecondForm();
form2.MdiParent = this;
form2.WindowState = FormWindowState.Maximized;
StartSplash();
Show();
form2.Show();
}
I am writing a test application where I need to put Form on separate thread.
So if I create Form window from main thread and set its .Owner = this everything works.
If I spawn thread UIThread and set Owner from new thread I get exception.
Getting exception is understandable since you can't access forms directly.
My question is is there a message that I need to catch on main thread and do BeginInvoke to push it on it's message pump? Since UIForm ShowInTaskbar is set to false I need to click on main application in taskbar and restore with all its children windows.
private void UIThread() // New Thread call
{
UIForm form = new UIForm();
form.ShowInTaskbar = false;
form.Owner = this;
Application.Run(form); // Expected Exception
}
I am not sure, maybe Application.Run shall be called only once per application. See if this one will work for you
Application.Run(new Form1());
-----------------
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var thread = new Thread(
() =>
{
var form2 = new Form {Owner = this};
});
thread.Start();
}
}
I am trying to start a winForm from a thread, but when i do so, the form show but none of the labels are loaded ( the background where they should be is white ) and the form is frozen.
I've tried it with some other winForms that i know that work just fine and it still doesn't seem to work ? Has anyone encountered this problem ?
I know the question is vague but there isn't really any specific code that I could give to help understand the problem.
That is because the Message Loop runs on UI thread only. And when a control or window is created in any other thread, it cannot access that message loop. And thus, cannot process user input.
To solve this, try creating a window from UI thread and create a thread from that window to do whatever you want to do in different thread.
UI thread is supposed to be one.
Then, I suggest you to open your form calling a method of your original form thread, like in the example below:
(To test it just create an empty form called MainForm and paste this code in it)
public delegate void OpenFormDelegate(string txt);
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
var button1 = new Button();
button1.Text = "Run for 5 secs and open new window";
button1.Dock = DockStyle.Top;
this.Controls.Add(button1);
button1.Click += new EventHandler(button1_Click);
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Run));
t.Start();
}
public void Run()
{
Thread.Sleep(5000); // sleep for 5 seconds
this.BeginInvoke(new OpenFormDelegate(OpenNewForm), "Hello World !");
}
public void OpenNewForm(string text)
{
Form f = new Form();
f.Text = text;
f.Show();
}
}
It is related to thread access, when the new form is created it will not be able to access the UI thread. Use the main thread to create the form and new thread to process the infomation.