i want when i click on button1 on form1 to call button2 that is on form2 and execute the code that is under button2 event.
The following code won't work:
button2.PerformClick();
The error i get is "button2 does not exist in current context", so i tried to set modifiers to public, also click event to set on public void... no luck.
form2.button2.PerformClick(); also doesn't work.
You should put the code that you want to call into a public method on Form2 and then call that method from form1.
If you need a specific instance of form2 to call the method from then you could store the Handle property from form2 somewhere and then get the appropriate form as follows.
var myForm = Form.FromHandle(myForm2Handle);
myForm.MyPublicMethod();
You could then call this from the Button1 click event.
You've got an architecture problem if you're reaching between forms and executing button click event code. You should really have an event system in place for this.
I'd suggest that Form2 would have a listener set up for an event on Form1:
public class Form2{
public Form2{
// get your form instance
Form1 MyForm1Instance = new Form1();
// hook up the event
MyForm1Instance.SomeEvent += new EventHandler(MyHandler);
}
public void MyHandler(){
// handle event here; run your button_click code or whatever
}
}
...and Form1 would simply need to fire "SomeEvent" when you click the appropriate button.
try this and see if it works, it works for me.
in the first form create your method
public void DoSomething(parameters)
{
//code to handle those parameters
}
in the calling or second form use this in the event you want to call form1 from
Form1 f1 = new Form1();
try
{
f1.DoSomething(arguments);
}
catch (Exception)
{
catch the exceptions
}
f1.Show();
comment if it works for u and mark it as an answer.
I think your problem is that you are trying to call instance method without object creation. Here is sample code to demostrate to you possible way of method calling:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new TestForm1());
}
}
public partial class TestForm1 : Form
{
private System.Windows.Forms.Button button1;
public void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello! Im TestForm1 and Im going to call TestForm2's code!");
// You must create TestForm2 because of button1_Click is not a static method!!!
TestForm2 form2 = new TestForm2();
form2.button1_Click(this, new EventArgs());
}
public TestForm1()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(88, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
this.Controls.Add(this.button1);
this.Name = "TestForm1";
this.Text = "TestForm1";
this.ResumeLayout(false);
}
}
public partial class TestForm2 : Form
{
private System.Windows.Forms.Button button1;
public void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello! Im TestForm2");
}
public TestForm2()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(88, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
this.Controls.Add(this.button1);
this.Name = "TestForm2";
this.Text = "TestForm2";
this.ResumeLayout(false);
}
}
}
Related
My application has a need to hide an initial form and open any one of four new forms. I can hide the initial and open the selected form with:
private void btn_Option1_Click(object sender, EventArgs e){
Visible = false;
Application x = new Application();
x.show();
}
My question is how to close the second form and reopen the original form? Or I suppose it would be plausible to close each form on each form opening, but that seems wasteful.
It sounds like you need the FormClosed event.
When you create instances of the new Form from your initial Form you can subscribe to the new Form's FormClosed event and show your initial Form from the handler.
This way, whenever one of your new Forms close, the event handler will fire and your initial Form will become visible again.
// This is a method that you would add to your initial Form.
private void SubForm_Closed(object sender, FormClosedEventArgs e)
{
Visible = true;
}
private void btn_Option1_Click(object sender, EventArgs e)
{
Visible = false;
Application x = new Application();
x.FormClosed += SubForm_Closed;
x.show();
}
If you use Hide(), the form "essentially" disappears, you won't even see it in the taskbar. Then you can open by Showing it from one of the subsequent forms. I threw the below together and only tested it quickly so YMMV, you'll need to clean it up and make it work for you, but it should illustrate the point. Create a Windows Form Application. Call it "WindowsFormsApp2" since that's what I used. Paste in the below code:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Form2 form2;
Form3 form3;
Form4 form4;
Form5 form5;
private void button1_Click_1(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
form2 = new Form2(this);
form2.Show();
}
if (checkBox2.Checked)
{
form3 = new Form3(this);
form3.Show();
}
if (checkBox3.Checked)
{
form4 = new Form4(this);
form4.Show();
}
if (checkBox4.Checked)
{
form5 = new Form5(this);
form5.Show();
}
this.Hide();
}
}
}
Paste the below code into the Form1 designer:
namespace WindowsFormsApp2
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.checkBox2 = new System.Windows.Forms.CheckBox();
this.checkBox3 = new System.Windows.Forms.CheckBox();
this.checkBox4 = new System.Windows.Forms.CheckBox();
this.button1 = new System.Windows.Forms.Button();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.Controls.Add(this.checkBox4);
this.groupBox1.Controls.Add(this.checkBox3);
this.groupBox1.Controls.Add(this.checkBox2);
this.groupBox1.Controls.Add(this.checkBox1);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(274, 73);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Forms";
//
// checkBox1
//
this.checkBox1.AutoSize = true;
this.checkBox1.Location = new System.Drawing.Point(17, 32);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(55, 17);
this.checkBox1.TabIndex = 0;
this.checkBox1.Text = "Form2";
this.checkBox1.UseVisualStyleBackColor = true;
//
// checkBox2
//
this.checkBox2.AutoSize = true;
this.checkBox2.Location = new System.Drawing.Point(78, 32);
this.checkBox2.Name = "checkBox2";
this.checkBox2.Size = new System.Drawing.Size(55, 17);
this.checkBox2.TabIndex = 1;
this.checkBox2.Text = "Form3";
this.checkBox2.UseVisualStyleBackColor = true;
//
// checkBox3
//
this.checkBox3.AutoSize = true;
this.checkBox3.Location = new System.Drawing.Point(139, 32);
this.checkBox3.Name = "checkBox3";
this.checkBox3.Size = new System.Drawing.Size(55, 17);
this.checkBox3.TabIndex = 2;
this.checkBox3.Text = "Form4";
this.checkBox3.UseVisualStyleBackColor = true;
//
// checkBox4
//
this.checkBox4.AutoSize = true;
this.checkBox4.Location = new System.Drawing.Point(200, 32);
this.checkBox4.Name = "checkBox4";
this.checkBox4.Size = new System.Drawing.Size(55, 17);
this.checkBox4.TabIndex = 3;
this.checkBox4.Text = "Form5";
this.checkBox4.UseVisualStyleBackColor = true;
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 91);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(199, 23);
this.button1.TabIndex = 1;
this.button1.Text = "Open Selected Forms";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click_1);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.button1);
this.Controls.Add(this.groupBox1);
this.Name = "Form1";
this.Text = "Form1";
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.CheckBox checkBox4;
private System.Windows.Forms.CheckBox checkBox3;
private System.Windows.Forms.CheckBox checkBox2;
private System.Windows.Forms.CheckBox checkBox1;
private System.Windows.Forms.Button button1;
}
}
Add 4 Windows Forms: Form2, Form3, Form4 and Form5.
Paste in the below code to each and just change the form name on each to it's correct name (Form3, Form4, Form5):
using System;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form2 : Form
{
public Form2(Form1 Form1In)
{
InitializeComponent();
form1 = Form1In;
}
Form1 form1;
private void button1_Click(object sender, EventArgs e)
{
form1.Show();
}
}
}
Finally paste in the below code to the designer of each form and change the name for the forms other than Form2 to their respective name:
namespace WindowsFormsApp2
{
partial class Form2
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(22, 21);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(104, 23);
this.button1.TabIndex = 0;
this.button1.Text = "Show Form1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.button1);
this.Name = "Form2";
this.Text = "Form2";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
}
}
Then run it.
Here's a shiny new wheel. It concentrates all the logic in a Manager class.
A SubForm Base Class
First, we'll create a base class for all the sub forms. This way, you don't need to put the form switching logic into every form.
Create a new Form in your project, name it ShowHideFormBase , change to the "View Code" view and change the boilerplate so it looks like this:
public partial class ShowHideFormBase : Form
{
public ShowHideFormManager Manager { get; private set; }
public ShowHideFormBase()
{
Manager = null;
InitializeComponent();
}
public void SetManager (ShowHideFormManager manager)
{
Manager = manager;
}
}
We'll create the Manager class in a bit
I really wanted to have a constructor on ShowHideFormBase that took a ShowHideFormManager instance as a parameter. The Windows Forms designer really doesn't like having forms base classes with non-default constructors. Such is life.
In the designer for this form, show the properties of the form, and change to the Events view (the lightening bolt icon). Double-click on the FormClosing event. That will add this code for a ShowHideWindowBase_FormClosing handler to your base class. Change it so it looks like this.
private void ShowHideWindowBase_FormClosing(object sender, FormClosingEventArgs e)
{
Debug.Assert(Manager != null); //Make sure that SetManager was called
e.Cancel = true;
Manager.HideAll();
}
The e.Cancel=true; says "I know someone tried to close this form, don't let it close". Instead, we'll cause all the sub forms to be hidden including this one.
That class is done. Now let's do the manager class.
The Manager
Create a new class in your project name ShowHideFormManager. Use this code:
public class ShowHideFormManager
{
private Dictionary<string, ShowHideFormBase> _forms = new Dictionary<string, ShowHideFormBase>();
private Form _mainForm;
public ShowHideFormManager(Form mainForm)
{
_mainForm = mainForm;
}
// This will add a sub form to be managed
public void Add(string windowsName, ShowHideFormBase form)
{
_forms.Add(windowsName, form);
}
// This will hide all managed sub forms and show the main form
public void HideAll()
{
_mainForm.Show();
foreach (var formPair in _forms)
{
formPair.Value.Hide();
}
}
// this will hide the main form and show the named sub form
public void Show(string formToShowName)
{
_mainForm.Hide();
foreach (var formPair in _forms)
{
if (formPair.Key != formToShowName)
{
formPair.Value.Hide();
}
}
if (_forms.TryGetValue(formToShowName, out var formToShow))
{
formToShow.Show();
}
}
}
Note that the shiny new wheel is up to about 75 or 80 lines of code.
The Application's Main Form
I'm assuming that you have a form named Form1 in your application (the one that gets created when you create your application.
Add this private field:
private ShowHideFormManager _manager = null;
and this public method:
public void SetManager(ShowHideFormManager manager)
{
_manager = manager;
}
Drop three buttons on the main form, set their Name properties to "SubForm1Btn", "SubForm2Btn", and "SubForm3Btn" and set the Text properties to something useful. In the click handlers do something like this for each button:
private void SubForm1Btn_Click(object sender, EventArgs e)
{
_manager.Show("Sub Form 1");
}
changing the numbers, obviously
The Sub Forms
Now create three new forms in your project. Name the classes SubForm1, SubForm2 and SubForm3.
Note, I create three sub forms, not 4 - creating the fourth would be easy
Open the code view for each form. The only change we are going to make to these three forms is to change the class declaration and constructor to:
public partial class SubForm1 : ShowHideFormBase
{
public SubForm1(ShowHideFormManager manager)
{
InitializeComponent();
SetManager(manager);
}
}
For all three forms: 1, 2, and 3, obviously
This changes the base class from Form to ShowHideFormBase. It also makes it so each of these forms must be initialized with a ShowHideFormManager instance, and we call SetManager (which is a base class method) to initialize the form with the manager.
Finally, changes to Program.cs
Finally, we'll make some minor changes to the Main method in Program.cs. These changes:
Create the Manager
Instantiate the main form as well as the three sub forms.
And call the normal Application.Run method
Here's the new Main method:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainForm = new Form1();
var manager = new ShowHideFormManager(mainForm);
mainForm.SetManager(manager);
var subForm1 = new SubForm1(manager);
var subForm2 = new SubForm2(manager);
var subForm3 = new SubForm3(manager);
manager.Add("Sub Form 1", subForm1);
manager.Add("Sub Form 2", subForm2);
manager.Add("Sub Form 3", subForm3);
Application.Run(mainForm);
}
Important: The strings you use in the manager.Add call (e.g. "Sub Form 1") must exactly match the string you pass in each of the calls to _manager.Show("Sub Form 1"); in the button handlers. When I finished this, I decided that I should have used an enum, but everything was working.
How it Works
Press F5 and the main form (with three buttons on it) should pop up. Press any of the buttons and the corresponding form will pop up (and the main form will hide). Close the sub form, and the main form will re-open letting you press that button again or another button.
While you are looking at this, notice that there are almost no changes to the Main Form or the Sub Forms. The magic is all in the form base class and the manager class. You can do whatever you want with any of the forms (as long as you get those minimal changes in the right places.
I have a Windows Forms App that is functioning in a way that I do not understand. I have created a simple app to reproduce the functionality to minimum logic for clarity. It has two forms.
Form1:
There is a button1 that when clicked adds rows to a datatable.
There is a button2 that displays a form and sends the datatable to the form.
Form2:
Form2 creates a dataview from the datatable and binds the dataview to a listbox. Form2 also has a button1 that does a this.Close() on the form. I read that anything opened with formName.Show() will be disposed of during .Close.
Here is where things are get weird. Form1 button1 can be clicked over and over each time clearing out the datatable and adds the rows again. Once from1 button2 is clicked and the form2 is displayed and then closed, going back to form1 and clicking button1 raises an error. The error is from the form2 (which has been closed) listBox1_SelectedIndexChanged event.
The question is, why is an event firing for a control that is gone on a form that is gone?
I have found several ways to avoid this but I am wanting to understand why it is happening such as setting the listbox datasource = null but would like to know what is going on.. Spent half a day trying to figure this out. SO community, please educate me here.
Form1 code
public partial class Form1 : Form
{
Boolean bInitialLoad = true;
DataTable dtHardware = new DataTable("Hardware");
Form2 multiServerView = new Form2();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
dtHardware.Clear();
if (bInitialLoad == true)
{
dtHardware.Columns.Add("ServerName", typeof(String));
dtHardware.Columns.Add("Environment", typeof(String));
}
DataRow drNewRow = dtHardware.NewRow();
drNewRow["ServerName"] = "SomeName";
drNewRow["Environment"] = "SomeEnvironment";
dtHardware.Rows.Add(drNewRow);
drNewRow = dtHardware.NewRow();
drNewRow["ServerName"] = "SomeName2";
drNewRow["Environment"] = "SomeEnvironment2";
dtHardware.Rows.Add(drNewRow);
bInitialLoad = false;
}
private void button2_Click(object sender, EventArgs e)
{
OpenMultiServerView();
}
private void OpenMultiServerView()
{
multiServerView = new Form2(dtHardware);
this.AddOwnedForm(multiServerView);
multiServerView.Show();
}
}
Form1 Designer
namespace WindowsFormsApp4
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(308, 390);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(107, 34);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(437, 296);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(102, 33);
this.button2.TabIndex = 1;
this.button2.Text = "button2";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(679, 482);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
}
}
Form2 code
public partial class Form2 : Form
{
DataView dvServers = null;
public Form2()
{
InitializeComponent();
}
public Form2(DataTable dt1)
{
InitializeComponent();
dvServers = new DataView(dt1);
}
private void Form2_Load(object sender, EventArgs e)
{
listBox1.DisplayMember = "ServerName";
listBox1.DataSource = dvServers;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
System.Windows.Forms.FormCollection fc = Application.OpenForms;
foreach (System.Windows.Forms.Form frm in fc)
{
if (frm.Name == "Form2")
{
return;
}
}
MessageBox.Show("I am the listbox event from Form2");
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
}
Form2 Designer
namespace WindowsFormsApp4
{
partial class Form2
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.listBox1 = new System.Windows.Forms.ListBox();
this.button1 = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// dataGridView1
//
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Location = new System.Drawing.Point(23, 113);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.Size = new System.Drawing.Size(749, 287);
this.dataGridView1.TabIndex = 0;
//
// listBox1
//
this.listBox1.FormattingEnabled = true;
this.listBox1.Location = new System.Drawing.Point(291, 31);
this.listBox1.Name = "listBox1";
this.listBox1.Size = new System.Drawing.Size(171, 69);
this.listBox1.TabIndex = 1;
this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged);
//
// button1
//
this.button1.Location = new System.Drawing.Point(653, 415);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(118, 25);
this.button1.TabIndex = 2;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.button1);
this.Controls.Add(this.listBox1);
this.Controls.Add(this.dataGridView1);
this.Name = "Form2";
this.Text = "Form2";
this.Load += new System.EventHandler(this.Form2_Load);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.Button button1;
}
}
It's because the instance of Form2 isn't actually gone. You are correct in assuming that it has been disposed, since you called Show, but it has not been collected by the garbage collector. In fact, it can't be collected because in the code example provided Form1 has two references to the instance of Form2 that it created.
The first is in Form1.OwnedForms. The second is the field Form1.multiServerView.
You said you had a few ways to fix it, such as breaking the binding when Form2 closes, but I thought I'd just throw out this suggestion. If you don't actually need to create a new instance of Form2 every time you show it, you could just create one instance of it in the constructor of Form1, handle Form2.Closing and just hide Form2 when the user closes it.
So, something like...
public partial class Form1 : Form
{
//Removed bInitialLoad, we'll set up the data table in the constructor.
DataTable dtHardware = new DataTable("Hardware");
Form2 multiServerView; //No longer initalizing here, we'll do that in the constructor.
public Form1()
{
InitializeComponent();
dtHardware.Columns.Add("ServerName", typeof(String));
dtHardware.Columns.Add("Environment", typeof(String));
multiServerView = new Form2(dtHardware);
AddOwnedForm(multiServerView);
}
private void button1_Click(object sender, EventArgs e)
{
dtHardware.Clear();
//Removed the check of bInitialLoad.
DataRow drNewRow = dtHardware.NewRow();
drNewRow["ServerName"] = "SomeName";
drNewRow["Environment"] = "SomeEnvironment";
dtHardware.Rows.Add(drNewRow);
drNewRow = dtHardware.NewRow();
drNewRow["ServerName"] = "SomeName2";
drNewRow["Environment"] = "SomeEnvironment2";
dtHardware.Rows.Add(drNewRow);
//Removed setting bInitialLoad.
}
private void button2_Click(object sender, EventArgs e)
{
OpenMultiServerView();
}
private void OpenMultiServerView()
{
multiServerView.Show(); //Just show it.
}
}
public partial class Form2 : Form
{
DataView dvServers = null;
//Removed the empty constructor since Form1 no longer needs it.
public Form2(DataTable dt1)
{
InitializeComponent();
dvServers = new DataView(dt1);
}
private void Form2_Load(object sender, EventArgs e)
{
listBox1.DisplayMember = "ServerName";
listBox1.DataSource = dvServers;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("I am the listbox event from Form2");
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form2_Closing(object sender, FormClosingEventArgs e)
{
//Don't forget to wire it up in the designer!
//If the user clicks "button1" or the "X" then just hide the form.
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
}
This way, there is only ever one instance of Form2 for Form1. You don't have to worry about instances of Form2 that are disposed but not collected yet triggering events when something on Form1 alters the data source.
I have a form Menu that won't fully dispose. Below is the complete form code. It's part of a larger system so other forms open and close before Menu is first opened.
There's a form timer that fires every second and prints whether the form is disposed or not. There's a button that opens another form, Search, and closes Menu. Search also has a timer that prints whether it is disposed or not.
When Menu opens, the debug output is as expected
*********** (in main menu): Disposed False
*********** (in main menu): Disposed False
When I click, I get timer ticks for both Menu and Search
*********** (in main): Disposed True
*************** (in search) Disposed False
It shows that the first instance of Menu is disposed but obviously the timer is still running. When I exit Search and Main is opened, there are now two Main timers running
*********** (in main): Disposed True
*********** (in main): Disposed False
I can keep doing this (click to open Search and exit) and the number of Main timers running keeps increasing. I'm perplexed. Here's the code for Main
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
namespace Gui
{
public partial class Menu : Form
{
private System.Windows.Forms.Timer timer1;
private Button button1;
private IContainer components;
public Menu()
{
InitializeComponent();
}
private void Menu_Load(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
Debug.Print("*********** (in main): Disposed {0}", IsDisposed);
}
private void button1_Click(object sender, EventArgs e)
{
var search = new Search();
search.Show();
Close();
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 1000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// button1
//
this.button1.Location = new System.Drawing.Point(11, 17);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(125, 32);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Menu
//
this.ClientSize = new System.Drawing.Size(282, 253);
this.Controls.Add(this.button1);
this.Name = "Menu";
this.Load += new System.EventHandler(this.Menu_Load);
this.ResumeLayout(false);
}
}
}
this.timer1 = new System.Windows.Forms.Timer(this.components);
It looks like you copy/pasted the content of a Designer.cs file of a form class. The InitializeComponent() method is certainly boilerplate. But you didn't do it right, you forgot to actually use the this.components member. Which exists for only one reason, disposing any components that the form class uses. Like timer1. It is automatic for any controls you drop on the form, they can be found back through the form's Controls member but components need extra help.
So don't just copy/paste InitializeComponent(), you must copy/paste the Dispose() method as well:
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
And the timer now stops ticking when you close the form.
I create a simple Windows Form with a text box, and Set button and a Toggle button. When I click the Toggle button, a thread is created setting text to the text box repeatedly. When I click the button again, the thread stops. When I click the Set button, a text is set to the text box once. Deadlock occurs if I do the following:
Run the app (in debug mode).
Click the Toggle button to let text run in the text box.
Click Set button. -> Deadlock occurs in this step.
Can you explain why and how deadlock occurs in this situation? How to avoid it?
Below is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace DeadLockTest
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace DeadLockTest
{
public class Form1 : Form
{
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private int counter;
private Thread thread;
private bool cancelRequested;
private string content;
private object lockKey = new object();
public Form1()
{
InitializeComponent();
}
protected void UpdateContent()
{
this.textBox1.Text = this.content;
}
protected void InvokeUpdateContent()
{
lock (this.lockKey)
{
if (InvokeRequired)
{
Invoke(new Action(UpdateContent));
}
else
{
UpdateContent();
}
}
}
protected void SetText(string text)
{
this.content = text;
InvokeUpdateContent();
}
protected void StressTest()
{
int localCounter = 0;
while (!this.cancelRequested)
{
SetText(string.Format("{0}", localCounter++));
}
this.cancelRequested = false;
this.thread = null;
}
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(12, 12);
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(260, 20);
this.textBox1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 38);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 1;
this.button1.Text = "Set";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(93, 38);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 2;
this.button2.Text = "Toggle";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
private void button1_Click(object sender, EventArgs e)
{
SetText(string.Format("{0}", this.counter++));
}
private void button2_Click(object sender, EventArgs e)
{
if (this.thread == null)
{
this.thread = new Thread(new ThreadStart(StressTest));
thread.Start();
}
else
{
this.cancelRequested = true;
}
}
}
}
Can you explain why and how deadlock occurs in this situation?
Sure...the deadlock occurs because of the lock()/Invoke() combination.
While the secondary thread is running and updating, it obtains a lock on the object. Then the secondary thread calls Invoke(), which is a synchronous call. It is key to realize that the secondary thread actually waits for the main UI thread to update the TextBox before continuing.
When you click the Set button, it attempts to update but must wait for the lock to be released by the secondary thread. At this point the main UI actually stops and freezes at the lock() line waiting for the secondary thread to release the lock. While waiting for the lock to be released, the main UI thread cannot process any messages.
But what is the secondary thread doing? It currently has the lock and is waiting for the main UI thread to service its synchronous Invoke() call. Since the main UI thread is waiting for the lock to be released, though, it cannot service any requests (including Invoke() requests) and bam...DEADLOCK! They are both waiting for each other.
How to avoid it?
Never use lock() from the main UI thread. In some scenarios, switching from Invoke() to BeginInvoke() can solve the problem since BeginInvoke() is asynchronous.
I'm trying to create my own error window for the project I am working on. When I show my error window I have no way to pass the error message, and user message to the Error window because the "ErrorMessage.Text" cannot be seen in the classes I make.
I went into the form designer generated code and tried to make the TextBox static, but that just breaks things. Can I make a TextBox public / static so I can change it from another form?
How do I make a TextBox Static Public so I can manipulate it across other forms, or is there another method for doing this?
EDIT:
Okay, for more information...
I have my own Form created. It is called "formErrorWindow." I need to display the form that i've pre-designed with the message set from another form. The only way I can do this is if I create a Function in the windows designer area for the form, and I set the variables with "this.errorMsg.text = error." The only way I can see that function is if I set it to static. If I set the function to Static, when I try and put "this.errorMsg.Text = error" I get this error: An object reference is required for the non-static field, method, or property.
Here is what I've attempted:
namespace LCR_ShepherdStaffupdater_1._0
{
partial class formErrorWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
public System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
///
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
// ********* HERE IS THE FUNCTION THAT IVE ADDED BELOW. THIS WOULD WORK BUT.... *********
public static void showError(string errorTitle, string usrMsg, string errorMsg)
{
formErrorWindow errorWindow = new formErrorWindow();
errorMsgItem.Text = errorMsg;
errorTitleItem.Text = "Error! : " + errorTitle;
usrMsgItem.Text = usrMsg;
errorWindow.ShowDialog();
}
// ********* HERE IS THE FUNCTION THAT IVE ADDED ABOVE. THIS WOULD WORK BUT.... *********
// ********* I get an error: "An object reference is required for the non-static field, method, or property." *********
public void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(formErrorWindow));
this.usrMsgItem = new System.Windows.Forms.TextBox();
this.errorTitleItem = new System.Windows.Forms.Label();
this.errorMsgItem = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.panel1 = new System.Windows.Forms.Panel();
this.label2 = new System.Windows.Forms.Label();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// usrMsgItem
//
this.usrMsgItem.Enabled = false;
this.usrMsgItem.Location = new System.Drawing.Point(13, 37);
this.usrMsgItem.Multiline = true;
this.usrMsgItem.Name = "usrMsgItem";
this.usrMsgItem.Size = new System.Drawing.Size(334, 81);
this.usrMsgItem.TabIndex = 0;
this.usrMsgItem.Text = "Undefined";
//
// errorTitleItem
//
this.errorTitleItem.AutoSize = true;
this.errorTitleItem.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.errorTitleItem.ForeColor = System.Drawing.Color.Red;
this.errorTitleItem.Location = new System.Drawing.Point(12, 9);
this.errorTitleItem.Name = "errorTitleItem";
this.errorTitleItem.Size = new System.Drawing.Size(152, 20);
this.errorTitleItem.TabIndex = 1;
this.errorTitleItem.Text = "Error! : Undefined";
//
// errorMsgItem
//
this.errorMsgItem.Enabled = false;
this.errorMsgItem.Location = new System.Drawing.Point(0, 21);
this.errorMsgItem.Multiline = true;
this.errorMsgItem.Name = "errorMsgItem";
this.errorMsgItem.Size = new System.Drawing.Size(329, 101);
this.errorMsgItem.TabIndex = 2;
this.errorMsgItem.Text = "Undefined";
//
// button1
//
this.button1.Location = new System.Drawing.Point(272, 256);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 3;
this.button1.Text = "Continue";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// panel1
//
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(128)))));
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.errorMsgItem);
this.panel1.Location = new System.Drawing.Point(12, 124);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(335, 126);
this.panel1.TabIndex = 4;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.Location = new System.Drawing.Point(68, 1);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(189, 17);
this.label2.TabIndex = 3;
this.label2.Text = "Technical Error Message";
//
// formErrorWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(192)))));
this.ClientSize = new System.Drawing.Size(359, 290);
this.Controls.Add(this.panel1);
this.Controls.Add(this.button1);
this.Controls.Add(this.errorTitleItem);
this.Controls.Add(this.usrMsgItem);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "formErrorWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Error!";
this.VisibleChanged += new System.EventHandler(this.formErrorWindow_VisibleChanged);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label2;
public System.Windows.Forms.TextBox usrMsgItem;
public System.Windows.Forms.Label errorTitleItem;
public System.Windows.Forms.TextBox errorMsgItem;
}
}
Look for the function I've added above. How do I get that to compile and do what I want it to do WITHOUT that error I keep getting: An object reference is required for the non-static field, method, or property.
I would just pass the message as a constructor parameter.
MyMessageBox messageBox = new MyMessageBox("My error message");
messageBox.Show();
Yes! have a function made public that can receive this text:
pseudo: public void updateTextBox(string new_text)
and have the function update the textbox from there.
don't mix UI with logic.
public partial class Form1 : Form
{
private static TextBox box;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// box
box = new TextBox();
box.Location = new System.Drawing.Point(87, 230);
box.Name = "box";
box.Size = new System.Drawing.Size(100, 20);
box.TabIndex = 1;
this.Controls.Add(box);
}
}
You can make the TextBox public first of.
Second make a static form(name of your form) variable and put the form there each time its loaded...
This is not a good solution in my eyes but without knowing more about your design its the best i can come up with...
Allowing public access to an object contained in your class may be a design. How about writing a property in the error window class called ErrorText and inside that property set the ErrorMessage.Text to the passed string? Or am I missing something?
If you really want to go with accessing the controls directly:
public Form2(Form1 form1)
{
InitializeComponent();
Owner = form1;
_form1 = form1;
}
now you can access the controls on form1 this way:
_form1.theForm1ControlName
You could create a method in the Error window's class which could be something like:
public void SetErrorText(string errorText)
{
this.ErrorMessage.Text = errorText; // assuming the TextBox' ID is ErrorMessage.
}
Sounds like you're on the right track by adding the public function you did, but you shouldn't have to make it static. As a general rule, if you have to go into the Designer Generated Code, something isn't right, so let's get out of there - it doesn't mean you can't add code to the form, obviously, but don't do it in that section :)
The error you're getting I believe originates from having a non-instantiated object (i.e. creating the error form like:
formErrorWindow myErrorWindow;
as opposed to instantiating it / creating an instance of it like so:
formErrorWindow myErrorWindow = new formErrorWindow();
You've made the function static, so you can see it from anywhere, however that function can't actually do anything because the form that it runs on doesn't exist yet until you use " = new formErrorWindow();".
So if I'm in a form called "MainForm" and I want to show an error, using the name of the function you provided it would be done like this:
formErrorWindow myErrorWindow = new formErrorWindow();
myErrorWindow.showError("Title", "User Message Here", "Error Message Here");
myErrorWindow.ShowDialog();
The key is to make sure nothing is static and then make sure you're initializing the formErrorWindow with = new formErrorWindow();
Pass the TextBox to the function/method like so
public static void myFunc(TextBox textBox1)
{
textBox1.Text = "do stuff";
{