travisj said:
I have a question about this that perhaps someone could enlighten me
about. For some time now I have been passing the reference from one
form to the other via the constructor of the second form as follows:
namespace Pass
{
public partial class Form1 : Form
{
public Form2 form2;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2(this);
form2.Show();
}
}
and then my second form would look like this:
namespace Pass
{
public partial class Form2 : Form
{
public Form1 form1;
public Form2(Form1 f1)
{
this.form1 = f1;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
form1.label1.Text = "I changed for you";
}
}
}
Is there anything inherently wrong with the way I am doing things? It
has been working fine, I just want to make sure I am getting myself in
the habit of proper coding techniques.
Thanks,
Travis Calvert
Generally this is a bad idea. It comes with two problems.
First, you can only ever use a Form2 in conjunction with a Form1. If,
someday, you discover that it would be useful to have a Form2 working
together with a Form3, you're hooped.
Second, Form2 knows intimate details about how Form1 is put together.
If someday you decide that label1 shouldn't be a label after all, but a
RichTextBox (because you want to display something with fancy fonts or
formatting) then you're hooped.
There are two ways to improve your design. I always use the second, not
the first, but it depends upon your needs. Your current design is what
is called "very tightly coupled": a FOrm2 works _only_ with a Form1 and
knows about the internals of Form1.
The first alternative is less tightly coupled, so it's better, but not
optimal. First, you should replace a direct reference to
form1.textBox1.Text with a property. Second, you should create an
interface that declares that property. So:
public interface IHasGreeting
{
string GreetingText { get; set; }
}
Now you say that Form1 implements this interface:
public class Form1 : Form, IHasGreeting
{
...
public string GreetingText
{
get { return this.textBox1.Text; }
set
{
if (value == null) { throw new
ArgumentNullException("value"); }
this.textBox1.Text = value;
}
}
}
Finally, you say that Form2 will work with any form that implements
IHasGreeting:
public class Form2 : Form
{
IHasGreeting form1;
public Form2(IHasGreeting greetingForm)
{
this.form1 = greetingForm;
}
private void button1_Click(object sender, EventArgs e)
{
this.form1.GreetingText = "Hello world";
}
}
Of course, "GreetingText" should be some name descriptive of what the
thing is.
Notice that this does two things: first, Form2 no longer knows how
Form1 displays its text; second, Form2 will work with _any object_ that
implements IHasGreeting, whether it be a Form1, a Form3, or some other
object.
The second way involves even more loose coupling. This is the method I
tend to use. Have Form2 raise an event when something interesting
happens (the user presses the Hello button) and offer a property from
which a caller can fetch any required information. Form1 then
subscribes to this event and does whatever it wants to with the
information.
public class Form2 : Form
{
private string _greeting = "";
public event System.EventHandler UserSaidHello;
public string UserGreeting { get { return this._greeting; } }
private void button1_Click(object sender, EventArgs e)
{
this._greeting = "Hello world.";
if (UserSaidHello != null) { UserSaidHello(this,
System.EventArgs.Empty); }
}
}
public class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.UserSaidHello += new
System.EventHandler(form2_UserSaidHello);
form2.Show();
}
private void form2_UserSaidHello(object sender, System.EventArgs e)
{
Form2 f2 = (Form2)sender;
this.textBox1.Text = f2.UserGreeting;
}
}
Now Form2 is completely ignorant of the environment in which it is
being used. It notifies any interested parties when the user clicks the
button, and offers a property that interested parties can read when the
event happens. Any object can use Form2: just subscribe to the event.
One refinement to this second approach is to create a custom event
arguments type that passes the greeting string as part of the event,
but this is necessary only in certain multithreading scenarios, in
which the UserGreeting property could change between the time that the
event is raised and the time that the event handler fetches the
property value.
Either of these two approaches--an interface or an event--is better
than passing a form reference to another form, from an O-O point of
view.