Synchronizing Changes
When a user clicks one of the menu items, the framework fires the
viewCustomerList_Click event-handler, which displays a Customer List View.
objView = New frmCustomerListView()
objView.Initialize(Me.Model)
If Not Me.Container Is Nothing Then
objMDIPArent = Me.Container
objView.MdiParent = objMDIPArent
End If
objView.Display()
If the user changes any data in the screen, the Mediator relays the changed data to the Controller to be validated and processed. To see how this works, change the name of one of the customers by editing the label in the List View. When you press Enter or click away from the label, the
AfterLabelEdit event goes off.
Dim objListItem As System.Windows.Forms.ListViewItem
Dim objEvent As MVCLibrary.clsEvent
objListItem = lvwCustomers.Items(e.Item)
objEvent = New MVCLibrary.clsEvent()
With objEvent
.Name = "CustomerNameChange"
.Value = e.Label
.ValueEx = objListItem.Tag
End With
Try
mobjController.HandleEvent(objEvent)
Catch objError As System.Exception
MsgBox(objError.Message)
e.CancelEdit = True
End Try
The
AfterLabelEdit event creates a new clsEvent instance and populates it with information about the changed data. Then the View notifies its Controller of the change by calling the
HandleEvent method. The notification call occurs within a
Try...Catch block because the Controller may return an error if the data is not valid. For example, the business rules might state that the customer name cannot be blank. The Controller's
HandleEvent method validates the Event's value, catches the error condition, and raises an error.
Public Overrides Sub UpdateData(ByVal NewEvent As MVCLibrary.clsEvent)
Dim objCusotmers As clsCustomers
Dim objCustomer As clsCustomer
Select Case NewEvent.Name
Case "CustomerNameChange"
If Len(NewEvent.Value) < 1 Then
Err.Raise("50000",
"clsCustomerListController", _
"Name cannot be blank")
End If
objCusotmers = Me.Model
objCustomer = objCustomers.Item
(NewEvent.ValueEx)
objCustomer.Name = NewEvent.Value
End Select
End Sub
If the data is valid, the Controller makes changes to the Model by retrieving the appropriate Customer instance from the collection and changing its
Name property.
When this change occurs, it must be propagated to other Views that might be relying on the data. Therefore, in clsCustomer, the
Name property's
Set code notifies any observers that a change has occurred by creating a new instance of clsEvent, setting its properties, and passing the changes along using the parent's (clsCustomers)
Notify method. The
Notify method is a member of the base class clsSubject. It cycles through the list of attached observers and sends the event on to them.
Public Property Name() As String
Get
Name = mstrName
End Get
Set(ByVal Value As String)
Dim objEvent As MVCLibrary.clsEvent
mstrName = Value
objEvent = New MVCLibrary.clsEvent()
With objEvent
.Name = "CustomerDataChanged"
.Value = Me
End With
mobjParent.Notify(objEvent)
End Set
End Property
Views all register with the Model when they are initialized. That means that any other currently displayed View will receive events and be able to adjust its information immediately. There's no need for the user or the application to refresh the screen to see the new data, everything happens in real-time.
To see this in action, open up two or three instances of the Customer List form, change the name of a customer on one of the lists and press enter. You'll see the name updated immediately in all Views.
The sample project also has a New Customer View you can experiment with. To activate this view, click File, then New, then Customer. The New Customer View displays differently than the Customer List View; it's a modal form, not an MDI child, but it behaves the same way. When you click OK to create a new customer, its Controller validates the entered data. If the data is valid, the Controller makes modifications to the Model, which in turn notifies all attached observers about the new customer. Try it out and see what happens.
I hope the sample helps you solve the problem of coordinating multiple programmers working simultaneously on a single UI. Although the sample project code is not robust enough to include directly in a real project, you should be able to use it as a starting point. There are several things you can do to improve the sample project, such as:
- Load the list of Mediators from an XML file, and create them using dynamic binding.
- Replace the even names with enumeration values.
- Add a Windows menu that lists all open windows, and lets you tile and cascade them.
- Give windows the ability to trap standard menu and toolbar events (such as Save from the main menu)