Ok, so I have a method in one class and I'm trying to call it from another class.
Form1:
public void ChangeBack(Color clr)
{
this.BackColor = clr;
}
Form2:
public void ChangBackColor_Click(object sender, EventArgs e)
{
if (ColorDialog.ShowDialog() == DialogResult.OK)
{
Form1.ChangeBack(ColorDialog.Color);
}
}
But I need to make the ChangeBack method static to be able to call it. So:
Form1:
public static void ChangeBack(Color clr)
{
this.BackColor = clr;
}
But then I can't use "this." as the void doesn't allow it because it's static. And I can't create a new form1 because it needs to run overall in two windows.
Please help!C
When you are working with multiple forms, you need to pass a reference to the second form, so that it "knows" about the first form. To do this you will need to change the constructor of your second form, and add a private reference to that form, like so:
class Form2 : Form
{
//Variables
private Form1 _ParentForm; //Add this here
//Constructor
public Form2(Form1 parentForm)
{
InitalizeComponent();
_ParentForm = parentForm; //Add this here
}
}
The, when you create the second form on your main form, you can use this to pass the reference of itself on to the new form:
class Form1 : Form
{
public void ChangeBack(Color clr) //No longer needs to be static
{
this.BackColor = clr;
}
public void CreateSecondForm()
{
Form2 secondForm = new Form2(this);
secondForm.Show();
}
}
Then you can call any function on the parent form (i.e. Form1) from the second form like so:
public void ChangBackColor_Click(object sender, EventArgs e)
{
if (ColorDialog.ShowDialog() == DialogResult.OK)
{
//Access Form1's reference with _ParentForm instead of Form1
_ParentForm.ChangeBack(ColorDialog.Color);
}
}
If you work with 2 different WinForm windows you should simply pass a reference from one window to the other (typically using constructor). For instance:
var childForm = new ChildForm(this); // where this is your main WinForm
after that you can use the reference to the main WinForm to call it's methods.
That's correct, you cannot make the method static and access instance of the object.
Change BackColor to a static property or change your method to a non static one
Related
I have a main Form (Form1) with an UserControl (ucModule2).
ucModule2 contains a button (simpleButton1) which opens another Form (Form2).
On Form1, I have a button (UsrCtrlDialog) which should close Form2.
I have two scenarios:
Scenario 1:
My ucModule2.cs:
public partial class ucModule2 : UserControl
{
public static Form2 fr2 = new Form2();
private void simpleButton1_Click(object sender, EventArgs e)
{
//Form2 fr2 = new Form2();
fr2.Show();
fr2.TopMost = true;
textModule2 = textBox_ucModule2.Text;
}
}
Form1's button used to close Form2:
private void UsrCtrlDialog_Click(object sender, EventArgs e)
{
// Form2 fr2 = new Form2();
ucModule2.fr2.TopMost = false;
ucModule2.fr2.Close();
ucModule2.fr2.Dispose();
}
In this scenario, Form2 is opened only once and is closed when the UsrCtrlDialog button, from Form1, is closed. But if I want to open again Form2, I get, on fr2.Show():
System.ObjectDisposedException exception ('Cannot access a disposed
object.'`
I know that when I want to open again Form2, a new object of Form2 type isn't created.
What can I do to be able to open a new Form2 after the old one was closed?
Scenario 2:
My ucModule2.cs:
public partial class ucModule2 : UserControl
{
//public static Form2 fr2 = new Form2();
private void simpleButton1_Click(object sender, EventArgs e)
{
Form2 fr2 = new Form2();
fr2.Show();
fr2.TopMost = true;
textModule2 = textBox_ucModule2.Text;
}
}
In this scenario, I can open as many Form2 windows as I click on simpleButton1. For example, I press 3 times simpleButton1. I will have 3 Form2 windows.
How could close the 3 Form2 windows when I press UsrCtrlDialog button form Form1? How could I get the fr2 object from ucModule2.cs to Form1.cs?
In your code, you're treating the UserControl as a static class object, using it's Type to access Fields and Properties:
public partial class ucModule2 : UserControl
{
public static Form2 fr2 = new Form2();
// (...)
}
Then, in Form1:
ucModule2.fr2.TopMost = false;
// (...)
ucModule2 is the same name of the UserControl Type, so you're trying to use the Type to set Fields/Properties that belong to an Instance of that Control.
If you added an Instance of ucModule2 to the Form, then the Designer would have renamed the first Instance of the UC to ucModule21.
As usual, adding an index value (1, if it's the first instance of that Type) to the name of the Type created.
You need to use the Instance member of that UserControl (or any other control), not its Type.
Some documentation on the subject:
Inheritance (C# Programming Guide)
Members (C# Programming Guide)
Classes and structs have members that represent their data and
behavior. A class's members include all the members declared in the
class, along with all members (except constructors and finalizers)
declared in all classes in its inheritance hierarchy...
Static Classes and Static Class Members
To create a non-static class that allows only one instance of itself
to be created, see:
Implementing Singleton in C#.
It's also quite important to follow a standard naming convention when assigning names to types. Most devs assume that a Type name uses the Pascal Case convention, while an instance of this type will be named using Camel Case convention, as in:
MyUserControl myUsrControl = new MyUserControl();
myUsrControl.Show();
You can also see the different Markup colors used for the two, here
In Form1:
Call the UserControl's SetForm() method from Form1 (its parent Form).
After that, Form1 can use the UserControl's public FormInstance property.
public partial class Form1: Form
{
// If an Instance of the UC has been added in the Form's Designer,
// use that instance reference instead
UCModule2 ucModule2 = new UCModule2();
private void Form1_Load(object sender, EventArgs e)
{
ucModule2.Location = new Point(100, 100);
this.Controls.Add(ucModule2);
ucModule2.SetForm(typeof(Form2));
}
private void UsrCtrlDialog_Click(object sender, EventArgs e)
{
ucModule2?.FormInstance?.Close();
}
}
In UCModule2 (Type renamed using proper case):
If the Form instance has been closed/disposed by the parent Form, recreate a new instance and reset the public FormInstance Property.
You can determine whether the Form's instance has been destroyed, testing:
FormInstance is null || FormInstance.IsDisposed
public partial class UCModule2: UserControl
{
public Form FormInstance { get; private set; }
public Form SetForm(Type formType)
{
if (this.FormInstance == null || this.FormInstance.IsDisposed) {
this.FormInstance = (Form)Activator.CreateInstance(formType);
}
return this.FormInstance;
}
private void simpleButton1_Click(object sender, EventArgs e)
{
if (this.FormInstance is null || this.FormInstance.IsDisposed) {
this.SetForm(FormInstance.GetType());
}
this.FormInstance?.Show();
}
}
Handle a collection of different Form Types generated at run-time
If more than one form needs to be generated while this UserControl is active, we can add each new instance of a Form Type, determined by the UC's Parent Form, to a List. Then Dispose of each Form instance in the List when the Parent Form decides to do it and/or when the UserControl itself is destroyed:
The Parent Form can call the SetForm(Type formType) public method, setting a Form type to be generated. Then call the CloseAllForms() public method to close them all when needed. The UC calls the same method when its handle is being destroyed, to remove existing Form instances (if required).
The Form change the Form Type simply calling SetForm() with another type:
ucModule2.SetForm(typeof(Form2));
// (... and after...)
ucModule2.SetForm(typeof(Form3));
The UC's Button will generate the new Type of Form specified.
In Form1:
public partial class Form1: Form
{
// If an Instance of the UC has been added in the Form's Designer,
// use that instance reference instead
UCModule2 ucModule2 = new UCModule2();
private void Form1_Load(object sender, EventArgs e)
{
ucModule2.Location = new Point(100, 100);
this.Controls.Add(ucModule2);
ucModule2.SetForm(typeof(Form2));
}
private void UsrCtrlChangeType_Click(object sender, EventArgs e)
{
ucModule2.SetForm(typeof(Form3));
}
private void UsrCtrlDialog_Click(object sender, EventArgs e)
{
ucModule2.CloseAllForms();
}
}
In UCModule2:
public partial class UCModule2: UserControl
{
List<Form> formsCollection = null;
public UCModule2()
{
InitializeComponent();
formsCollection = new List<Form>();
}
private Type FormType { get; set; }
// Check whether the new type is different before setting the property,
// in case the FormType property has an explicit setter.
public void SetForm(Type formType)
{
if (this.FormType != formType) {
this.FormType = formType;
}
}
public void CloseAllForms()
{
if (formsCollection != null && formsCollection.Count > 0) {
for (int i = formsCollection.Count - 1; i >= 0 ; i--) {
formsCollection[i].Dispose();
}
}
}
protected override void OnHandleDestroyed(EventArgs e)
{
CloseAllForms();
base.OnHandleDestroyed(e);
}
private void btnShowForm_Click(object sender, EventArgs e)
{
if (FormType == null) return;
var instance = (Form)Activator.CreateInstance(FormType);
formsCollection.Add(instance);
instance.Show();
}
i have a main form with a function that changes the text of a text box thats on the main form, the code is below:
main form function:
public void consoleLog(string message)
{
txtConsoleLog.Text += Environment.NewLine;
txtConsoleLog.Text += message;
txtConsoleLog.SelectionStart = txtConsoleLog.TextLength;
txtConsoleLog.ScrollToCaret();
txtConsoleLog.Refresh();
}
So now i open a new form called frm connect when i click a button like this:
private void connectToolStripMenuItem_Click(object sender, EventArgs e)
{
Form frmConnect = new FrmConnect(this);
frmConnect.Show();
}
this is the frmConnect below
public partial class FrmConnect : Form
{
private Form mainForm;
public FrmConnect(Form frmMain)
{
this.mainForm = frmMain;
InitializeComponent();
}
private void btnConnect_Click(object sender, EventArgs e)
{
FrmMain.Connected = true;
mainForm.consoleLog("Connected");
}
}
So when i click a button i want to call the function but its saying it doesnt contain a definition for it, also im trying to change the 'Connected' variable thats on the main form which works by just referencing the FrmMain but am i able to do that using mainForm.Connected = true?
If i change the function to public static, it will work by referencing FrmMain but then i get errors with the txtConsoleLog as i cant reference an object in a non static method or something like that, any help is appriciated
When you pass the form into your constructor, and store it as the private member variable, in both places you declare it of the base type Form. In order to use a method on the type that you defined, your parameter and variable should be of type FrmMain.
public partial class FrmConnect : Form
{
private FrmMain mainForm;
public FrmConnect(FrmMain frmMain)
{
this.mainForm = frmMain;
InitializeComponent();
}
private void btnConnect_Click(object sender, EventArgs e)
{
FrmMain.Connected = true;
mainForm.consoleLog("Connected");
}
}
You should change
private Form mainForm;
public FrmConnect(Form frmMain)
{
To
private FrmMain mainForm;
public FrmConnect(FrmMain frmMain)
{
which will later give you access to all of the public properties on FrmMain in your other methods in the FrmConnect class.
This question already has answers here:
Communicate between two windows forms in C#
(12 answers)
Closed 6 years ago.
hey everyone I am currently trying to refresh a form once changes are done on a second. On my first form I press a button "create" that will open another form, form2. This second form will have input fields and allows you to input values that populate comboboxes on the first form. On the second form there is a button "update" I would like the fist form to refresh once update is pressed on the first.
I know there is this.refresh();, but I'm not sure if this is useful for me. I am trying to something along the lines of:
On form2 -
Private void Form2UpdateButton_Click
{
//do update stuff
Form1_load.Refresh();
}
or maybe
private void Form2UpdateButton_Click
{
//do update stuff
Form1.close();
Form1.Open();
}
I am still pretty new to C# and interacting 2 forms together is a rather complex concept to me so please let me know if I am going about this the wrong way. My refresh may be in the wrong spot, but I think this is what I want.
Create an own event on form2 that triggers when the button gets clicked. This way you can just form2.OnUpdateClicked += yourMethod. Like this:
public partial class Form1 : Form
{
private void CreateForm2()
{
Form2 frm2 = new Form2();
// Hook the event of form2 to a method
frm2.PropertyUpdated += Form2Updated;
}
private void Form2Updated(object sender, EventArgs e)
{
// this will be fired
}
}
public partial class Form2 : Form
{
// On form2 create an event
public event EventHandler PropertyUpdated;
private void Form2UpdateButton_Click()
{
// If not null (e.g. it is hooked somewhere -> raise the event
if(PropertyUpdated != null)
PropertyUpdated(this, null);
}
}
Recommendations:
Your second form should be created with a reference to first one, ie,
Form1:
public void RefreshParameters()
{
// ... do your update magic here
}
private void openForm2(..)
{
// Pass your current form instance (this) to new form2
var aForm = new Form2(this);
aForm.Show(); // show, run, I don't remember... you have it
}
Form2:
// Here you will store a reference to your form1
form1 daddy = null;
// A new constructor overloading default one to pass form1
public Fomr2(Form1 frm):base()
{
daddy = frm; // Here you store a reference to form1!
}
public void UpdateDaddy()
{
// And here you can call any public function on form1!
frm.RefreshParameters();
}
One way is to pass a reference of Form1 to Form2, like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonLaunchForm_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.LauncherForm = this;
form2.Show();
}
public void RefreshFormData()
{
// Refresh
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public Form1 LauncherForm { set; get; }
private void buttonUpdate_Click(object sender, EventArgs e)
{
// do your normal work then:
LauncherForm.RefreshFormData();
}
}
The above technique is called "Property-Injection";
Well here is the thing, I have MainForm that call OrderForm.Show().
Now I want to call public function in MainForm with a button, but I cant.
Here is the code in MainForm:
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
// Main form
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder();
frmO.Show();
}
public void Refresh()
{
// some action
}
}
And in OrderForm I do this:
public partial class FormOrder : Form
{
public FormOrder()
{
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
Form form = Form.ActiveForm as frmMain;
if (form != null)
{
form.Refresh();
}
}
}
so in the Program I run MainForm.Show() then load OrderForm. Then I when I click Copy button, it will run Public Function Refresh in MainForm. But I cant make it work, it always return null in:
Form form = Form.ActiveForm as frmMain;
so how can I actually get the active form, is it another solution? or I get it wrong??
Thanks in advance for the answer :)
You're getting a null because your active form is the one you're in and it is not of type frmMain. (I think you may actually be confusing an active form for a parent form?)
There are so may ways to do this. You could make your frmMain a singleton but that's weird and ugly and not recommended of late or you could pass a reference of it to its children somehow. Here's one, simple way:
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
// Main form
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder(this); // pass a ref of self
frmO.Show();
}
public void Refresh()
{
// some action
}
}
And in OrderForm:
public partial class FormOrder : Form
{
private frmMain _parent; // create a field that refers to the parent
public FormOrder(frmMain parent) // mod the constructor
{
if (parent == null) throw new NullReferenceException("Can't be NULL!!!"); // check clause
_parent = parent; // assign the ref of the parent
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
_parent.Refresh(); // make the call to parent
}
}
You could make this better by using an interface but the general idea would be the same.
I would not suggest you take the route to access the active form to accomplish your target. What you need to do is to pass a reference of the MainForm to the OrderForm, so OrderForm could use the reference to call any method on the MainForm.
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder();
frmO.MainForm = this;
frmO.Show();
}
And in FormOrder class you should add a new attribute MainForm, and use the attribute to reference the calling form :-
public partial class FormOrder : Form
{
public Form MainForm;
public FormOrder()
{
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
if (MainForm != null)
{
MainForm.Refresh();
}
}
}
You need to have a reference to the mainForm and/or mainForm's button click event that lives outside of mainForm. You can do this is in various ways. Create a static class that has a reference to mainForm, create a static class that has a delegate to the mainForm click event, pass mainForm into the contructor for childForm, pass in a delegate to mainForms click event to the the constructor for the childForm. Create a static method that handles the click event then use that from within childForm.
It really all depends upon what you want to to do, what you need to do, and how you desire to do it.
While the other answers are absolutely correct, I'd like to suggest a much easier yet "dirty" way to do it.
Every component has a Tag property where you can store any user-defined value or reference in. The idea is to save a reference to the main form in the second form's Tag, and access it from the button click event handler.
The good thing is that you do not have to write a lot of code, or create any data structure.
The dirty thing about it is that neither the framework at runtime nor the compiler at compile-time have any idea what you store in .Tag, and since it is not typed correctly, you are doing a type cast, depending on you storing the correct value there beforehand.
If you store the wrong value there, you might have a hard time debugging the cause since the symptom does not signal where you put the wrong value into Tag. So wild Tag usuage in a team of programmers is a good thing if you want to give birth to many unexpected hard-to-detect-and-fix bugs :)
Also, there is only one Tag per component. If you need more than one user-defined value, you might end up creating a custom data structure which Tag then holds a reference to, but that probably is a bigger mess than all of the other suggestions.
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
// Main form
private void MainFrm_Load(object sender, EventArgs e)
{
FormOrder frmO = new FormOrder();
frmO.Tag=this;
frmO.Show();
}
public void Refresh()
{
// some action
}
}
public partial class FormOrder : Form
{
public FormOrder()
{
InitializeComponent();
}
private void ShowForm()
{
// some action
}
private void btnCopy_Click(object sender, EventArgs e)
{
Form form = Tag as frmMain; // form now references the main form
if (form != null)
{
form.Refresh();
}
}
}
A good example for a quick and dirty solution, with limitations so inherent that you usually identify them only when it is too late -- that's why I am telling in the first place ;)
I have two Form classes, one of which has a ListBox. I need a setter for the SelectedIndex property of the ListBox, which I want to call from the second Form.
At the moment I am doing the following:
Form 1
public int MyListBoxSelectedIndex
{
set { lsbMyList.SelectedIndex = value; }
}
Form 2
private ControlForm mainForm; // form 1
public AddNewObjForm()
{
InitializeComponent();
mainForm = new ControlForm();
}
public void SomeMethod()
{
mainForm.MyListBoxSelectedIndex = -1;
}
Is this the best way to do this?
Making them Singleton is not a completely bad idea, but personally I would not prefer to do it that way. I'd rather pass the reference of one to another form. Here's an example.
Form1 triggers Form2 to open. Form2 has overloaded constructor which takes calling form as argument and provides its reference to Form2 members. This solves the communication problem. For example I've exposed Label Property as public in Form1 which is modified in Form2.
With this approach you can do communication in different ways.
Download Link for Sample Project
//Your Form1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 frm = new Form2(this);
frm.Show();
}
public string LabelText
{
get { return Lbl.Text; }
set { Lbl.Text = value; }
}
}
//Your Form2
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private Form1 mainForm = null;
public Form2(Form callingForm)
{
mainForm = callingForm as Form1;
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
this.mainForm.LabelText = txtMessage.Text;
}
}
(source: ruchitsurati.net)
(source: ruchitsurati.net)
Access the form's controls like this:
formname.controls[Index]
You can cast as appropriate control type, Example:
DataGridView dgv = (DataGridView) formname.Controls[Index];
I usually use the Singleton Design Pattern for something like this http://en.wikipedia.org/wiki/Singleton_pattern . I'll make the main form that the application is running under the singleton, and then create accessors to forms and controls I want to touch in other areas. The other forms can then either get a pointer to the control they want to modify, or the data in the main part of the application they wish to change.
Another approach is to setup events on the different forms for communicating, and use the main form as a hub of sorts to pass the event messages from one form to another within the application.
It's easy, first you can access the other form like this:
(let's say your other form is Form2)
//in Form 1
Form2 F2 = new Form2();
foreach (Control c in F2.Controls)
if(c.Name == "TextBox1")
c.Text = "hello from Form1";
That's it, you just write in TextBox1 in Form2 from Form1.
If ChildForm wants to access the ParentForm
Pass ParentForm instance to the ChildForm constructor.
public partial class ParentForm: Form
{
public ParentForm()
{
InitializeComponent();
}
public string ParentProperty{get;set;}
private void CreateChild()
{
var childForm = new ChildForm(this);
childForm.Show();
}
}
public partial class ChildForm : Form
{
private ParentForm parentForm;
public ChildForm(ParentForm parent)
{
InitializeComponent();
parentForm = parent;
parentForm.ParentProperty = "Value from Child";
}
}
There is one more way, in case you don't want to loop through "ALL" controls like Joe Dabones suggested.
Make a function in Form2 and call it from Form1.
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public void SetIndex(int value)
{
lsbMyList.SelectedIndex = value;
}
}
public partial class Form1 : Form
{
public Form2 frm;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
frm=new Form2();
frm.Show();
}
private void button1_Click(object sender, EventArgs e)
{
frm.SetIndex(Int.Parse(textBox1.Text));
}
}
Here's also another example that does "Find and Highlight". There's a second form (a modal) that opens and contains a textbox to enter some text and then our program finds and highlights the searched text in the RichTextBox (in the calling form). In order to select the RichTextBox element in the calling form, we can use the .Controls.OfType<T>() method:
private void findHltBtn_Click(object sender, EventArgs e)
{
var StrBox = _callingForm.Controls.OfType<RichTextBox>().First(ctrl => ctrl.Name == "richTextBox1");
StrBox.SelectionBackColor = Color.White;
var SearchStr = findTxtBox.Text;
int SearchStrLoc = StrBox.Find(SearchStr);
StrBox.Select(SearchStrLoc, SearchStr.Length);
StrBox.SelectionBackColor = Color.Yellow;
}
Also in the same class (modal's form), to access the calling form use the technique mentioned in the #CuiousGeek's answer:
public partial class FindHltModalForm : Form
{
private Form2 _callingForm = null;
public FindHltModalForm()
{
InitializeComponent();
}
public FindHltModalForm(Form2 CallingForm)
{
_callingForm = CallingForm;
InitializeComponent();
}
//...