I have a windows form app with 2 forms, and I need to press a button in form one to go to form 2(this is done already) then form 2 will be able to create an object using the add customer method to add to the system. My question is:
1)if I create an Object in Form 2, how could other forms(form3,form4 etc.) have access to this object? As far as I have learned, I can only call the method through an object.
2)if I created an object in Form1, and other forms inherited from form 1, will this object still work in other forms?
3)Objects can be inhereited or not? is this a good practice in real world?
4) How to allow different forms using one object different method?
A static field or property as suggested in zdimension's answer is possible, of course, but it shouldn't be your first option. There are lots of ways to pass data between forms, and it depends on your application which one is best. For example, one way of doing it is:
class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public AirlineCoordinator Coordinator {get; set;}
...
}
class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public AirlineCoordinator Coordinator {get; set;}
private void Form1_Load(object sender, EventArgs e)
{
this.Coordinator = new AirlineCoordinator(...);
...
}
...
private void ShowForm2Button_Click(object sender, EventArgs e)
{
using(var form2 = new Form2())
{
form2.Coordinator = this.Coordinator;
form2.ShowDialog(this);
}
}
}
In this hypothetical example, Form1 has a button ShowForm2Button; clicking on this button shows Form2 using the same AirlineCoordinator as is used by Form1.
The usual way to make something available to "everyone" is to use a static field, like this:
public class GlobalStuff
{
public static MyType SomeVariable;
}
Here, the GlobalStuff obviously only ever contains global things, so you could consider making it static too to indicate it will never be instanciated.
Here's what MSDN say about it:
Use a static class as a unit of organization for methods not associated with particular objects. Also, a static class can make your implementation simpler and faster because you do not have to create an object in order to call its methods. It is useful to organize the methods inside the class in a meaningful way, such as the methods of the Math class in the System namespace.
Related
I'm new to C# so bear with me, I'm trying to add methods to the Forms class so that I can show and hide other forms from within a different form if that makes sense. In my extension I have:
namespace ExtensionMethods
{
public static partial class FormExtentsion : Form
{
public static void HideForm(this Form frm)
{
frm.Hide();
}
public static void UnhideForm(this Form frm)
{
frm.Show();
}
}
}
And in my project I have:
private void bnTrBack_Click(objects sender, EventArgs e)
{
Main.UnhideForm();
this.Close();
}
Where Main is my main form. Is there a way to have a form open/close another form? Any help is much appreciated!!
As #CEvenhuis pointed out in a question comment, you need an instance of the main form.
(And, you don't need extension methods at all. Never do—They just allow calling code to look and feel different. But, in this case, in the code you've shown, you are just giving pet names to well-known, existing methods.)
Anyway, in the "child" form, you could have a field to refer to the instance of the main form.
Form _parent;
And use it like
private void bnTrBack_Click(objects sender, EventArgs e)
{
_parent.Show();
Close();
}
Or, assuming the child is only ever a child of the main form,
Main _main;
private void bnTrBack_Click(objects sender, EventArgs e)
{
_main.Show();
Close();
}
The question would be how to set the field.
It can be set in a constructor:
ReadOnly Main _main;
And change the designer-generated code from:
public Child()
{
InitializeComponent();
}
to
public Child(Main main)
{
InitializeComponent();
_main = main;
}
Stepping back a bit, instead of hiding the main form, you could instead—if appropriate— show the child form as a modal dialog and receive back a simple result after it closes:
var result = new Child().ShowDialog();
In general, there are two things to evaluate concerning the options:
User experience: Are the controls presented to the users in a way they can understand and in a way that allows them to follow desired workflows?
Coupling between classes: Which classes depend on which classes and in what way? Is there a good plan for passing data and control?
For example after creating a new Windows Form project I have my class called Form1.cs and from that form I can simply start typing the name of a form control and it will auto populate the form control variable names and I am able to use them in the class. However I have other classes that need to be able to access these form control variables as well, but they are not accessible.
Make them public if they are going to be used in another assembly, or internal if they are going to be used in the same project. Making them static means you don't have to pass your Form1 into the other classes.
Example... Say your Form1 has a string that contains the text you display in the title bar. Making it internal static, like this:
internal static readonly string MsgBox_Title = " Best Application Evar!";
lets you access it from other classes like this:
Form1.MsgBox_Title
It doesn't have to be readonly; that's just an example I pulled from an old app...
If you don't want static variables, you'll have to pass in an instance of Form1.
public class SomeClass
{
private Form1 m_Form1;
public SomeClass(Form1 form1)
{
m_Form1 = form1;
}
private void someMethod()
{
string localValue = m_Form1.SomeMemberStringVariable;
}
}
It's a very contrived example, but hopefully you get the idea.
If you want to call the Refresh method from a class instantiated from Form1, you could use an event in the child class to notify Form1.
Example:
This Form1 has a button that I use to show a secondary form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnShowPopup_Click(object sender, EventArgs e)
{
PopupForm f = new PopupForm();
f.CallRefreshHandler += PopupForm_CallRefreshHandler;
f.ShowDialog();
}
private void PopupForm_CallRefreshHandler(object sender, EventArgs e)
{
Refresh();
}
}
The secondary form, "PopupForm", has a button that I use to raise an event that the Form1 is subscribed to, and lets Form1 know to call Refresh.
public partial class PopupForm : Form
{
public event EventHandler CallRefreshHandler;
public PopupForm()
{
InitializeComponent();
}
private void btnRaiseEvent_Click(object sender, EventArgs e)
{
EventHandler handler = CallRefreshHandler;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Hope this helps.
Create an object of that class & start using those variables like this
Form1 fm = new Form1();
string abc = fm.VAR;
Define a public property in your form.
public string MyProp { get; set; }
Form1 frm = new Form1();
frm.MyProp = "Value";
Or define the property as static to avoid having to instantiate Form1:
public static string MyProp { get; set; }
Form1.MyProp = "Value";
I ran into this issue recently. I was keeping some methods in a separate class. Maybe not a good design decision in my case, I'm not sure yet. And these methods sometimes needed to communicate with controls in the main Form1. For example, to write to textBox1.
Turns out easy enough. Just write your method signature to include a TextBox instance. For example you pass textBox1 in and inside the method you refer to it as tb. Then when you call that method (even though it is in another class) you set the tb.Text property to whatever you like and it will show on textBox1.
This makes sense when you consider that control is just a special kind of object, graphically represented in the Form. When you pass it as an argument to a method in another class or the same class, you are actually passing the reference. So writing text to it in the method call will write text to the original control.
I'm trying to call a function in a main form from another form... Already got to call a simple function, by declaring it public static in main form, yet I can't call the needed one.
The function to call:
public static void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
MainForm.txtSendKeys.Text = dial;// Here it asks me for a reference to an object.
foreach (char c in txtSendKeys.Text)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
txtSendKeys.Clear();
}
The procedure I use to call it from a child form:
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Hoho";
MainForm.spotcall();
}
I completely admit that I lack some theory about C#, but as it often happens, I just have to do it for my work, so I expect to get help if by chance I don't get to the solution by myself. Thank you :)
You cannot reference instances of controls on your MainForm in a static method. Like the compiler is telling you, you need an instance of the form in order to update things like TextBoxes. Without an instance, where would the values you are trying to update go?
I'm not sure exactly how the child form is being created, but one way you could call methods on your MainForm would be to provide a reference to your MainForm instance directly to the child form. This could be through the constructor or some public property.
For example
public class ChildForm : Form {
public MainForm MyParent { get; set; }
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Hoho";
// Now your child can access the instance of MainForm directly
this.MyParent.spotcall();
}
}
Assuming you are creating ChildForm inside of MainForm the code to give the child a reference is pretty simple:
var childForm = new ChildForm();
childForm.MyParent = this; // this is a `MainForm` in this case
childForm.Show();
You would also need to make spotcall an instance method and not a static method, and remove the static reference to MainForm in your code:
public void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
// Now it no longer asks you for a reference, you have one!
txtSendKeys.Text = dial;
foreach (char c in txtSendKeys.Text)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
txtSendKeys.Clear();
}
I think the correct way to do this is to use delegates. This way your form (window) does not have to know anything about the parent form (the form can be opened from different parent forms).
Let's say we want to call a function in the parent form when the child form is closed (not showing the form as modal).
At the top of your child form create a delegate:
public delegate void CloseEvent();
public CloseEvent WindowClosed;
Create the form closing event and have it call your delegate:
private void child_FormClosing(object sender, FormClosingEventArgs e)
{
WindowClosed();
}
A button in the parent form can show the child form and set the callback:
private ChildForm childform = null;
private void buttonShowChildForm_Click(object sender, EventArgs e)
{
if (childform == null)
{
childform = new ChildForm();
childform.WindowClosed += childClosed;
childform.Show();
} else
{
childform.BringToFront();
}
}
private void childClosed()
{
childform = null;
}
In this example we use a button to open a new form that does not block the parent form. If the user tries to open the form a second time, we just bring the existing form to the front to show it to the user. When the form is closed we set the object to null so that next time we click the button a new form is opened because the old was disposed when closed.
Best regards
Hans Milling...
You can not access non-static members in static context, which means you have to made txtSendKeys static, or make your function non-static.
If you create a static function, you may not reference global variables inside the function that aren't static as well.
So in order for spotcall to be static, you have to remove the reference to the txtSendKeys (I'm assuming this is a text box that you have created elsewhere in the form) or txtSendKeys must be declared within the static function.
Additional:
You obtained the value for txtSendKeys.Text in the previous line, via variable dial. Instead of referencing txtSendKeys.Text at all, I imagine you could simply use the variable dial to complete the function and leave the function static (you clear it at the end anyway).
public static void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
foreach (char c in dial)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
}
Although, that wouldn't overcome the same issue you would likely run into with checkBoxPrivate.Checked.
You could change it to take a boolean argument.
public static void spotcall(Boolean PrivateChecked)
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
foreach (char c in dial)
{
sideapp.Keyboard.SendKey(c.ToString(), PrivateChecked);
}
}
You can put the shared code in a third class that's visible to both forms. So, for example:
public class static HelperFunctions
{
public static void spotcall()
{
. . .
}
}
Then replace
MainForm.spotcall()
with
HelperFunctions.spotcall()
The MainForm is just a class. It has the structure of the class. But the only data you can get from it is static data.
But an instance of that class appears when you do: MainForm MyFormInstance = new MainForm();
The MainForm can be used only to access static members (methods, properties...).
When you want to get the txtSendKeys, you must get it from an instance (object reference). That's because the textbox is not static, so it only exists in instances of the form.
So, you should do the following:
Make spotcall NOT static.
Put in child form a variable MainForm MyParentMainForm;
When you call the child, set that MyParentMainForm with the instance of the mainform. If it's being called from the main form, you can get the instance with the this keyword.
Inside child form, call MyParentMainForm.spotcall
PS: I'm not sure if there's something like a real child form or if you just call new forms from another. If there's really a child form, you can get the Parent property to access the instance of the main form.
This is sort of a "design pattern" issue, which I'll elaborate on, but I can try to explain the most direct way to solve this if you don't expect this program to change very much. "Static" things only exist once - once in the entire application. When a variable or function is static, it's much easier to access from anywhere in the program; but you can't access an object's associated data, because you're not pointing to a particular instance of that object (ie, you have seven MainForms. Which one are you calling this function on?) Since standard WinForm design expects you could have seven copies of MainForm displaying, all variables associated are going to be instance variables, or non-static. However, if you expect never to have a second MainForm, then you can take the "singleton" approach, and have an easy way of accessing your one instance.
partial class MainForm {
// only including the code that I'm adding; I'm sure there's a lot of stuff in your form.
public static MainForm Instance { public get; private set; }
protected void onInitialize() { // You need to hook this part up yourself.
Instance = this;
}
}
partial class SubForm {
protected void onImportantButton() {
MainForm.Instance.doImportantThing()
}
}
Putting too much active data-changing logic in form classes is a pretty common issue with many beginners' code. That's not a horrible thing - you wouldn't want to be making 5 controlling classes just for a simple thing you're trying. As code gets more complex, you start to find some things would make more sense to move to a "sublevel" of classes that don't interact with the user (so, some day, if this is being re-coded as a server program, you could throw away the form classes, and just use the logic classes - theoretically speaking). It also takes some time for many programmers to understand the whole concept of object "instances", and the "context" that a function is called in.
How does one access WinForm controls such as ProgressBar properties from another class?
Please see my code below. I know this might not be the best option to expose WinForm class and its members as public but I am trying to clear the concept at this point.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void button1_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
c.loop();
}
public void PBSetup()
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
}
public void PBUpdate(int recno)
{
progressBar1.Value = Class1.recno;
}
}
}
namespace WindowsFormsApplication1
{
class Class1
{
public static int recno;
public void loop()
{
//How do I access Form1.PBSetup()??
for (recno = 0; recno <= 100; recno++)
{
//How do I access Form1.PBUpdate(recno)??
}
}
}
}
You do not want your business logic (your class) interacting with your UI (your form). The business logic should be agnostic of the presentation layer.
If you want the form to respond to things that happen inside the class, you could consider exposing an Event inside the class that the form could subscribe to, much like it would subscribe to a button's click event. The class instance could fire off the event completely unaware of who might be listening, and any subscribers would be notified.
This looks like a big time code smell :).
You would need an instance of Form1 inside of Class1 in order to PBUpdate.
Something tells me what you are doing is just not right.
Explain what you are trying to do and we can help. Otherwise there is no way to access PBUpdate unless you either made it a static function where you could call it like Form1.PBUpdate() or you had an instance of Form1 within your class Class1
You can change the access modifiers of the progress bar from private to Internal or public , you can do this operation from properties pane .
Keep in mind that you have to pass to the second class the instance of the form and then you can change the value of the progress bar directly from the second class.
However is a tricky solution, the best should be keep the presentation layer implementation separated and work with an event.
I do not recommend to use this method, for simple reason as mentioned here by one of the comments. But if you really want to access that form control, here is how:
1) Select that control and set its access modifier to internal.
2) Assume your form id is "Form1" and control id is "control1"
Inside your method:
Form1 form = (Form1)Application.OpenForms["Form1"];
// form.control1 should now be available.
I almost never used (advanced, or at all) graphical interfaces, or one simple form with simple controls... but this time I've got something a little more complex, and I don't have much experience with GUI.
I have one main form (and possibly more in the future) from which other sub-forms open (and they might have sub-forms of themselves) and I wonder what is, in your opinion, the best way to communicate between them?
I thought of passing the main form as a parameter to the constructors of the sub-forms, but it doesn't seem like a good way, especially if I'm going to need to communicate between other, distinct, sub-forms, not to mention I have to double check the input, or make a few methods, but it seems more like functional programming than object oriented programming...
Perhaps I can:
Create a static class (or Properties.Settings) for global settings. Cons: every change of data is needed to be copied to the class, I'm looking for something a bit more comfortable and elegant.
Use the ugly way of accessing the controls from Application.OpenForms - fixes the problem of passing the main form as parameter. Cons: not very stable.
Do something else I haven't thought of. Suggestions? Cons: don't know what it is yet.
Your constructor idea is probably the most sound method of communication back to the main form. Your sub form would do something like the following:
public class SubForm : Form
{
public SubForm(MainForm parentForm)
{
_parentForm = parentForm;
}
private MainForm _parentForm;
private void btn_UpdateClientName_Click(object sender, EventArgs e)
{
_parentForm.UpdateClientName(txt_ClientName.Text);
}
}
And then you expose public methods on your MainForm:
public class MainForm : Form
{
public void UpdateClientName(string clientName)
{
txt_MainClientName.Text = clientName;
}
}
Alternatively, you can go the other way around and subscribe to events from your SubForms:
public class MainForm : Form
{
private SubForm1 _subForm1;
private SubForm2 _subForm2;
public MainForm()
{
_subForm1 = new SubForm1();
_subForm2 = new SubForm2();
_subForm1.ClientUpdated += new EventHandler(_subForm1_ClientUpdated);
_subForm2.ClientUpdated += new EventHandler(_subForm2_ProductUpdated);
}
private void _subForm1_ClientUpdated(object sender, EventArgs e)
{
txt_ClientName.Text = _subForm1.ClientName; // Expose a public property
}
private void _subForm2_ProductUpdated(object sender, EventArgs e)
{
txt_ProductName.Text = _subForm2.ProductName; // Expose a public property
}
}
A good way is to declare delegates in the form that want to start the communication. You need a delegate and a callback function:
public delegate void SetValueDelegate(string value);
public SetValueDelegate SetValueCallback;
Another form can then attach to this delegate. At that moment both forms have to know each other, but not after that moment:
firstForm.SetValueCallback += new SetValueDelegate(secondForm.SetValueFunction);
The second form has to declare a function that matches the delegate definition:
public void SetValueFunction(string value)
{
// do something
}
Now the first form can use the delegate to use the function of the second form (and all other forms or classes that were attached to the delegate:
SetValueCallback(txtParam.Text);
Edit: made an complete example
using System;
namespace DelegateTest
{
public delegate void SetValueDelegate(string value);
public class Class1
{
public SetValueDelegate SetValueCallBack;
public void Test()
{
if(SetValueCallBack != null)
{
SetValueCallBack("Hello World!");
}
}
}
public class Class2
{
public void SetValueFunction(string value)
{
Console.WriteLine(value);
}
}
public class Launcher
{
public static void Main(string[] args)
{
Class1 c1 = new Class1();
Class2 c2 = new Class2();
c1.SetValueCallBack += new SetValueDelegate(c2.SetValueFunction);
c1.Test();
}
}
}
The most flexible, scalable (and IMHO the most professional) way to do it is to use CAB (Composite Application Block). In simple terms CAB is a set of 2-3 assemblies that implement a lot of plumbing required to make complex UI applications the right way and it exposes this plumbing to the user of the library in a nice way. Among others it has a very nice event and command (as in command pattern) system.
The downside: requires some time to learn and not very trivial to grasp.
Here is a comprehensive (but easy to understand) tutorial that will help you make the learning easier.
You can use the built in Tag property of the form which is an "object" class.
public Form1()
{
(ComplicatedDataStructure)Tag = new ComplicatedDataStracture();
}
.
.
form1 = new Form1();
.
.
form2 = new Form2();
.
.
form2.Tag = form1.Tag;
so form2.Tag is equals to "ComplicatedDataStracture" object;