This article addresses the similar area I wrote 3 years ago. MVP is now very popular to separate business logic from data models and data views. Some business logic is solely used to control appearance of controls on views. For example: Disable/Enable, Visible/Invisible, List Options, Color/Font, etc... The logic of these is slight different from the transaction logic of a business model, it can also change very often during the whole development life cycle. Failure to define and separate it from the main business logic can result in a very messy code base, and is very hard for new developers to maintain or extend functionalities.
I introduced a config file to record this logic and make it out of the programming domain. Because it's XML, first it's kind to business analysts, they can create one based on its schema and hand it to development directly. I am not expecting that the developer can use it directly in an app but it does provide a very accurate requirement and greatly eases change control.
Secondly, by using a fairly generic logic controller object which loads this config file and coordinates GUI behaviors for your forms, it reduces coding in view or presenter. And it provides good separation between GUI logic and Business logic.
Moreover, it provides an easy way to update your GUI logic through an online update process. For example, a web service can feed this config file and change the way user can interact with GUI.
Inside the Config File
<Pub Sub="Simpsons" State="BooleanTrue" />
<Sub Obj="Members" State="MembersOptionSimpsons" />
<Pub Sub="Friends" State="BooleanTrue" />
<Sub Obj="Members" State="MembersOptionFriends" />
<State Name="BooleanTrue" Impl="GUIStateMVP.Model.BooleanState">
<Facet Name="Value" Value="true" />
<State Name="MembersOptionSimpsons" Impl="GUIStateMVP.Model.OptionState" >
<Facet Name="OptionItem" Value="Homer" />
<Facet Name="OptionItem" Value="Marge" />
<Facet Name="OptionItem" Value="Bart" />
<Facet Name="OptionItem" Value="Lisa" />
<Facet Name="OptionItem" Value="Maggie" />
<State Name="MembersOptionFriends" Impl="GUIStateMVP.Model.OptionState" >
<Facet Name="OptionItem" Value="Monica" />
<Facet Name="OptionItem" Value="Ross" />
<Facet Name="OptionItem" Value="Joey" />
<Facet Name="OptionItem" Value="Chandler" />
<Facet Name="OptionItem" Value="Rachel" />
<Facet Name="OptionItem" Value="Pheobe" />
It defines a pub-sub and a state pattern in XML. The reason to have State outside Event is for reuse. Facet element is fairly generic here, so State classes can use polymorphism to implement
public interface IState
bool Equals(IState s);
void Save(Control control);
void Update(Control control);
I also included two
State classes in the demo project to show how it works. But it's for demo purposes only, please don't charge me for coding standards. :-)
Where the Magic Happens
LogicController is the place where the mission is accomplished. It loads XML, caches it, and uses it for the business.
LogicController provides a good place to handle view related logic like the above. I would prefer to use it and XML file to store option labels for (List Box/Combo) rather than the old way having a dictionary table in database, because that data is naturally closer to View rather than Data Model. For example, Data Model of your app provides value (normally it will be an ID), and view/
logicController provides proper labels (Display Text) for the current view. This is more flexible to have it defined in XML outside your data storage, and it is easier to be localized too. Consider globalization, all you need is to ship a new version of XML config file. Surely you can do lots of things to make it closer to the standard way handled in .NET.
Some Good Staff
State class works for deserialization. It uses Dependency Injection to create XXXState classes based on information from the XML file. It's a smart way to solve polymorphism from XML serialization. At least for now, I believe so.
Some UML to assist you in reading my code. Coming soon...
- 21st December, 2006: Initial post