JAL Computing

C++COMProgramming .NET Mac Palm CPP/CLI Hobbies

 

Home
Up

Chapter 33 "Multiple Controllers"

It is possible to create more than one controller, each with a different domain of registered listeners at startup. When the user logs on as an Administrator the Administration controller is used. If the user logs on as a User the User controller is used. The Form1 class saves the current Controller in an instance field named currentController. Each controller can send events to Views as appropriate to the user's privileges. Since each controller only saves type safe pointers to Views, there is only one set of actual View objects. Finally, we create a third controller that sends events to all Views, controllerAllViews, but only contains listeners for the Clear() and Reset() events. We use this controller when we switch between users. A successive call to Clear() and Reset() should suffice to return a View object to its initial appearance and state. In the case of controllerUser, we limit the domain of View objects that are registered as listeners. In the case of controllerAllViews, we limit the domain of events that are registered.

Note: It is also possible to dynamically add and remove delegates at runtime from a given controller, but this approach may be more error prone and harder to maintain.

In this example, only the Administrator can access the TotalMortgageView.  

Controller Class

Here is the Controller class that implements the IViewable interface. This class does not implement the IViewableEvents interface.

using System;
using System.Collections.Generic;
using System.Text;
namespace M_C_V
{
    // indirection between View and Model
    // View and Model have no implementation knowledge or each other
    // Views register an interest in NotifyHandler events via NotifyDispatcher
    // Request to change state of Model are triggered by call to IUpdate.Update
    // Controller can have state and saves result of last calculation
    // Controller provides a matching event dispatcher for each method in IViewable
    // Clear, Refresh, Enable and Disable(string message) see MCV.IViewableEvents
    // 
    public class Controller : Mortgage.IUpdate, MCV.IViewable
    {
        // Controller owns the Model
        private Model model = new Model();
        private bool isEnabled = true;  // used only in Refresh
        private string disableMessage = "";
        private Mortgage.Calculation defaultCalculation =
                        new Mortgage.Calculation(0, 0, 0, 0, Mortgage.INVALID, "");
        private Mortgage.Calculation lastCalculation;
        public Controller()
        {
            this.lastCalculation = defaultCalculation;
        }
        public Mortgage.Calculation Calculation
        {
            get { return this.lastCalculation; }
        }
           
        // implement IViewable methods, but Controller is NOT a View
        // Views should clear visible components, but NOT component state
        // allows recovery from clear using refresh and stored local state
        public bool Clear()
        {
            ClearDispatcher(this, new EventArgs());
            return true;
        }
        // clients will refresh data from LOCAL view state
        public virtual bool Refresh()
        {
            if (this.isEnabled)
            {
                RefreshDispatcher(this, new EventArgs());
                return true;
            }
            else
            {
                ClearDispatcher(this, new EventArgs());
                return false;
            }
        }
        public bool Enable()
        {
            isEnabled = true;
            EnableDispatcher(this, new EventArgs());
            return true;
        }
        public bool Disable(string message)
        {
            isEnabled = false;
            // reset this controller do not fire reset event
            this.lastCalculation = defaultCalculation;
            // controller is not a View component
            // it does not clear itself
            if (message != null)
            {
                disableMessage = message;
            }
            else
            {
                disableMessage = "";
            }
            // each _View_ should clear and reset _itself_ on disable event
            DisableDispatcher(this, new MCV.DisableEventArgs(disableMessage));
            return true;
        }
        public bool Reset()
        {
            this.lastCalculation = defaultCalculation;
            ResetDispatcher(this, new EventArgs());
            return true;
        }
        // create dispatcher to easily register interest in Notify event
        // eg. listener calls controller.NotifyDispatcher  += new Controller.NotifyHandler(FormView_OnNotifyEvent);
        // to register an interest in this event
        public event Mortgage.NotifyHandler NotifyDispatcher;
        // provide dispatcher in IViewableEvents that maps to each method in IViewable
        public event MCV.ClearHandler ClearDispatcher;
        public event MCV.DisableHandler DisableDispatcher;
        public event MCV.EnableHandler EnableDispatcher;
        public event MCV.RefreshHandler RefreshDispatcher;
        public event MCV.ResetHandler ResetDispatcher;
       
        public bool Update(Mortgage.Parameters mp)
        {
            Mortgage.Calculation mc = model.Calculate(mp);
            if (mc.IsValid() && isEnabled)
            {
                this.lastCalculation = mc;
                // fire event to all registered listeners of change im model state
                NotifyDispatcher(this, new Mortgage.MortgageEventArgs(mc));
                return true;
            }
            else return false; // do nothing if input data is invalid
        }
        // fire event to all registered listeners of lastCalculation
        protected bool InvokeNotifyDelegates()
        {
            if (this.isEnabled && lastCalculation.IsValid())
            {
                NotifyDispatcher(this, new Mortgage.MortgageEventArgs(lastCalculation));
                return true;
            }
            else
            {
                ClearDispatcher(this, new EventArgs());
                return false;
            }
        }
        public bool IsEnabled
        {
            get
            {
                return this.isEnabled;
            }
            set
            {
                this.isEnabled = value;
            }
        }
    }
}

Download the Project

 Here is the latest project build: M_C_V.zip 

Create a valid calculation, then use the clear button and then the refresh the Views using the menu choice "Refresh Views."  Each View is able to refresh or restore its appearance since each View object stores its state. There is no complex logic chain to follow since each object knows its own state and responds appropriately to the myriad possible combinations of user states and view states. In  Chapter 34, we refactor our calculator and derive our two Views from an abstract class AbstractMCVView.

- 30 -

 
Send mail to [email protected] with questions or comments about this web site. Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 © 
Last modified: 08/04/09
Hosted by www.Geocities.ws

1