Click to Rate and Give Feedback
Related Articles

Here the author dissects the ASP.NET MVC framework and looks at how controllers work. He then explains how the framework interacts with your controllers and how you can influence those interactions.

Scott Allen

MSDN Magazine May 2009

...

Read more!

We use the new Asynchronous Agents Library in Visual C++ 2010 to solve the classic Dining Philosophers concurrency problem.

Rick Molloy

MSDN Magazine June 2009

...

Read more!

Memory usage can have a direct impact on how fast an application executes and thus is important to optimize. In this article we discuss the basics of memory optimization for .NET programs.

Subramanian Ramaswamy and Vance Morrison

MSDN Magazine June 2009

...

Read more!

Mike Calligaro shows you the basics of using XNA Game Studio 3.0 to write games for Zune.

Mike Calligaro

MSDN Magazine May 2009

...

Read more!

This column shows you how to secure the .NET Services Bus and also provides some helper classes and utilities to automate many of the details.

Juval Lowy

MSDN Magazine July 2009

...

Read more!

Also by this Author

Ken Spencer

MSDN Magazine March 2003

...

Read more!

Ken Spencer

MSDN Magazine July 2003

...

Read more!

In past versions of Visual Basic, there were rudimentary graphics controls. In Visual Basic .NET you have the GDI+ library, which enables you to draw lines, circles, and most anything else. But how can you use the functionality of GDI+ to create lines and other graphics that respond to user mouse clicks and events? Find out.

Ken Spencer

MSDN Magazine June 2003

...

Read more!

Ken Spencer

MSDN Magazine May 2004

...

Read more!

Ken Spencer

MSDN Magazine October 2003

...

Read more!

Popular Articles

This article introduces 10 development tools that can increase your productivity, give you a better understanding of .NET, and maybe even change the way that you develop applications. The tools covered include NUnit to write unit tests, Reflector to examine assemblies, FxCop to police your code, Regulator to build regular expressions, NDoc to create code documentation and five more.

James Avery

MSDN Magazine July 2004

...

Read more!

WPF is one of the most important new technologies in the .NET Framework 3.0. This month John Papa introduces its data binding capabilities.

John Papa

MSDN Magazine December 2007

...

Read more!

When incorporating the ASP.NET DataGrid control into your Web apps, common operations such as paging, sorting, editing, and deleting data require more effort than you might like to expend. But all that is about to change. The GridView control--the successor to the DataGrid-- extends the DataGrid's functionality it in a number of ways. First, it fully supports data source components and can automatically handle data operations, such as paging, sorting, and editing, as long as its bound data source object supports these capabilities. In addition, ...

Read more!

C# allows developers to embed XML comments into their source files-a useful facility, especially when more than one programmer is working on the same code. The C# parser can expand these XML tags to provide additional information and export them to an external document for further processing. This article shows how to use XML comments and explains the relevant tags. The author demonstrates how to set up your project to export your XML comments into convenient documentation for the benefit of other developers. He also shows how to use comments ...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

New information has been added to this article since publication.
Refer to the Editor's Update below.

Advanced Basics
Synchronizing Multiple Windows Forms
Ken Spencer

Code download available at: AdvancedBasics0404.exe (135 KB)
Browse the Code Online

Q I'm creating a Windows® Forms application with multiple instances of a single form. I'd like to write it so that any operation performed on any one instance of the form will be reflected on all other instances. How can I do this?
Q I'm creating a Windows® Forms application with multiple instances of a single form. I'd like to write it so that any operation performed on any one instance of the form will be reflected on all other instances. How can I do this?

A This is an interesting question. I guarantee that some smart code jockey is going to suggest that I use delegates. Before that happens, let's explore several solutions to this problem.
A This is an interesting question. I guarantee that some smart code jockey is going to suggest that I use delegates. Before that happens, let's explore several solutions to this problem.
Say I have two forms, each with two textbox controls: txt1stData and txt2ndData. How can I keep those controls in sync on each of the two forms? For the purposes of this discussion, it does not matter whether I have two forms or ten, the problem is the same.
The first approach is rather simple. In fact, it's even simpler than using delegates directly, which I think is sometimes overkill. First, I created a class that contains properties that I want to share with all the forms in the application (see Figure 1). For instance, MyData and MoreData hold data that each form can display. I will come back to this class in a minute.
Public Class DataClass
    Private privateMyData As String = ""
    Private privateMoreData As String = ""

    Public Event MyDataChanged As EventHandler
    Public Event MoreDataChanged As EventHandler
    Public Property MyData() As String
        Get
            Return privateMyData
        End Get
        Set(ByVal Value As String)
            privateMyData = Value
            RaiseEvent MyDataChanged(Me, New EventArgs)
        End Set
    End Property
    Public Property MoreData() As String
        Get
            Return privateMoreData
        End Get
        Set(ByVal Value As String)
            privateMoreData = Value
            RaiseEvent MoreDataChanged(Me, New EventArgs)
        End Set
    End Property
    Public Sub Reset()
        MyData = ""
        MoreData = ""
    End Sub
