Breaking News

How to Pass Data Between Forms in .NET


It seems a simple enough task, yet I see repeated questions asking how to do it: how to pass data between two forms. In this article, I will show you the different mechanisms available for you to do just that. This article is directed towards the .NET novice.

The first thing you should realize about Form in .NET is that they are classes. How do you or I know that they are classes? The code tells you:

C#
public partial class Form1 : Form
1:

VB
Public Class Form1
1:


Take note of the "class" keyword. This is your indicator that forms are classes. "Ok, fine. Forms are classes. So what?" Well, if you think of forms as classes, which they are, then you can begin to realize how to share data between forms. How do you share data between two classes? You create references to two different instances and then share data via each class' public members. The same holds true for forms. You can create public members (or parameterized constructors or static members) to accept external data. Where I think most people get tripped up by this concept is the the scope each reference has.



Variable Scope


What is "scope?" Scope deals with what parts of code a variable is visible (accessible) to. There are typically two types of scope:  global and local. Global scope can mean a variable is accessible to all parts of class, file, or assembly, depending on how you modify the variable's declaration. Local scope usually means a variable that can only be accessed from within the same function or block--blocks are things like for and while loops, if statements, etc. You can think of blocks as anything between curly braces in C# or anything between an opening statement and its matching "End" statement in VB (If/End If for example).

Why is scope relevant to passing data between forms? You are going to need to pay attention to how your instances are scoped in order to be able to effectively pass data between forms. Let's look at an example of variable scope.

C#
public partial class Form1 : Form
{
    private Form2 f2 = new Form2();

    public void MyArbitraryMethod1()
    {
        Form2 f3 = new Form2();

        // I CAN ACCESS f2 AND f3
    }

