Problem
I have a model that uses the State pattern. Some States allow read/write access to the Model’s properties and some are read-only. I wanted a good way to separate the logic for this from my Views; the View shouldn’t have to decide whether properties are displayed as TextBoxes or inside Divs.
First Thought
At first I was going to create a custom ViewEngine. That ViewEngine would be responsible for choosing the right View – that is, if the View was called “Edit” but the model was in a readonly state, it would change the View to “ReadOnly”. However, that just didn’t sound right – why was that responsibility falling on the ViewEngine? Plus, it’s kind of inflexible – what if some editable Views are called “Edit” but some are called “Details”.
Solution
I ended up with a mixture of a custom ActionFilter and a base Controller class. Before I show you the code, let me show you the usage:
1: [AcceptVerbs(HttpVerbs.Get)]
2: [StateItemEditView("ReadOnly")]
3: public ActionResult Edit(int id)
4: {
5: var module = _Repository.GetByID(id);
6: return View(module);
7: }
Notice Line 2 – this specifies that:
- The current action has editable content
- The ReadOnly View name is called “ReadOnly”
The code for the controller, which I use as a base class for my controllers that act upon StateItems:
1: public class StateItemController : Controller
2: {
3: protected override ViewResult View(string viewName, string masterName, object model)
4: {
5: object readOnlyViewName;
6: if (ControllerContext.Controller.TempData
7: .TryGetValue(StateItemEditViewAttribute.key_ReadOnlyView, out readOnlyViewName))
8: {
9: var stateItem = model as IStateItem;
10: if (workflowItem != null && stateItem.CurrentState.IsReadOnly)
11: viewName = readOnlyViewName as string;
12: }
13: return base.View(viewName, masterName, model);
14: }
15: }
Before returning the View, which has a ViewName string, we want to make sure the ViewName is correct. We check the Controller’s TempData for a predefined key. If that key exists, the value will be the name of the View to display for ReadOnly states. We then check if the Model exists and is a StateItem, and if it’s ReadOnly we change the ViewName before returning the View.
The StateItemEditViewAttribute is just responsible for setting the TempData key:
1: public class StateItemEditViewAttribute : ActionFilterAttribute
2: {
3: public StateItemEditViewAttribute(string readOnlyView)
4: {
5: _ReadOnlyView = readOnlyView;
6: }
7: private string _ReadOnlyView;
8: public const string key_ReadOnlyView = "ReadOnlyView";
9: public override void OnActionExecuting(ActionExecutingContext filterContext)
10: {
11: filterContext.Controller.TempData.Add(key_ReadOnlyView, _ReadOnlyView);
12: }
13: }
I’m using this for a binary dicision – Edit/ReadOnly – but this could be used for a lot of other purposes too.