End Class
Second, I created two forms with the same controls (txt1stData and txt2ndData) as mentioned earlier. You can see the layout of the form in Figure 2. Both forms have exactly the same data, and I'll explain why in a moment.
Figure 2 Form Layout 
Next, I created a module named modGeneral and added the following line of code:
Friend DataStuff As DataClass
This line creates a friend variable for my new DataClass that you can access throughout the assembly, which for this simple example is the entire application. Then I added the following code to the Form1 Load event:
DataStuff = New DataClass

Me.txt1stData.DataBindings.Add("Text", DataStuff, "MyData")
Me.txt2ndData.DataBindings.Add("Text", DataStuff, "MoreData")
The first line creates a new instance of DataClass. The second two lines of code set up the data binding to the textbox controls. That's it for this form!
Now, how do you synchronize them with the data on Form2 and all other forms? Add the following two lines to the form load event of Form 2:
Me.txt1stData.DataBindings.Add("Text", _
    DataStuff, "MyData")
Me.txt2ndData.DataBindings.Add("Text", _   
    DataStuff, "Moredata")
That's one easy way to make sure all forms are in sync with most any type of data. You can simply data bind the controls to the same instance of a class and you're finished.
Now, for another approach. I created a new form named frmBase. Then I placed a textbox (txtNextData) and label on it. I wanted each form in my application to share the textbox and label and I wanted them all to stay in sync with each other, so I rebuilt the project. I created Form1 and Form2 by inheriting from the new frmBase so they could inherit all the new controls. But how can I keep the controls in sync? This time a little coding is required to make it work, but the code is in a single class where it's reused by simply calling a function.
The code sample in Figure 3 shows the module called modGeneral. Its first task is to define two variables: MyForms and localNextData. MyForms is a collection that will contain a list of all the forms I want to synchronize. The variable that I called localNextData will store all the data that I want to display in the forms. Keep in mind that both of these variables could reside in a class instead of a module.
Module modGeneral
    Friend DataStuff As DataClass
    Friend MyForms As New Collection
    Friend localNextData As String
    Sub AddForm(ByVal ThisForm As Form)
        MyForms.Add(ThisForm)
        UpdateControlsNextData(localNextData)
    End Sub

    Friend Property NextData() As String
        Get
            Return localNextData
        End Get
        Set(ByVal Value As String)
            localNextData = Value
            UpdateControlsNextData(localNextData)
        End Set
    End Property

    Private Sub UpdateControlsNextData(ByVal Value As String)
        Dim frm As frmBase
        Dim obj As Object
        
        For Each obj In MyForms
            frm = CType(obj, frmBase)
            If Not IsNothing(frm.txtNextData) Then
                frm.txtNextData.Text = Value
            End If
        Next
    End Sub
