Is it possible to close a form while the constructor is executing (or simply to stop it showing at this stage)?
I have the following code:
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Close();
}
}
}
Which throws an ObjectDisposedException in Main(), here:
static void Main()
{
...
// Following line errors
Application.Run(new MyForm());
}
I’ve tried checking the result of MyForm like this:
static void Main()
{
...
MyForm frm = new MyForm();
if (frm != null)
{
// Following line errors
Application.Run(frm);
}
}
But that doesn’t seem to help. Can anyone tell me a way around this, please? Maybe a way to check the form to see if it still exists?
Calling Close from the constructor of the Form is not possible, as it will call Dispose on a Form that has not yet been created. To close the Form after construction, assign an anonymous event handler to the Load event that closes your Form before it is displayed for the first time:
public partial class MyForm : Form
{
public MyForm()
{
if (ShouldClose())
{
Load += (s, e) => Close();
return;
}
// ...
}
// ...
}
The only thing you could do it set a flag to close it in the constructor, and then closing it in the Shown event. Of course, if you're doing that, it makes sense to move the code to determine whether it should be closed there in the first place.
The following works well:
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Shown += new EventHandler(MyForm_CloseOnStart);
}
}
private void MyForm_CloseOnStart(object sender, EventArgs e)
{
this.Close();
}
}
When you call Close() on a form, internally it is disposing of the form and releasing any managed resources. When you do this:
Application.Run(new MyForm());
You'll likely get an ObjectDisposedException. What you need to do is set the Form's visibility through a property:
Application.Run(new MyForm() { Visible = false });
Just make sure you remove the call to Close() in the constructor, or even move the property assignment there too.
Can you make MyFunc static? and then do something like:
static void Main()
{
...
if (MyForm.MyFunc())
{
Application.Run(new MyForm());
}
}
this would essentially give you the same control over whether the form is going to be constructed or not?
I found adding a handler to the 'Load' event is better as this way the dialog is never displayed at all. With the 'Shown' event you might briefly see the dialog open and then close which may be confusing:
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Load += MyForm_CloseOnStart;
}
}
private void MyForm_CloseOnStart(object sender, EventArgs e)
{
this.Close();
}
}
I think it is not wise to close a form in the constructor. If you do this, users of your form wouldn't know whether to ShowDialog or not.
The following code would be quite normal use:
// in the parent form:
public void ShowMyForm()
{
MyForm form = new MyForm();
form.propertyA = ...;
from.propertyB = ...;
DialogResult dlgResult = form.ShowDialog(this);
ProcessDialogResult(dlgResult);
}
If you decided in the constructor whether the Form ought to be shown, you would have to add code after construction to decide whether to call ShowDialog or not and whether to Process the dialog result.
Furthermore, are you sure that changing the properties will never influence whether the form is to be shown or not? Also after future changes?
During construction the form is not shown / opened yet. So I'm afraid Close() doesn't do what you expect.
The neat method is to do the checks that you wanted to do in the constructor in the Form_Load. Add an event handler for form-load and do your checks in the event handler. Use the property DialogResult to indicate that you decided not to show the form.
private void FormMain_Load (object sender, EventArgs e)
{
if (FormShouldNotLoad())
{
this.DialogResult = System.Windows.Forms.DialogResult.Abort;
Close();
// Warning, this does not work, see below, (but we're almost there!)
}
}
The user of the code could check the result of the dialog:
// in the parent form:
public void ShowMyForm()
{
MyForm form = new MyForm();
form.propertyA = ...;
from.propertyB = ...;
DialogResult dlgResult = form.ShowDialog(this);
switch (dlgResult)
{
case System.Windows.Forms.DialogResult.Abort:
ProcessFormNotLoaded();
break;
case System.Windows.Forms.DialogResult.OK:
ProcessFormOk();
break;
// etc.
}
}
However, calling Close() in the event handler for form-load won't work, because Close() can only be called properly after Load is completed.
Therefore, instead of calling Close(), you should BeginInvoke the Close() function, so the Close function will be called after loading is done:
private void FormMain_Load (object sender, EventArgs e)
{
if (FormShouldNotLoad())
{
this.DialogResult = System.Windows.Forms.DialogResult.Abort;
// invoke the Close function after Load completed
this.BeginInvoke(new MethodInvoker( () => this.CancelLoading())
}
}
Environment.Exit(...) is working for me (without window flickering):
public partial class MyForm : Form
{
public MyForm()
{
if (weShouldClose)
{
Environment.Exit(0);
}
}
}
If you want your window to never be seen(no flickering windows that open for an instant and then disappear):
public new void Show()
{
if (MyFunc())
base.Show();
else
; // dispose or whatever
}
Though Show(...) has 2 overloads and ShowDialog(...) has 2 too.
Doesn't work for the main form that is opened via Application.Run(). But who would do that anyways? Except there is also a way to open main form without using Application.Run().
Related
I am developing a Client/server app.
In the Client app, I have a Main Form that is an MDI parent.
The Main Form has a Load event that creates a new instance of a Child Form and makes it visible.
This event also establishes Main as the MdiParent of Child.
The Child form is meant to be a sign-in screen.
From the child form, I create a reference to the parent, to be able to call methods from the parent.
However, upon executing the MdiParent.RequestConnection method, the GUI becomes stuck.
So I tried to execute the method from a Thread, but it is not accepting my declaration, even if I'm seemingly following the correct syntax.
I don't see what am I doing wrong. Please help
Main form
public partial class frmMainForm: Form
{
public frmMainForm()
{
InitializeComponent();
}
Thread runningClient;
public MyTcpClient client= new MyTcpClient ();
frmChildForm frmSignIn;
bool clientConnected;
private void frmMainForm_Load(object sender, EventArgs e)
{
clientConnected= false;
panelSidebar.Hide();
if(frmSignIn == null)
{
frmSignIn= new frmChildForm();
frmSignIn.MdiParent = this;
frmSignIn.Show();
}
}
public void TurnOnPanels()
{
panelSidebar.Visible = true;
panelSidebar.BringToFront();
}
public void RequestConnection(string username)
{
string serverRsp = client.Connect(username);
if(serverRsp.Equals("SUCCESS"))
{
MessageBox.Show("Signed In", "Welcome", MessageBoxButtons.OK, MessageBoxIcon.Information);
clientConnected = true;
frmSignIn.Close();
}
}
}
And my child form
public partial class frmChildForm : Form
{
frmMainForm frmParent;
Thread clientRunning;
public frmChildForm()
{
InitializeComponent();
frmParent= (frmMainForm)this.MdiParent;
}
private void frmSignIn_FormClosing(object sender, FormClosingEventArgs e)
{
frmParent= (frmMainForm)this.MdiParent;
frmParent.TurnOnPanels();
}
private void btnSignIn_Click(object sender, EventArgs e)
{
if (txtSignInUsername.Text.Equals(""))
{
MessageBox.Show("No empty fields.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
//This is where it fails
clientRunning= new Thread(new ParameterizedThreadStart(frmParent.RequestConnection);
//"No Overload for RequestConnection matches delegate ParameterizedThreadStart"
//If I try to include the parameter inside that call, I get a "waiting for method //name" syntax error message instead.
clientRunning.Start(txtSignInUsername.Text.ToUpper());
}
}
private void frmSignIn_Load(object sender, EventArgs e)
{
frmParent = (frmMainForm)this.MdiParent;
}
}
I also tried to do it from the main form by creating a thread inside RequestConnection, where it was supposed to execute client.Connect, but I got the same error.
Couple of things you need to fix
public void RequestConnection(object username) // changed parameter type
{
if (username == null)
throw new ArgumentNullException(nameof(username));
if (!(username is string))
throw new InvalidCastException("Expect string");// give proper message
string serverRsp = client.Connect(username.ToString());
if (serverRsp.Equals("SUCCESS"))
{
MessageBox.Show("Signed In", "Welcome", MessageBoxButtons.OK, MessageBoxIcon.Information);
clientConnected = true;
//this is require to solve cross-thread operation
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(delegate ()
{
frmSignIn.Close();
}));
else
frmSignIn.Close();
}
}
you need to get MdiParent in Form Load Event and remove from Constructor.
Your child form load event/ or use Parent changed Event
private void FrmLogin_Load(object sender, EventArgs e)
{
frmParent = (MainForm)this.MdiParent;
}
The ParametrizedThreadStart delegate actually takes an object as parameter.
So you must provide it a method that takes an object as parameter, not as string. In your method you will receive the object, check that it is a string then convert it to a string.
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.
I was wondering if anyone could offer some advice. I need to automate a process within my program that when fingerprint has been captured, it automatically goes to the next page through calling ShowNext() in the wizard.
public partial class AddFingerprintsPage : Neurotec.Samples.WizardPage
...
#region Scanner // Fingerprint Scanner
public void timer1_Tick(object sender, EventArgs e)
{
Timer timer1 = new Timer();
timer1.Interval = 1000;
timer1.start() //
{
Form2 f2 = new Form2();
f2.Show(); // Display Form2 which asks user to present finger..
}
if (capturing..)
{
blah blah capture finger..
timer1.stop()
}
}
....
public partial class WizardForm : Form
....
public void ShowNext() // In the wizard form, go to the next page
{
ShowPage(_currentPage + 1, false);
}
I am not sure what is the best away around this. I have tried making an if statement so when timer1.stop occurs in fingerprints then I call it in WizardForm and go to next page. But I am pretty sure I am not calling it correctly and even if I had I still have this error 'an object is required for the non-static field, method of property 'NeurotechSamples.AddFingerprintsPage.timer1'
public void ShowNext()
{
if(Neurotec.Samples.Fingers.AddFingerprintsPage.timer1.stop()) // Not sure what to call here?
ShowPage(_currentPage + 1, false);
}
Any help would be appreciated!
On the AddFingerprintsPage, add a static bool and call it "Stopped" and set the value to true when the timer stops:
public static bool Stopped = false;
if (capturing..)
{
blah blah capture finger..
timer1.stop()
Stopped = true;
}
Then you can say:
public void ShowNext()
{
if(Neurotec.Samples.Fingers.AddFingerprintsPage.Stopped)
{
ShowPage(_currentPage + 1, false);
}
}
The error that you are getting is saying that you need to create a new instance of Neurotec.Samples.Fingers.AddFingerprintsPage in order to reference timer1 because it is not static.
I have been searching for an answer to my particular problem for a while with no success.
I have a task in my program that takes a few seconds and I want to show a new form while that task is being done. The new form has a loadingbar and some text.
I need to show the new form parallel to the task otherwise the task will not start untill I close the new form.
This is the solution I have now:
private void loadingBar()
{
frmLoading frm = new frmLoading("Please wait while the database is being backed up", "This might take several days.");
frm.ShowDialog();
}
public void Backup()
{
Thread load = new Thread(new ThreadStart(loadingBar));
load.Start();
///Execute a task.
load.Abort();
}
So, this works OK but my question is: Wouldn't it be better to close the the form "frm" in the load-thread to make it stop?
You could do this a few ways...
1 - You could do as BendEg suggested and invoke you frmClose once you are ready
Something like;
Invoke(new Action(Close));
or
Invoke(new Action(() => frmMain.Close()));
2 - Or you could simply use a background worker;
The simplest way to demonstrate this would be to add a BackgroundWorker to your form, and use the events provided;
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
MessageBox.Show(#"Please wait while the database is being backed up", #"This might take several days.");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("Running"); //Execute a task
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("Ended"); //Dispose of any objects you'd like (close yor form etc.)
}
I hope this helps.
You can declare the form on Class-Level and later close it with an invoke.
MSDN-Windows Forms Invoke
Like this:
public class Class1
{
private Form myForm;
public Class1()
{
myForm = new Form();
}
public void DoSomeWork()
{
// ===================================================
// Do Some Work...
// ===================================================
myForm.Invoke(new MethodInvoker(this.Hide));
}
public void Hide()
{
myForm.Hide();
}
public void Backup()
{
myForm.ShowDialog();
Thread load = new Thread(new ThreadStart(DoSomeWork));
load.Start();
}
}
I think this can work for you.
void YourMethod()
{
WaitForm wf = new WaitForm();
Invoke(new PleaseWaitDelegate(Launch),wf);
bool val = BoolMethodDoWork();
Invoke(new PleaseWaitDelegate(Close), wf);
if(val)
{
MessageBox.Show("Success!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
return;
}
MessageBox.Show("Damn!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
delegate void PleaseWaitDelegate(Form form);
void Launch(Form form)
{
new System.Threading.Thread(()=> form. ShowDialog()).Start();
}
void Close(Form form)
{
form.Close();
}
I think this will help you (if i understood you right):
Parallel.Invoke(() => somemethod(), () =>
{
someothertaskmethod();
});
I placed methods as example to demonstrate 2 tasks running.
You nee to use the proper using statement using System.Threading.Tasks;
I hope that the title and this simple example says everything.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdateLabel(string str)
{
label1.Text = str;
MessageBox.Show("Hello");
}
private void buttonIn_Click(object sender, EventArgs e)
{
UpdateLabel("inside");
}
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod();
}
}
public class MyClass
{
public void MyMethod()
{
Form1 MyForm1 = new Form1();
MyForm1.UpdateLabel("outside");
}
}
When I'm trying to change lable1 from MyClass it does nothing. But I can get to the UpdateLable method from outside, it says Hello to me, it just doesn't change the label.
Use a delegate for setting your label
public class MyClass {
Action<String> labelSetter;
public MyClass(Action<String> labelSetter) {
this.labelSetter = labelSetter;
}
public void MyMethod() {
labelSetter("outside");
}
}
.
public void buttonOut_Click(object sender, EventArgs e) {
var outside = new MyClass(UpdateLabel);
outside.MyMethod();
}
a bit unsure because the example actually leaves some bits unclear... but here is a try:
public class MyClass
{
public void MyMethod(Form1 F)
{
F.UpdateLabel("outside");
}
}
this works as long as MyClass is NOT running on a different thread - otherwise the call to UpdataLabel must be synchronized with the UI thread...
EDIT:
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod(this);
}
Either go with Yahia's way (it has been updated and will work correctly) or try the following (probably overkill for what you're trying to do... whatever that is).
UPDATE:
Based on your comment in the question, you are also doing the work in MyClass on a different thread. Code change is below.
public partial class Form1 : Form
{
// keep a reference to a MyClass object for your Form's lifetime
private MyClass _myClass;
public Form1()
{
InitializeComponent();
// Intstantiate your MyClass object so you can use it.
_myClass = new MyClass();
// Register to the MyClass event called UpdateLabel.
// Anytime MyClass raises the event, your form will respond
// by running the UpdateLabelFromMyClass method.
_myClass.UpdateLabel += UpdateLabelFromMyClass;
}
private void button1_Click(object sender, EventArgs e)
{
// Call MyMethod in your MyClass object. It will raise
// the UpdateLabel event.
// update, you are calling this on a background thread?
_myClass.MyMethod();
}
void UpdateLabelFromMyClass(string message)
{
// Update your label with whatever message is passed in
// from the MyClass.UpdateLabel event.
// UPDATE: If called from a background thread you'll need this:
this.BeginInvoke( (Action) (()=>
{
label1.Text = message;
}));
}
}
public class MyClass
{
// An event that can be raised, allowing other classes to
// subscribe to it and do what they like with the message.
public event Action<string> UpdateLabel;
public void MyMethod()
{
// Raise the UpdateLabel event, passing "Outside" as
// the message.
UpdateLabel("Outside");
}
}
After wasting a ton of time on what should be a simple task and trying every answer on stack overflow, I said, if C# wants to make it stupid hard to change the text of a simple label, I am going to come up with a stupid fix.
Here is what you do:
In Form1 or whatever form has the label you want add:
public void setStatus()
{
lblStatus.Text = status;
}
public static string status;
Now, add a timer to Form1 and have it run "setStatus();" on every tick
Now, in any class, just write:
Form1.status = "Change label to this text";
you need to make both the method MyMethod and the label in question static. But if you do then you cannot access MyMethod through a new instance of the form instead you have to access it directly like Form1.MyMethod(). But if you do make the label the static visual studio will make it non-static one you access the label from the designer so you will have to keep making it static from form1.designer.cs. Also if you do make the label static change every line that refers to any of its properties so if it says this.label1.Text change it to label1.Text. This should give you the desired effect