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();
}
}
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.
I am running a test unit (and learning about them). Quite simply, my unit creates a List and passes it to my MainWindow.
The issue I have is after I show() the main window the unit method ends. I want the unit to not finish until I close the MainWindow. This is what I've done (see below) - it obviously doesn't work and feels like I'm on the wrong path here. How can I do this properly?
[TestClass]
public class Logging
{
bool continueOn = true;
[TestMethod]
public void ShowLogs()
{
ShowResults(createLogList());
}
private void ShowResults(List<Log> logList)
{
MainWindow mw = new MainWindow(logList);
mw.Closed += mw_Closed;
mw.Show();
while (continueOn)
{ }
}
void mw_Closed(object sender, EventArgs e)
{
this.continueOn = false;
}
private List<Log> createLogList()
{
List<Log> listLog = new List<Log>();
//logic
return listLog;
}
Maybe I have to put this onto a background worker thread and monitor that - to be honest I've no idea and before I waste hours, I'd appreciate some guidance.
The WPF Window must be created and shown on a thread which supports the WPF window infrastructure (message pumping).
[TestMethod]
public void TestMethod1()
{
MainWindow window = null;
// The dispatcher thread
var t = new Thread(() =>
{
window = new MainWindow();
// Initiates the dispatcher thread shutdown when the window closes
window.Closed += (s, e) => window.Dispatcher.InvokeShutdown();
window.Show();
// Makes the thread support message pumping
System.Windows.Threading.Dispatcher.Run();
});
// Configure the thread
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
Note that:
The window must be created and shown inside the new thread.
You must initiate a dispatcher (System.Windows.Threading.Dispatcher.Run()) before the ThreadStart returns, otherwise the window will show and die soon after.
The thread must be configured to run in STA apartment.
For more information, visit this link.
Of course, since it is only for testing, using
ShowDialog()
may be an option instead of 'Show()'
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 :)
if I call form.show() on a WinForms object from another thread, the form will throw an exception. Is where any way I can add a new, visible form to the main app thread? Otherwise, how can I open the form without stopping my currently executing thread?
Here is my sample code. I am attempting to start a thread and then execute some work within that thread. As the work progresses, I will show the form.
public void Main()
{
new Thread(new ThreadStart(showForm)).Start();
// Rest of main thread goes here...
}
public void showForm()
{
// Do some work here.
myForm form = new myForm();
form.Text = "my text";
form.Show();
// Do some more work here
}
Try using an invoke call:
public static Form globalForm;
void Main()
{
globalForm = new Form();
globalForm.Show();
globalForm.Hide();
// Spawn threads here
}
void ThreadProc()
{
myForm form = new myForm();
globalForm.Invoke((MethodInvoker)delegate() {
form.Text = "my text";
form.Show();
});
}
The "invoke" call tells the form "Please execute this code in your thread rather than mine." You can then make changes to the WinForms UI from within the delegate.
More documentation about Invoke is here: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
EDIT: You will need to use a WinForms object that already exists in order to call invoke. I've shown here how you can create a global object; otherwise, if you have any other windows objects, those will work as well.
You should call Application.Run() after you call form.Show(). For example:
public void showForm()
{
// Do some work here.
myForm form = new myForm();
form.Text = "my text";
form.Show();
Application.Run();
// Do some more work here
}
As for the details behind why, this msdn post may help.
The best way by my experience:
var ac = (ReportPre)Application.OpenForms["ReportPre"];
Thread shower = new Thread(new ThreadStart(() =>
{
if (ac == null)
{
this.Invoke((MethodInvoker)delegate () {
ac = new ReportPre();
ac.Show();
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
pictureBox1.Visible = true;
});
if (ac.InvokeRequired)
{
ac.Invoke(new MethodInvoker(delegate {
ac.Hide();
ac.Show();
}));
}
}
}));
shower.Start();
If your use case is to display a GUI while the Main GUI Thread is busy (like loading bar) you can do the following:
private void MethodWithLoadingBar()
{
Thread t1 = new Thread(ShowLoading);
t1.Start();
// Do the Main GUI Work Here
t1.Abort();
}
private void ShowLoading()
{
Thread.Sleep(1000); //Uncomment this if you want to delay the display
//(So Loading Bar only shows if the Method takes longer than 1 Second)
loadingGUI = new LoadingGUI();
loadingGUI.label1.Text = "Try to Connect...";
loadingGUI.ShowDialog();
}
My LoadingGUI is a simple Form with a public Label and a ProgressBar with Style set to Marquee
After searching the web and not finding a good solution, I came up with my own:
public async Task FunctionWhereIStartForm( Func<Delegate,object>invoke )
{
//this is on another thread
invoke( new MethodInvoker( ( ) =>
{
new formFoo().Show( );
} ) );
}
Then call like this:
await FunctionWhereIStartForm(Invoke);
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.