End Module
[ Editor's Update - 12/6/2004: The code in Figure 3 has been updated to prevent multiple form instances from being added to the MyForms collection with the same key.] I will use this collection in the UpdateControlsNextData procedure to determine which forms to update. AddForm also calls UpdateControlsNextData to ensure a new form is updated with the correct data.
The other code in modGeneral is the NextData property. This property's set accessor updates localNextData and also calls UpdateControlsNextData to synchronize all of the forms. Now all I need to do is set NextData whenever I want to change it and all forms will be updated by the call to UpdateControlsNextData.
The third approach, custom linking, is a refinement of the second one. I created it to get a bit more flexibility when processing controls on a form. For instance, I only want to track and process certain forms, those with the controls that must be in sync. This method also lets me define which controls I want to synchronize and then process only those forms.
I added another module for this approach (modGeneralv2) which is shown in Figure 4. This module includes a collection (MyFormsToUpdate) which will hold all the forms I want to sync. The module also has a new array (ControlsToUpdate) which provides a list of the controls I want to sync. The definition for the array looks like this:
Private ControlsToUpdate() As String = _
    {"txtCustomer", "txtAddress", "txtName"}
Friend MyFormsToUpdate As New Collection
Private ControlsToUpdate() As String = _
  {"txtCustomer", "txtAddress", "txtName"}

Friend Sub AddFormToUpdate(ByVal ThisForm As Form)
  Dim ctrl As Control
  Dim i As Integer
  Dim AddThisForm As Boolean = False

  Try
    For Each ctrl In ThisForm.Controls
      For i = 0 To ControlsToUpdate.GetUpperBound(0)
          If ctrl.Name = ControlsToUpdate(i) Then
            AddThisForm = True
           Exit For
         End If
         If AddThisForm Then
           MyFormsToUpdate.Add(ThisForm, ThisForm.Name)
           Exit For
         End If
       Next
     Next
     If AddThisForm Then
       If MyFormsToUpdate.Count > 1 Then
         UpdateControlsOnAllForms( CType(MyFormsToUpdate(1), Form))
       End If
     End If
   Catch ex As Exception
     Throw New Exception( _
       "AddForm generated this error: " & ex.Message, ex)
   End Try
 End Sub

 Friend Sub UpdateControlsOnAllForms(ByVal MasterForm As Form)
   Dim frm As Form
   Dim ctrlMaster, ctrlClient As Control
   Dim i As Integer
   Try
   For Each frm In MyFormsToUpdate
     For Each ctrlMaster In MasterForm.Controls
       For i = 0 To ControlsToUpdate.GetUpperBound(0)
         If ctrlMaster.Name = ControlsToUpdate(i) Then
           For Each ctrlClient In frm.Controls
             If ctrlClient.Name = ControlsToUpdate(i) Then
               ctrlClient.Text = ctrlMaster.Text
                 Exit For
              End If
            Next
          End If
        Next
     Next
   Next
   Catch ex As Exception
     Throw New Exception( _
       "UpdateControlsNextData generated this error: " & ex.Message, ex)
     End Try
 End Sub
Instead of AddForm, this module has a new and improved version called AddFormToUpdate. This method works in a fashion similar to AddForm but now it only adds the forms that actually contain one or more of the controls in my ControlsToUpdate array, so only those forms with the specific controls are in the update collection. This allows me to call this function from every form. If I decide to add one of the special controls later, it will automatically be added to the forms list. I only need to make one small change to the forms code to hook it up.
This module also contains the UpdateControlsOnAllForms procedure, which performs the update. Instead of using an application-level variable as shown in the last method, I now use the concept of a master form. Thus I can copy the values from that form to all other forms in the collection. UpdateControlsOnAllForms is really a set of simple For...Nexts that loop through the controls on a form, find the ones to update, and update them.
To implement this functionality in my forms, I add this line to the form's Load event:
AddFormToUpdate(Me)
Alternately, I can add it into the constructor. This line will add the current form instance to the collection to update.
Now let's hook up a single event procedure:
Private Sub txt_Leave(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles txtAddress.Leave, _
    txtCustomer.Leave, txtName.Leave 
        UpdateControlsOnAllForms(Me)
End Sub
This wires up the Leave event of all three controls (txtAddress, txtCustomer, and txtName) that I want to synchronize to one event handler. Then I can add the single line to call UpdateControlsOnAllForms. Me is passed to this procedure call to cause the other forms to be synchronized with this form.
Now I have three versions of code that can synchronize controls on forms, so I have a choice. I could have used custom events, defining an event in the DataClass and having each form subscribe to it. Then when the event fires, the forms could pick up the new data from each event handler and set the appropriate controls. But that's at least as much code as just data binding the controls to the class like I did in the first method. I could create a single procedure that performs the update and place this procedure in a module. I would need to pass the form instance to the procedure for it to perform the update. I could fire this procedure with an event handler from the class. The procedure would look like this one:
Sub UpdateControls(ByVal ThisForm As frmBase)
    With ThisForm
        .txtNextData.Text = localNextData
    End With
End Sub
The parameter to ThisForm is typed as frmBase so that it can have access to IntelliSense® and pick up properties that are custom to the form. Simply typing it as a Form will not cause the properties that are in frmBase and its derived forms to be shown.
Another option is to use delegates. A delegate would, of course, let me redirect an invocation of the delegate to a method in each form. If I used multicasting I could let each form handle the event and update the appropriate controls. It sounds really simple to set up this functionality with delegates, but to me it's more trouble than it's worth in practice. Plus, other than the nested For...Next loops in the third method, the code is not difficult to understand. And after all, the most costly part of an app is still its maintenance.

Send your questions and comments for Ken to  basics@microsoft.com.


Ken Spencer works for 32X Tech, where he provides training, software development, and consulting services on Microsoft technologies.

Page view tracker