My program has a main form where I keep a repository of values from other sub forms. For some reason the sub form is giving me an error:
an object reference is required for the non-static field
This is my main form:
public partial class frm_SystemLog : Form
{
public frm_SystemLog()
{
InitializeComponent();
}
public string TextBoxValue
{
// suppose to get value from other forms
get { return this.textBox1.Text; }
set { textBox1.Text = value; }
}
private void frm_SystemLog_Load(object sender, EventArgs e)
{
Log frm_LoginMenu = new Log();
frm_LoginMenu.ShowDialog();
}
}
This is my sub form:
public partial class Log : Form
{
public Log()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
// this is where the error happens
frm_SystemLog.TextBoxValue = "SomeValue";
this.Close();
}
}
you should create a property in your log form then set its value when you are accessing it
//in log form
public String MyValue{get;set;}
then in properties of your button2 of log form choose dialogresult and set it to ok
then in your button2 click event
private void button2_Click(object sender, EventArgs e)
{
MyValue = "SomeValue";
//no need to close ,dialogresult will do it...
}
then in your frm_SystemLog form do this
private void frm_SystemLog_Load(object sender, EventArgs e)
{
Log frm_LoginMenu = new Log();
frm_LoginMenu.ShowDialog();
if(frm_LoginMenu.ShowDialog() == DialogResult.OK)
{
this.TextBoxValue = frm_LoginMenu.MyValue;
}
}
this should solve your problem.
frm_SystemLog.TextBoxValue isn't accessible from button2_Click, because it's in a different class.
Currently you're trying to reference an object on your parent form class, not an instance of your class. In that case, the only objects you can reference are static ones, hence the error you're getting.
You need an actual reference to the instance of your parent form. Change your Log class as follows:
public partial class Log : Form
{
private frm_SystemLog parentForm;
public Log(frm_SystemLog parentForm)
{
InitializeComponent();
this.parentForm = parentForm;
}
...
...
Then instantiate your sub form using:
Log frm_LoginMenu = new Log(this);
Read "Understanding Classes, Methods, and Properties in C#" for more information, in particular:
There are two kinds of methods in C#. They are:
Instance Method
Static Method
Instance Methods are methods declared outside the main method and can be accessed only by creating an object of the corresponding class.
Class methods also are declared outside the main method but can be accessed without creating an object of the class. They should be declared with the keyword static and can be accessed using the classname.methodname syntax.
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.
I have seen a few links on attempts at this but I haven't found a solution. I am attempting to access my form textbox and update it with text from another class. I can update the text within my DataOrganizerForm class directly but when I pass text back to the DataOrganizerForm class then it doesn't update on the GUI. Here is what I have:
public partial class DataOrganizerForm : Form
{
//Default constructor
public DataOrganizerForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
//Handle a Start/Stop button click
private void start_stop_button_Click(object sender, EventArgs e)
{
SerialNumberSearcher snsearch = new SerialNumberSearcher();
snsearch.searchSN();
}
//Allow simple access to update to notification textbox
public void setNotificationText(string text)
{
notification_textbox.Text = text;
}
}
public class SerialNumberSearcher
{
public void searchSN()
{
DataOrganizerForm formAccess = new DataOrganizerForm();
formAccess.setNotificationText("Updated text from different class");
}
}
Well, it won't update the textbox 'cause you're instantiating another object of the DataOrganizerForm class. What you could do is passing a reference of the form object to the SerialNumberSearcher, like this:
public class SerialNumberSearcher
{
private readonly DataOrganizerForm _form;
public SerialNumberSearcher(DataOrganizerForm form)
{
_form = form;
}
public void searchSN()
{
_form.setNotificationText("Updated text from different class");
}
}
You need to understand at which instance you operate. When you use the new-eperator you create a new instance, like a new copy of that type.
DataOrganizerForm formAccess = new DataOrganizerForm();
The original Form is a different instance then the one you create in the searchSN method.
You need to pass that instance into the method to manipulate it:
public void searchSN(DataOrganizerForm formAccess )
{
formAccess.setNotificationText("Updated text from different class");
}
When you want to call this method you need to use this to reference the current object :
private void start_stop_button_Click(object sender, EventArgs e)
{
SerialNumberSearcher snsearch = new SerialNumberSearcher();
snsearch.searchSN(this);
}
this will access the current instance of the Form, thereby allowing you to manipulate the textbox that you are interested in.
When do you use the “this” keyword? might also be helpfull
Thanks for the help. This is what I was able to do to make my application work. I passed the Textbox object by reference to my other class and was able to display my information that way. Also, I had issues getting my text box to continuously update. I had to add
public partial class DataOrganizerForm : Form
{
//Default constructor
public DataOrganizerForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
//Handle a Start/Stop button click
private void start_stop_button_Click(object sender, EventArgs e)
{
SerialNumberSearcher snsearch = new SerialNumberSearcher();
snsearch.searchSN(notification_textbox);
}
//Allow simple access to update to notification textbox
public void setNotificationText(string text)
{
notification_textbox.Text = text;
notification_textbox.Update();
}
}
public class SerialNumberSearcher
{
public void searchSN(Textbox notifyTextbox)
{
notifyTextbox.setNotificationText = "Updated text from different class";
notifyTextbox.Update();
}
}
I apologize in advance with what will probably be a fairly easy/quick answer based on scope, but I've looked everywhere and am surprised to not come up with an answer.
I have created a class called Soldier with roughly 100 class variables. I need a user to enter information and gradually build a Solider object over the course of several different class Forms (because there is too much data to collect on just one).
I create an (empty) instance of a Soldier (tempSoldier) in Form1.cs and start to set the object's class variables that I collect from the user.
private void button1_Click(object sender, EventArgs e)
{
Soldier tempSoldier = new Soldier();
tempSoldier.surname = textbox1.text;
}
My question: how do I gain access to the object instance (tempSoldier) from Form1.cs in the other classes (Form2.cs, Form3.cs ...)?
I should mention that all of the Forms (Form1.cs, Form2.cs ...) share the same namespace.
Thanks in advance
Edit: All solutions below work so it just depends upon which one you like the best. Thank you for your feedback.
One little note, make sure you make ALL of the class modifiers Public including your custom class (in my case Soldier.cs).
You'll need to declare the Soldier instance in in a higher scope.
One way of doing this would be to declare it inside Form1, then pass it to Form2, and so on.
public class Form1
{
private Soldier tempSoldier = new Soldier();
private void button1_Click(object sender, EventArgs e)
{
tempSoldier = new Soldier();
tempSoldier.surname = textbox1.text;
}
private void GotoNextStep()
{
// pass the existing instance to the next form
Form2 form2 = new Form2(tempSoldier);
// display form 2 ...
}
}
Another possibility is to use a singleton variable in a class that all the forms have access to.
public class MyAppManager
{
private static readonly Soldier _soldier = new Solider();
public static Soldier SoldierInstance
{
get { return _soldier; }
}
}
private void button1_Click(object sender, EventArgs e)
{
MyAppManager.SoldierInstnace.surname = textbox1.text;
}
The former approach is ok if there's a distinct sequence to the forms; the latter is better if difference forms could be used at different times or revisited.
You can also make Soldier a static variable :
public static Soldier soldier {get;set;}
private void button1_Click(object sender, EventArgs e)
{
soldier = new Soldier();
soldier.surname = textbox1.text;
}
Code in other forms:
Form1.soldier.name ="";
You should create a public property on your form that exposes the soldier. You can then access this property from the other forms.
// ...
public Soldier Soldier { get; private set; }
private void button1_Click(object sender, EventArgs e)
{
Soldier tempSoldier = new Soldier();
tempSoldier.surname = textbox1.Text;
this.Soldier = tempSoldier;
}
// ...
Your Form2 class could look something like this:
public partial class Form2
{
private Form1 form1;
public Form2(Form1 form1)
{
this.form1 = form1;
this.InitializeComponent();
}
public void DoStuffWithForm1()
{
// ...
string surname = this.form1.Soldier.surname;
// ...
}
}
In your other class, create a method with objects as parameters.
public class TryMe (TextBox newTextbox) {
newTextbox.Text = "Hello this is a text."
//You can also get the value of textbox of another form
var textString = newTextbox.Text;
}
And then in your main form, call that method with your objects as parameters. In this case, add textbox1 to your method's parameter.
Your code in form:
TryMe(textbox1);
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 ;)