Monday, October 21, 2013

Managing Data Among Multiple Forms (Part 3)

Firstly, let me apologise for having taken so long to finish this three-part series. Parts 1 and 2 showed how you CAN manage data among multiple forms but this part 3 will show how you SHOULD do it. That’s rather important I’d say, so let’s get into it.

The first and most important point to note is that forms are objects like any other, so moving data between forms is done just like it is for any other objects. How do you usually pass data into an object? You either set a property or else call a method and pass an argument. How do you usually get data out of an object? You either get a property or else call a method and get the return value. That’s exactly how you pass data into and get data out of a form because forms are objects.

The second point to note is that, generally speaking, a control should only be accessed by the form it is on. While it’s legal to access a control from outside its form, good practice dictates that you should not do so. With the first point in mind, that means that getting data from a control on a different form means getting data from the other form and the other form getting it from the control. Likewise, passing data to a control on another form means passing data to the other form and the other form passing it to the control.

This can be demonstrated fairly easily by displaying a list of records in one form and editing the selected record in another form. To build such an example, start by creating a new Windows Forms Application project. To Form1, add a DataGridView, a Button and a BindingSource. Now add the following code to populate the grid with a few records at startup.

Add a second form to the project and add a TextBox and a Button to that form. What we’re going to do is click the Button in Form1 to open Form2, get the record selected in the DataGridView in Form1 and edit its Name field in the TextBox in Form2. Now remember, Form2 cannot access the DataGridView in Form1 and Form1 cannot access the TextBox in Form2. How to move the data back and forth? The answer is that Form1 will set a property of Form2 to pass the initial data in and get the same property to get the final data out while, internally, that property of Form2 will access the TextBox. Which property? Well, one that you define yourself.

C#

publicstring TextBoxText

{

get

{

returnthis.textBox1.Text;

}

set

{

this.textBox1.Text = value;

}

}

VB

PublicProperty TextBoxText AsString

Get

ReturnMe.TextBox1.Text

EndGet

Set(value AsString)

Me.TextBox1.Text = value

EndSet

EndProperty

Back in Form1, we need to handle the Click event of the Button, open Form2 and pass it the Name from the record selected in the DataGridView.

Run the project now and Form1 will appear displaying the three records. Select one of the records and click the Button. You will see Form2 open with the Name field value from the selected record in the TextBox. Try editing the name and then click the Close button on the title bar of Form2. The dialogue will close and you’ll see that the Name field of the selected record remains unchanged. That’s because the DialogResult returned by ShowDialog was Cancel rather than OK.

Click the Button on Form1 again and this time, after editing the name in the TextBox, click the Button on Form2. This time, you’ll see that Form2 closes and the Name field of the selected record is updated to the value that you entered in the TextBox. Congratulations! You just passed data between two forms the right way.

That’s nice and all but what if, in our example, you wanted to do something back in Form1 without closing Form2? As it stands, ShowDialog returning is Form1’s notification that it should get some data from Form2 and update its own DataGridView. If we don’t call ShowDialog though, it can’t return and we can’t use it as a notification. What to do? Well, how are you usually notified that something has happened in .NET code? You handle an event.

If you want to learn all the details about custom events, I suggest that you check out my blog post here. I’m going to do it quick and dirty here because the point of this post is how to handle the event and use that notification rather than the details of how to generate it in the first place.

In Form2, we need to add an event that will notify anyone listening that the text in the TextBox has changed and, instead of closing the form when the Button is clicked, we need to raise that event.

If you run the project again you will see that you can open the dialogue and edit the selected record multiple times without closing the dialogue. If you close the dialogue and select another record then you can open a new dialogue and edit that as well.

This is a slightly contrived example but hopefully you get the idea. If you want to update a control in a form then only do it in that form. If you need to push and/or pull data between forms then you do so by getting or setting properties and/or calling methods of that form. If you need to notify a form that data is available to get then you do so with an event.

5 comments:

I've always used Public Properties to pass info between forms on the GUI thread (Main).......but could you possibly detail the best approach to passing data between open forms and background threads? (using the BackgroundWorker tool)

At this point i'm calling a delegate function to invoke the form and it's properties......which has been a challenge when the forms are dynamically generated and can be open/closed during the background operation.

I can provide all my sample code.......however I have an example already posted on VBNETFORUMS if you'd like a bash:

@Anonymous, with regards to your question on editing a 20-columned DataGridView, what you do depends on the specific situation but the first thing that comes to my mind is a single read-only property that exposes the currently selected item in the grid.