I am displaying a winForm using Application.Run(); in ShowForm() as shown below.
I am using ShowForm() with a thread in ShowSplash().
Im calling ShowSplash() in another Form,Lets say 'Form1'.
How can I display it as Modal to Form1?
static private void ShowForm()
{
splashForm = new frmWorking();
Application.Run(splashForm);
}
static public void ShowSplash()
{
Thread thread = new Thread(new ThreadStart(frmWorking.ShowForm));
thread.Start();
}
You can show modal form through ShowDialog method which expects instance of parent form. So you can provide instance of parent form (Form1 in your case).
eg.
splashForm.ShowDialog()
Related
How can I invoke into the message loop of the tread/form after calling Application.Run() without a dialog? The reason is that I want to prepare (and later show) a dialog that is clickable even if a modal dialog is shown in the main application.
static Form1 dialog;
private static void CreateDialog(object obj)
{
dialog = new Form1();
Application.Run();
}
static void Main(string[] args)
{
var thread = new Thread(CreateDialog);
thread.Start();
Thread.Sleep(2000); //only for demonstration
dialog.Invoke((Action)dialog.Show); //InvalidOperationException: need window handle first
}
I found the solution here: https://stackoverflow.com/a/4411493/1520078
I just have to access the Handle property of the Form to be able to invoke it from now on.
private static void CreateDialog(object obj)
{
dialog = new Form1();
_ = dialog.Handle;
Application.Run();
}
Now this does not throw any more:
dialog.Invoke((Action)dialog.Show);
Have C# Windows Forms application with secondary thread that receives requests from an external system to show or hide a form/dialog. I understand that secondary threads do not have a message loop mechanism. I understand that ShowDialog has its own message loop, thus a secondary thread can invoke it, but the secondary thread is then blocked until the form closes, and thus cannot respond to later requests to hide the form. The problem is, how to get the form displayed by the secondary thread to hide [or again become visible]. Tried invoking Interrupt on secondary thread, but that does not interrupt or abort the ShowDialog. Nothing appears to abort ShowDialog except a ShowDialog UI callback that calls Close.
Actually both forms share the same message loop.
Every code that handles gui must be ran on the same thread (which handles the gui part).
What you need is to run the commands on that thread using BeginInvoke.
I made a sample application which only has a simple button and when you press it a thread is started which sleeps 3 seconds and then opens the dialog and sleeps again and next time it closes it down and so forth.
Here is the code for the main window:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Thread t;
Form2 f2;
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(ThreadMethod);
t.Start();
button1.Enabled = false;
}
private void ShowForm()
{
f2 = new Form2();
f2.ShowDialog();
}
private void ThreadMethod()
{
for (; ; )
{
Thread.Sleep(3000);
if(f2 == null)
{
BeginInvoke((Action)(() => { ShowForm(); }));
}
else
{
f2.CloseMe();
f2 = null;
}
}
}
}
And then the code for the form used as a dialog:
using System;
using System.Windows.Forms;
namespace QuestionTesting
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public void CloseMe()
{
BeginInvoke((Action)(() => { Close(); }));
}
}
}
This is just simple code and adapt it as you see fit. Instead of the one liners you can create a delegate instead and use that in the BeginInvoke call.
So I tried to create a new form and reference it...the compiler didn't mind this but it clearly wasn't changing the visibility of my picturebox. this is how I was calling my method found in my form, FROM my c# script.
Form1 updateForm = new Form1();
updateForm.setLights();
It called the method, and seemed like it worked! Until I read a post about instancing forms, and how by creating a "new" instance of Form1, that anything referenced by my updateForm would not change what I would see on my Form1.
So what I need to do is to call the function in setLights() which is in my Form1, and get it to change the visibility of my image on that form, from my C# code. Please see below (i understand the issue of the instancing problem mentioned above, but I left it in so that hopefully it will give better insight into what I am "trying" to do :) ALSO, please keep in mind that setLightCall() is running in a separate thread. Thanks in advance!
This code is also in my main c# script, and is the main function that I use to call my threads
static void Main(string[] args)
{
Thread FormThread = new Thread(FormCall);
FormThread.Start();
Thread setLightThread = new Thread(setLightCall);
setLightThread.Start();
log4net.Config.XmlConfigurator.Configure();
StartModbusSerialRtuSlave();
}
This code is in my main C# script
public void setLightCall(Form1 parent)
{
Form1 updateForm = new Form1();
while(true)
{
updateForm.setLights();
}
}
The below code is in my form1
public void setLights()
{
Input1GreenLight.Visible = false;
}
Here is an example of what I think you are wanting to try. Note the use of Invoking and delegates to be able to access the PictureBox's Visible method. I had to add the System.Windows.Forms Namespace to the Console Application to be able to access the instance of the Form that was created in the FormThread Method, this is assuming that you only have 1 Form in your FormCollection.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;
namespace ConsoleApplication59
{
class Program
{
static void Main(string[] args)
{
Thread FormThread = new Thread(FormCall);
FormThread.Start();
Thread.Sleep(2000); //Sleep to allow form to be created
Thread setLightThread = new Thread(setLightCall);
setLightThread.Start(Application.OpenForms[0]); //We can get by with this because just one form
Console.ReadLine();
}
public static void setLightCall(object parent)
{
Form1 updateForm = (Form1)parent;
while (true)
{
updateForm.Invoke(updateForm.setLights, new object[] { false });
}
}
public static void FormCall()
{
Application.Run(new Form1());
}
}
}
Form1
public partial class Form1 : Form
{
public delegate void Lights(bool state);
public Lights setLights;
public Form1()
{
InitializeComponent();
setLights = new Lights(setLightsDelegate);
}
public void setLightsDelegate(bool state)
{
Input1GreenLight.Visible = state;
}
}
Given the class below, to launch a splash screen on an alternate thread:
public partial class SplashForm : Form
{
private static Thread _splashThread;
private static SplashForm _splashForm;
public SplashForm()
{
InitializeComponent();
}
// Show the Splash Screen (Loading...)
public static void ShowSplash()
{
if (_splashThread == null)
{
// Show the form in a new thread.
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// Called by the thread.
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new SplashForm();
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(_splashForm);
}
// Close the splash (Loading...) screen.
public static void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
This is called and closed with the following respective commands
SplashForm.ShowSplash();
SplashForm.CloseSplash();
Fine.
I am not exactly new to the TPL, of course we can show the form on another thread using something as simple as:
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
My issue is closing this SomeForm down when you are ready. There must be a better way than creating a public static method in the SomeForm class like
private static SomeForm _someForm;
public static void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
My question is, what is the best way to do the same thing as shown using the SplashForm class above using the Task Parrallel Library (TPL)? Specifically, the best way to close the form invoked on another thread from the UI.
Your question does not seem to be so much about a difference between Thread and Task because what you want is to get rid of the "dirty" static state. I suggest you encapsulate it into a class:
class SplashController
{
public void Run() {
_someForm = new SomeForm();
someForm.ShowDialog();
}
private SomeForm _someForm;
public void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
}
You can call Run using whatever threading mechanism you like. CloseSomeForm does not use threading so it is independent of this problem.
You can now store a reference to an instance of SplashController wherever you like. In local variables or indeed in a static variable. The latter makes sense because there is exactly one splash screen.
Because the static state is now well encapsulated I don't see any problem with it being statically held.
You probably shouldn't do something like this
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
because it would require a message loop to be present on the exact thread that creates the Form, which is a ThreadPool thread. But I haven't tested this.
You could try this:
public static Task<SplashForm> ShowSplash()
{
var tcs = new TaskCompletionSource<SplashForm>();
// Show the form in a new thread.
_splashThread = new Thread(() =>
{
var splashForm = new SplashForm();
tcs.SetResult(_splashForm);
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(splashForm);
});
_splashThread.IsBackground = true;
_splashThread.Start();
}
this would allow you to remove the static modifier from CloseSplash:
// Close the splash (Loading...) screen.
public void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
May be used like this:
var form = await SplashForm.ShowSplash();
form.CloseSplash();
I developing multithreading application with main form and another form in which progress is shown.
At first: I create ProgressForm in MainForm
Progress p=new Progress();
Second: I create new instance of class Model (whith all data in my app).
Model m = new Model();
And subscribe for event:
m.OperationStarted += new EventHandler(OnCopyStarted);
private void OnCopyStarted(object sender, EventArgs e)
{
p.Show();
}
Third: I run some operation in another thread where I change property in another Model
private bool isStarted;
public bool IsStarted
{
get{return isStarted;}
set
{
isStarted = value;
if (isStarted && OperationStarted != null)
{
OperationStarted(this, EventArgs.Empty);
}
}
}
My questoin is: Why Progress form is show not in Main Thread? How can I run it without lockups?
All UI operations must run on the main UI thread.
The OnCopyStarted method is being called on another thread, so it must switch to the UI thread before before showing the dialog.
You can use your form's BeginInvoke to switch to the UI thread. Such as:
void OnCopyStarted(object sender, EventArgs e)
{
p.BeginInvoke((Action) (() => p.Show()));
}
Try it :
var t = new Thread(() => {
Application.Run(new Progress ());
});
t.Start();