The EntitySpaces Community

Share and learn about the EntitySpaces Architecture.
Welcome to The EntitySpaces Community Sign in | Join | Help
in
Home Forums Photos

MVC (Passive View) Implementation feedback/advice please

Last post 06-20-2008, 3:12 PM by pritcham. 5 replies.
Sort Posts: Previous Next
  •  06-13-2008, 5:50 AM 9805

    MVC (Passive View) Implementation feedback/advice please

    Hi all

    Although it's something I've had on my mind for a while now, I've decided to take the plunge and try and get my head around the MVC/MVP patterns - in particular because I'd like to build an app that has both Windows and Web front ends - more of a personal challenge/learning experience than an actual requirement.

    Anyway, I've done a bit of reading yesterday and today and think(!) I have an idea as to how to implement a Passive View where the view is (very) dumb, and as such should be easily switched out to either winform or webform (or whatever).  I've detailed below my understanding of this based on my reading and firstly wanted to make sure I had understood correctly (feel free to point out where/if I have misunderstood anything - I'm trying to learn afterall).  Secondly, assuming my understanding is ok, I had a couple of questions:

    So - my understanding of what the various parts do, what they know (if anything) and how they interact is below (I'm using ES as the Model "as-is" and am happy with tying up my implementation to be ES specific):

    Model
    The Model isn't aware of any of the other elements.
    In this instance the model is made up of Entityspaces classes.  Takes input from the Controller.  Also raises a PropertyChanged notification event (which the Controller subscribes to so the Controller will be made aware when any property changes take place in the object).
     
    View
    The View doesn't really know anything (No connection to any of the other 2 elements).  
    Adds/Removes the propertyChangeRequest() events to the ChangeRequestEvents aggregator.
    Makes a call to the ChangeRequestEvents.Fire() method in the aggregator so that all subsribed listeners are made aware of the change (request)
     
    The UI controls for each displayed property are exposed via Writeonly Properties (which will be set by the Controller)
     
    Methods: (Control)Property Setters, Register/Unregister events in the event aggregator (below), calls to the event aggregator Fire() method to send the update request.
     
    Controller
    The controller knows about the Model (ES.BusinessObjects) and the View
     
    Subscribes to and handles PropertyChanged events from the Model.  The (PropertyChanged) event handler's call the view's appropriate property setter - e.g.
     - CompanyName gets changed in the Model
     - This fires a PropertyChanged event (with the property name as an arguement)
     - The Controller catches this event and sets the IView.CompanyName property to the new value - the concrete implementation (form/usercontrol etc) for the IView.CompanyName property setter would set the appropriate control on the View to reflect the new value
     
    Subscribes to and handles any View_OnxxxxChangeRequest() events - these events are fired by the concrete implementation of IView when the user has changed the value of the property via the View.  The event handler for each property (in this simplistic example) sets the corresponding value in the Model - e.g.
     - User changes CompanyName textbox
     - This fires a CompanyNameChangeRequest event (with the proposed new value as an arg)
     - The Controller catches this event and sets the Model.CompanyName property to the new value
     
    (The Controller would usually carry out any validation/authorisation etc before updating the value in the Model.)
     
    Implements the SetViewState method which sets all of the concrete implementation of IView properties to the value of the model properties (rather than an individual property) - these properties in the View will update the appropriate control for that property.
     
    Methods: WireEvents() - adds listeners for ChangeRequestEvents, UnWireEvents - removes listeners for ChangeRequestEvents,  SetViewState(), Also responsible for initialising (connecting) the Model and View + dealing with Model.PropertyChangedEvents.
     
     
    ChangeRequestEvents:
    This is acts as an event aggregator that a) registers "Listeners", b) sends the event(s) to interested listeners, and c) removes listeners from the list.
    Methods: RegisterListener, RemoveListener, Fire() - fires the actual event
     
    ===============
     
    Now - does that all sound about right? Have I mixed up any of the responsibilities (or not been clear in my explanation)?
     
    And now for my questions:
     
    1) At the moment my model is, for all intents and purposes, defined as a single EntitySpaces Entity.  This works well as the my test view is a "Details View" type of user control - at this stage just showing the CompanyName from the Northwind Customers entity.  I'm really not sure how I would go about constructing a view against a Collection of entities (for display in a grid/listbox/whatever).  It's (very!) early days for me in terms of getting a feel for how the various elements fit together (started looking yesterday!) so apologies if this is an obvious question.  My current test view has individual controls (textboxes for example) for each property to be displayed so it's very straightforward for my Controller to set each property appropriately.  I don't see (atm) how this would be done for, lets say, a Grid/listview etc).  Any pointers greatefully received!
     
    2) Displaying validation errors.  The View is ignorant of the Model so even though IDataErrorInfo is implemented in my ES objects, the View knows nothing of them.  In this instance do I just put an ErrorProvider on the view, make it accessible via a Property to the Controller, and set it as I would any other 'control'?
     
    Think that's about it for now - I appreciate it's a long post so sorry about that, just wanted to try and be as clear as I can be.
     
    Thanks in advance
    Martin
  •  06-19-2008, 11:03 AM 9897 in reply to 9805

    Re: MVC (Passive View) Implementation feedback/advice please

    Anyone? 

    I know it's not an ES question but I also know there's a lot of experience developers here so any help/advice/pointers would be very much appreciated - even if it's along the lines of "Forget your approach, a much more suitable architecture for an app with a winform AND webform front end would be....".

    Anyway, in the hope that what has stopped people replying is a lack of code, here's some... all generated against the standard Northwind db, sample code below is against the Categories table..

    Here's the interface for the view:

    Code:
    Public Interface ICategoriesView
      
    	Writeonly Property CategoryID As Nullable(Of System.Int32)
    	Writeonly Property CategoryName As System.String
    	Writeonly Property Description As System.String
    	Writeonly Property Picture As System.Byte()
    
    	Sub RegisterChangeRequestListener(Of T)(ByVal propertyName As String, ByVal handler As EventHandler(Of PropertyChangeRequestEventArgs(Of T)))
    	Sub UnRegisterChangeRequestListener(Of T)(ByVal propertyName As String, ByVal handler As EventHandler(Of PropertyChangeRequestEventArgs(Of T)))
    
    End Interface
    
    

    Here's the concrete (view) implementation for the above interface:

    Code:
    #Region "Imports"
    Imports System
    Imports System.Collections.Generic
    
    Imports Northwind.MVC
    #End Region
    
    Partial Public Class CategoriesView
    		Implements ICategoriesView
    
    #Region "Constructors"
    		Public Sub New()
    			InitializeComponent()
    			m_changeRequestedEvents = New ChangeRequestEvents(Me)
    		End Sub
    #End Region
    
    #Region "Member Variables"
    		Private m_changeRequestedEvents As ChangeRequestEvents
    #End Region
    
    		#Region "ICategoriesView Members"
      
    		Public Writeonly Property CategoryID As Nullable(Of System.Int32) Implements ICategoriesView.CategoryID
    			Set(ByVal value as Nullable(Of System.Int32))
    			    CategoryIDLabel.Text = value
    			End Set
    		End Property
      
    		Public Writeonly Property CategoryName As System.String Implements ICategoriesView.CategoryName
    			Set(ByVal value as System.String)
    			    CategoryNameTextbox.Text = value
    			End Set
    		End Property
      
    		Public Writeonly Property Description As System.String Implements ICategoriesView.Description
    			Set(ByVal value as System.String)
    			    DescriptionTextbox.Text = value
    			End Set
    		End Property
      
    		Public Writeonly Property Picture As System.Byte() Implements ICategoriesView.Picture
    			Set(ByVal value as System.Byte())
    			'TODO: set control to show value of Picture
    			' control type would be: Unknown/undecided Windows control	
    			' System type is: System.Byte()
    			End Set
    		End Property
    
    		Public Sub RegisterChangeRequestListener(Of T)(ByVal propertyName As String, ByVal handler As EventHandler(Of PropertyChangeRequestEventArgs(Of T))) Implements ICategoriesView.RegisterChangeRequestListener
    			m_changeRequestedEvents.RegisterListener(Of T)(propertyName, handler)
    		End Sub
    
    		Public Sub UnRegisterChangeRequestListener(Of T)(ByVal propertyName As String, ByVal handler As EventHandler(Of PropertyChangeRequestEventArgs(Of T))) Implements ICategoriesView.UnRegisterChangeRequestListener
    			m_changeRequestedEvents.UnRegisterListener(Of T)(propertyName, handler)
    		End Sub
    #End Region
    
    End Class
    
    

    This snippet would be in, say, the event handler for a "Save changes" button click:

    Code:
    Private Sub SaveChanges_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveChangesButton.Click
        m_changeRequestedEvents.Fire(Of String)("CategoryName", CategoryNameTextbox.Text)
        '......etc for the rest of the properties....
    End Sub
    
     

    Here's the controller base class:

    Code:
    Public MustInherit Class ControllerBase(Of TModel As {Northwind.Model.BusinessObjects.EntityBase, New}, TView)
        Implements IDisposable
    
    #Region "Member Variables"
        Protected _view As TView
        Protected _model As New TModel
    #End Region
    
    #Region "Abstract Methods"
        Protected MustOverride Sub WireEvents()
        Protected MustOverride Sub UnwireEvents()
        Protected MustOverride Sub SetViewState()
    #End Region
    
    #Region "Methods"
        Public Overridable Sub Initialize(ByVal model As TModel, ByVal view As TView)
            If _model IsNot Nothing OrElse _view IsNot Nothing Then
                UnwireEvents()
            End If
            _model = model
            _view = view
            SetViewState()
            WireEvents()
        End Sub
    #End Region
    
    #Region "Properties"
        Public ReadOnly Property Model() As TModel
            Get
                Return _model
            End Get
        End Property
        Public ReadOnly Property View() As TView
            Get
                Return _view
            End Get
        End Property
    #End Region
    
    #Region "IDisposable Members"
        Public Sub Dispose() Implements IDisposable.Dispose
            UnwireEvents()
        End Sub
    #End Region
    
    End Class
    

    And here's the (inherited) Controller

    Code:
    #Region "Imports"
    Imports System
    Imports System.Collections.Generic
    
    Imports Northwind.Model.BusinessObjects
    #End Region
    
    Public Class CategoriesController
    	Inherits ControllerBase(Of Categories, ICategoriesView)
    	
    	Public Sub New(ByVal model As Categories, ByVal view As ICategoriesView)
    		Initialize(model, view)
    	End Sub
    	
    	Protected Overrides Sub WireEvents()
    		AddHandler model.PropertyChanged, AddressOf model_PropertyChanged
      
    		view.RegisterChangeRequestListener(Of Nullable(Of System.Int32))("CategoryID", AddressOf View_OnCategoryIDChangeRequest)	
    		view.RegisterChangeRequestListener(Of System.String)("CategoryName", AddressOf View_OnCategoryNameChangeRequest)	
    		view.RegisterChangeRequestListener(Of System.String)("Description", AddressOf View_OnDescriptionChangeRequest)	
    		view.RegisterChangeRequestListener(Of System.Byte())("Picture", AddressOf View_OnPictureChangeRequest)	
    
    	End Sub
    	
    	
    	Protected Overrides Sub UnWireEvents()
    		RemoveHandler model.PropertyChanged, AddressOf model_PropertyChanged
    		view.UnRegisterChangeRequestListener(Of Nullable(Of System.Int32))("CategoryID", AddressOf View_OnCategoryIDChangeRequest)
    		view.UnRegisterChangeRequestListener(Of System.String)("CategoryName", AddressOf View_OnCategoryNameChangeRequest)
    		view.UnRegisterChangeRequestListener(Of System.String)("Description", AddressOf View_OnDescriptionChangeRequest)
    		view.UnRegisterChangeRequestListener(Of System.Byte())("Picture", AddressOf View_OnPictureChangeRequest)
    	End Sub
    	
    	Private Sub Model_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
    		Select Case e.PropertyName
      
    		Case "CategoryID"
    			view.CategoryID = model.CategoryID
      
    		Case "CategoryName"
    			view.CategoryName = model.CategoryName
      
    		Case "Description"
    			view.Description = model.Description
      
    		Case "Picture"
    			'Handle the Picture here - i.e. view.Picture = model.Picture
    
    		Case Else
    			Throw New ArgumentException("Property not handled: " & e.PropertyName)
    		End Select
    	End Sub
    
      
    	Private Sub View_OnCategoryIDChangeRequest(ByVal sender As Object, ByVal args As PropertyChangeRequestEventArgs(Of Nullable(Of System.Int32)))
    		model.CategoryID = args.RequestedValue
    	End Sub
      
    	Private Sub View_OnCategoryNameChangeRequest(ByVal sender As Object, ByVal args As PropertyChangeRequestEventArgs(Of System.String))
    		model.CategoryName = args.RequestedValue
    	End Sub
      
    	Private Sub View_OnDescriptionChangeRequest(ByVal sender As Object, ByVal args As PropertyChangeRequestEventArgs(Of System.String))
    		model.Description = args.RequestedValue
    	End Sub
      
    	Private Sub View_OnPictureChangeRequest(ByVal sender As Object, ByVal args As PropertyChangeRequestEventArgs(Of System.Byte()))
    		model.Picture = args.RequestedValue
    	End Sub
    
    
    	Protected Overrides Sub SetViewState()
      
    		view.CategoryID = model.CategoryID
      		view.CategoryName = model.CategoryName
      		view.Description = model.Description
      		'view.Picture = model.Picture
    
    	End Sub
    
    End Class
    
    

     

    Anyway, as I said in my initial post, I'm trying to get my head around the pattern and how to implement it/deal with collections etc - or even whether this is the right path to go down - I'm open to ideas as always!

    Cheers all, and apologies for yet another mega-long post!

    Martin

  •  06-19-2008, 1:20 PM 9900 in reply to 9897

    Re: MVC (Passive View) Implementation feedback/advice please

    Martin,

    I'm pretty much in the same boat as you, but I've built a little Nortwind MVC app using Entity Spaces with the ES framework, and also without it (loosely coupled).  You might want to take a look and see what I've done.  This might start you in the right direction.

    http://www.mvcstarterkits.net/post/ASPNET-MVC-Northwind-Demo-using-Entity-Spaces.aspx

    Looking a little closer at what you've created, now understand I'm still learning this so I'm no expert, but it seems you might be creating more work in event capturing than is necessary.  Also, everything that I've heard and read, make me feel that you want to keep the View as clean as possible, with as little working or processing code as possible.

    You can leave all the processing to a Service or Facade layer that talks to the Controller.  This way the application can stay as loosely coupled as possible.

    This demo app that I didn't build as much as refactored from the Northwind app by Phil Haack, was built using Linq and I refactored it using Entity Spaces and also separated the application into distinct application layers (projects).  This allows for more flexibility and extendability.  Anyway take a look. 

    Let me know what you think.

    Thanks,

    King Wilder
     


    King Wilder
    http://www.kingwilder.com
  •  06-20-2008, 11:01 AM 9918 in reply to 9900

    Re: MVC (Passive View) Implementation feedback/advice please

    Hi King

    I'd seen your blog post before (pretty much as soon as you'd posted the link) but haven't had chance to look at the code as of yet so I'll take some time to do that over the weekend if I get a few spare moments.

    As for your feedback on the code I'd posted, much appreciated. 

    To be honest the approach I've detailed above is an implementation of the Passive View discussed in the "Build your own CAB" series of articles by Jeremy Miller (which are excellent IMO - if you haven't seen them take a look at: http://codebetter.com/blogs/jeremy.miller/archive/tags/Build+your+own+CAB/default.aspx) - the view in this case is indeed very dumb, it effectively has property setters that are accessible by the Controller and are responsible for updating the UI widgets, and it passes (at the moment) any property change requests to the controller (or whichever other class is listening for the ChangeRequest events) for action, so the view doesn't do anything other than fire the event.  To be honest, events are just the communication method in this case, it could just as easily have been a CommandManager class (or Service layer) or whatever that gets called via calls like "CommandManager.SaveCustomer" etc.

    There are a few different implementations discussed for this pattern/architecture and this (Passive View) - this was one of the less complex ones (but with advantages) - a kind of happy medium for me to look at as a learning curve etc - having had some time to think following your reply I'm sure there are other approaches that I can take for the type of scenario I discussed above (i.e. how to handle a collection in the view etc) - really all the view needs to know in these instances is some form of interface for the collection for binding purposes (which is easy enough) and a method of notifying the controller of the currently selected item (I have no need of directly updateable collections at this point - the user will be updating things one entity at a time so I would have a separate form/usercontrol for the entity they are working on).

    Anyway, thanks again for the feedback (and for taking the time to look over what was effectively quite a long post with me 'thinking aloud'!) - I'll definitely be taking a look at your code, hopefully this weekend!

    Cheers

    Martin

  •  06-20-2008, 2:05 PM 9919 in reply to 9918

    Re: MVC (Passive View) Implementation feedback/advice please

    Martin,

    I've read a few posts by Jeremy.  They are usually very informative, and sometimes over my head.  Geeked  But that's ok, I usually get something from it.  I haven't heard about the Passive View pattern, but I'll read up on it to see what it's all about.  I'm all about patterns if they help me build an extendable and flexible application.

    Since I'm still fairly new to MVC, and I haven't thought about adding properties to the view. I'm still learning what is posssible and what isn't.

    I'm so used to using the controls for WebForms to handle much of the HTML rendering of the data, that I don't know how to do in MVC, things that are no-brainers in WebForms.  I think that is what is stalling many developers.  

    But the initial thing that got me interested in MVC was the "separation of concerns" as they call it.

    When I first saw a demo that Scott Guthrie had and he showed how you can create a test project that would simulate the UI, that's when I was hooked.  But I haven't seen enough examples of how to completely mirror what a WebForms application can do.

    I'd like to chat a little more on this.  I too am finishing a large project, and starting another larger project, so my time to create new posts on MVCStarterKits.net will be limited.

    Later,

    King Wilder


    King Wilder
    http://www.kingwilder.com
  •  06-20-2008, 3:12 PM 9922 in reply to 9919

    Re: MVC (Passive View) Implementation feedback/advice please

    Hi

    Be happy to chat - heck, it's good to have a learning bud! Seriously, take a look at the blog link I posted, it's relatively easy to get your head around - he's a great writer and I (think) I picked up a whole heap from reading through the series (not finished reading though so still more to learn!).

    Cheers

    Martin

View as RSS news feed in XML