    public void MyArbitraryMethod2()
    {
        // I CAN ACCESS f2
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:



VB
Public Class Form1
    Private f2 As New Form2()

    Public Sub MyArbitraryMethod1()
        Dim f3 As New Form2()

        ' I CAN ACCESS f2 AND f3
    End Sub

    Public Sub MyArbitraryMethod2()
        ' I CAN ACCESS f2
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:


In the above code samples, f2 has global scope to the class because it is declared at the class level (basically, outside of any function). It can be accessed by any method within the class.f3 has local scope to the function MyArbitraryMethod1 because it is declared at the function level. It can only be accessed within MyArbitraryMethod1. One thing that is not demonstrated above, but that you should be aware of is that f2 and f3 internally cannot access any data from the Form1. We'll see a bit later how to provide this access.

Here's a breakdown of how you could share data given our current example. SinceMyArbitraryMethod1 can access both f2 and f3, you could share data between those two forms. In contrast, within function MyArbitraryMethod2 you could not share data between the two forms since within that function, f3 does not exist. In order to share data between the two forms within the MyArbitraryMethod2 method, you would have to widen the scope of f3. "Widening the scope" of a variable just means to increase the areas of code which can access a variable. For this simple example, the only widening route you have is to change f3from a local variable to a global variable. Had f3 been declared from within an ifstatement, then f3 would have been accessible only from within the if. In widening its scope, you could either relocate f3 to be local to the function (rather than local to the ifblock) or you could relocate f3 to be global to the class.

You may have noticed that I didn't mention Form1, the class in which we are writing this code, in the above example. That's because I saved it for this paragraph! Since we are working from within Form1, we can share data between any form we declare from withinForm1's code--from anywhere in the code as long as the form object we are sharing data with is in scope. This means the same thing as I described above with regard to declaration of f3. If we are not working in a function which has access to a different form object, then we cannot share data. For our previous example, in MyArbitraryMethod1, we can share data between Form1 objects and f2 or f3, but in MyArbitraryMethod2, we can only share data with f2.



How To Share


So we have examined when we can share data, but we still haven't covered how. Here's how. I mentioned previously that you can share data by means of public or static members, or by passing data to constructors. Let's take a look at each method.


Public Methods and Constructors

I'm including these as one in the same because you could think of a constructor as being a method--at least with respect to what we're doing here. Let's expand our previous example:

C#
public partial class Form2 : Form
{
    private string _form1Data;

    public Form2(string form1Data)
    {
        InitializeComponent();

        this._form1Data = form1Data;
    }

    public void SomeFunction1()
    {

    }

    public void SomeFunction2(string form1Data)
    {

    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:


VB
Public Class Form2
    Private _form1Data As String

    Public Sub New(ByVal form1Data As String)
        InitializeComponent()

        Me._form1Data = form1Data
    End Sub

    Public Sub SomeFunction1()

    End Sub

    Public Sub SomeFunction2(ByVal form1Data As String)

    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:


Here, we are looking at Form2's code now. I have included both an example of passing via the constructor and passing via a method. The difference I'd like to make note of is that  we took in Form1 data as a parameter in the constructor. We then saved this data to a private variable within Form2. This private variable has global scope to Form2's code. BothSomeFunction1 and SomeFunction2 could subsequently access this value. There is nothing saying we must save the incoming Form1 data to a private variable. Perhaps you want to perform some calculation on the incoming data and store the result in some other member or field of Form2. This is perfectly fine. For this example, just know that if you pass data via a constructor and you want that data to be accessible to other parts of the class, then you will have to save the data somewhere--here I used the private member variable_form1Data. Now onto the methods.

For SomeFunction1 and SomeFunction2, scope rules are still in effect (and they always will be). SomeFunction1 has no knowledge of the parameter which SomeFunction2 takes. It can have knowledge of it if you save this data to some member variable, but this is a "dumb" knowledge. SomeFunction1 doesn't really know that SomeFunction2 takes a parameter; it just knows that some member variable can be accessed by it, due to scoping. It would be up to SomeFunction2 to assign the incoming Form1 data to the private member variable of the class.

If we have this mechanism to pass data, how do execute it from the other form? Well, since these mechanisms we have created are public members, they can be called from our other form provided we have valid instances. Here's what that could look like:

C#
public partial class Form1 : Form
{
    private string _dataToPass = "Hello World!";

    public void MyArbitraryMethod1()
    {
        Form2 f3 = new Form2();

        f3.SomeFunction2(this._dataToPass);
        f3.Show();
    }

    private void button1_Click(object sender, System.EventArgs e)
    {
        MyArbitraryMethod1();
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:


VB
Public Class Form1
    Private _dataToPass As String = "Hello World!"

    Public Sub MyArbitraryMethod1()
        Dim f3 As New Form2()

        f3.SomeFunction2(Me._dataToPass)
        f3.Show()
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MyArbitraryMethod1()
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:


In MyArbitraryMethod1, we have created a new instance of a Form2 class. Take note that we are passing a string that is private to the Form1 class. This string does not exist inForm2's code, at the moment. We use one of the public data-passing mechanisms we just covered to share the data, namely passing via a function. In Form2, we can now work with the Form1 data.

C#
public void SomeFunction2(string form1Data)
{
    this.label1.Text = form1Data;
}
1:
2:
3:
4:



VB
Public Sub SomeFunction2(ByVal form1Data As String)
    Me.Label1.Text = form1Data
End Sub
1:
2:
3:


One technicality to be aware of here is that for this demonstration we are passing a string.string in .NET is a reference type, but it behaves like a value type. What does that mean? Well, in Form2, we are working with a copy of the string we passed in from Form1. There are ways to overcome this and actually work with the string declared on Form1. Adding theref or out keywords in C# and changing the ByVal to ByRef in VB would accomplish this. The point here is that you should be careful with your references when passing data between forms (or any class for that matter) and know whether you are working with a copy of the data or the actual data.

Here is the output you would receive with the above code:
 
  • Data Passed to Form2
Data Passed to Form2


Note that the string "Hello World!" doesn't occur anywhere in Form2's code--we passed it in from Form1. Yes, it's that simple!


Public Properties

Behind the scenes, properties in .NET are turned into getter and setter methods. Since the syntax is a bit different from a method, however, I thought I'd have a separate section on properties.

Even though I'm giving you a separate section on properties, in actuality there is not much different in the way you pass data. The only real difference is that instead of making a function call and either immediately working with the value or storing the value for later use, you store the value immediately for later use. Here's an example:

C#

Property Definition
using System.Windows.Forms;

namespace WindowsFormsApplication13
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        public string StringToDisplay
        {
            get { return this.label1.Text; }
            set { this.label1.Text = value; }
        }
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:


Sharing Via the Property
using System.Windows.Forms;

namespace WindowsFormsApplication13
{
    public partial class Form1 : Form
    {
        private string _dataToPass = "Hello World!";

        public void MyArbitraryMethod1()
        {
            Form2 f3 = new Form2();

            f3.StringToDisplay = this._dataToPass;
            f3.Show();
        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            MyArbitraryMethod1();
        }
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:


VB

Property Definition
Public Class Form2
    Public Sub New()
        InitializeComponent()
    End Sub

    Public Property StringToDisplay() As String
        Get
            Return Me.Label1.Text
        End Get
        Set(ByVal value As String)
            Me.Label1.Text = value
        End Set
    End Property
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:


Sharing Via the Property
Public Class Form1
    Private _dataToPass As String = "Hello World!"

    Public Sub MyArbitraryMethod1()
        Dim f3 As New Form2()

        f3.StringToDisplay = Me._dataToPass
        f3.Show()
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.MyArbitraryMethod1()
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:


The result is the same as that displayed in the above image.


Passing a Reference to the Parent Form

Similar to passing data via functions or properties, another way to access data from one form on another would be to pass a reference to the "parent" form itself. In reality, this is the same thing as what we saw in Public Methods and Constructors. The benefit to this approach is that you open up access to all public members of the parent form from within the child form, rather than just a fixed number of data elements. Setting up this approach is simple: do the same thing we saw in Public Methods and Constructors, except that instead of passing a data element of type string (in the above example), change your procedure to accept an element of the type which the parent form is. Here's an example.


C#

Passing the Reference
public partial class Form1 : Form
{
    private string _dataToPass = "Hello World!";

    public void MyArbitraryMethod1()
    {
        Form2 f3 = new Form2(this);

        f3.Show();
    }

    public string StringToDisplay
    {
        get { return this._dataToPass; }
    }

    private void button1_Click(object sender, System.EventArgs e)
    {
        MyArbitraryMethod1();
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:


Sharing Via the Form Reference
public partial class Form2 : Form
{
    public Form2(Form1 form1Instance)
    {
        InitializeComponent();

        this.label1.Text = form1Instance.StringToDisplay;
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:


VB

Passing the Reference
Public Class Form1
    Private _dataToPass As String = "Hello World!"

    Public Sub MyArbitraryMethod1()
        Dim f3 As New Form2(Me)

        f3.Show()
    End Sub

    Public ReadOnly Property StringToDisplay() As String
        Get
            Return Me._dataToPass
        End Get
    End Property

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MyArbitraryMethod1()
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:


Sharing Via the Form Reference
Public Class Form2
    Public Sub New(ByRef form1Instance As Form1)
        InitializeComponent()

        Me.Label1.Text = form1Instance.StringToDisplay
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:


Notice that we pass a Form1 reference to Form2 by way of the this (Me in VB) keyword. Because Form2 now has a reference to a valid instance of a Form1 object, Form2 has access to Form1's public members, including fields, properties, and methods.


Static Members

This could be considered an extension to the previous two methods (excluding constructors), but it is different enough to warrant its own section. Static ("shared" in VB) members are those that do not need a valid instance in order to be referenced. If you have ever called MessageBox.Show(), then you have used a static member. Think about it. Did you have to create a new MessageBox object in order to call Show(), or did you just call it? Any time you call a method, assign to or read from a property, or assign directly to certain variables by using the syntax

[ClassName].[MemberName]


you are referring to static members. This access convention is why it's a bad idea to give properties the same name as their encasing class.

The thing to be aware of when dealing with static members is that only one occurrence of the member exists for every instance of a particular class. I'll discuss this while we examine our next example.

C#

Creating the Second Form
public partial class Form1 : Form
{
    public static string _dataToPass = "Hello World!";

    public void MyArbitraryMethod1()
    {
        Form2 f3 = new Form2();

        f3.Show();
    }

    private void button1_Click(object sender, System.EventArgs e)
    {
        MyArbitraryMethod1();
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:


Sharing Via the Static Member
public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();

        this.label1.Text = Form1._dataToPass;
    }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:



VB

Creating the Second Form
Public Class Form1
    Public Shared _dataToPass As String = "Hello World!"

    Public Sub MyArbitraryMethod1()
        Dim f3 As New Form2()

        f3.Show()
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MyArbitraryMethod1()
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:


Sharing Via the Static Member
Public Class Form2
    Public Sub New()
        InitializeComponent()

        Me.Label1.Text = Form1._dataToPass
    End Sub
End Class
1:
2:
3:
4:
5:
6:
7:


Have you noticed the difference here? Whereas in the last two examples Form1's data was pushed to Form2 from directly within Form1, here we are pulling the data from Form1from within Form2. This is due to the nature of static members and how you access them via the class name rather than an instance name. Now is probably a good time as any to talk about the negative of this approach:

It is generally a bad idea to use static (shared) members to share data between two instances. Any time you change a static value, every instance of the class will see this change. It is very easy to introduce bugs via this mechanism and it should be used judiciously.


The result of the above code is the same as the image above.



Summary



Congratulations if you've made it this far. I realize there was quite a bit of reading to get to this point. You should have a decent understanding of what mechanisms exist for you to share data among your forms. Keep in mind that you can share public data between forms provided there is some scope relationship between the forms--meaning one form is accessible to the other (or both forms are accessible to each other). You can also share data by implementing static members. Static members will be shared between all instances of the class, so a change to a static member in one instance will be seen by all instances. Another caveat of static members is that even private static members will be shared among instances--they just won't be accessible by objects of a different type. In the grand scheme of things, just remember that .NET treats forms as classes, and that you share data between forms the way you share data between any class. Don't sweat it if you get tripped up on your initial attempts at implementing these concepts in your code--you can always come to EE to share your frustrations!

No comments