Web Xcel Demo: View Services in nRoute

Posted by Rishi on 23-Oct-09 5:56 AM - Comments (4)

ViewServices in nRoute are basically UI implemented functionality exposed as services for non-visual consumption - think a Status-Bar as IStatusInfo service. The underlying idea is separation of concerns, so that non-visual components like ViewModels don't have to take dependency on View-based controls or on platform specific contraptions. Now, there is nothing radical about this concept, but with nRoute we get first-class API support to specifically identify, implement and consume "ViewServices" and that's what is discussed in this post.

To demonstrate ViewServices I've put up a small demo called Web Xcel - it replicates the Office Web look and feel for a spreadsheet type application. However, it is no spreadsheet; I've just used the feel of a potentially real application to show how we can integrate and consume ViewServices in an application's flow as it were. Below are some screenshots:

Web Xcel

Web Xcel

You can view the Web Xcel demo app here.

ViewServices - The Basics

Before we get to some examples of ViewServices let me just briefly recap how to define and consume ViewServices - for a proper introduction see the introductory post to nRoute.Toolkit. Firstly, the functionality that a ViewService exposes is encapsulated in a contact i.e. an interface. The defined interface, is then implemented onto a control/object which we earmark with a MapViewService attribute along with some related options. So for example, in Web Xcel demo we have this IConfirmMessageViewService contract that allows us to confirm "something" from the user, the interface definition looks like:

   1: public interface IConfirmMessageViewService
   2: {
   3:     string Title { get; set; }
   4:     string Message { get; set; }
   5:     IDisposable Confirm(Action<bool?> confirmationCallback);
   6: }

This contract basically takes in a title, message and a callback that returns the response from the user - we'll get to the details ahead. However, this contract's implementing class is decorated with the MapViewService attribute as shown below:

   1: [MapViewService(typeof(IConfirmMessageViewService), 
   2:     Lifetime=ViewServiceLifetime.PerInstance)]
   3: public class ConfirmationViewService
   4:     : IConfirmMessageViewService
   5: {
   6:     // implementation 
   7: }

Now, the only notable thing with MapViewService attribute is the Lifetime option which tells nRoute how/when to instantiate/serve a request of the IConfirmMessageViewService - and in this case, it is going to instantiate a new instance per request. You have other lifetime management options namely singleton, weak singleton, discovered instance and self-registered instance. Singleton obviously means a single instance for all requests and the weak singleton option serves a single instance around as long one or more consumers are using it, once it is out of use it's GC'ed. The discovered instance in interesting, as it goes through the VisualTree to find the first implementing instance and returns what it finds. And the self-registered ViewService relies on an implementing control to register an instance of itself when available and unregister when going dark. The important thing to remember with these non per-instance options is the effect on state-management of having potentially multiple concurrent consumers as opposed to each consumer having their own instance.

To resolve a ViewService, we can make use of the ViewServiceLocator helper class that like any IoC component returns an instance. Also in cases where we have more that one implementation of a ViewService we can name each implementation and resolve the same by identifying its name. Now, to use the IConfirmMessageViewService we do something like:

   1: // set up the service
   2: var _showMessageVS = ViewServiceLocator.GetViewService<IConfirmMessageViewService>();
   3: _showMessageVS.Title = MODIFIED_WORKSHEET;
   4: _showMessageVS.Message = CONFIRM_WORKSHEET;
   5:  
   6: // we get the user's response
   7: return _showMessageVS.Confirm(confirmationCallback);

The idea behind the use above is to confirm from the User as to if we save the active worksheet, which has pending changes, before we open or create another worksheet. The visual implementation uses a dialog, as shown below, which confirms for the ViewModel how to proceed. 

Confirmation Child Window

The larger point to ViewServices is that your ViewModel is not concerned about how the visually-implemented functionality is rendered or responded to. It disconnects the implementation from its consumption, so for example you could change or put up a totally new visual implementation, and your ViewModel will be none-the-wiser but more importantly it won't have to be changed.

Web Xcel Demo's ViewServices

I'm not going to go through the full details of the ViewServices in the demo app (you have the code for that), but I'll just describe them and highlight some of the more interesting parts.

IOpenFileViewService

This ViewService is part of the toolkit itself, and as it self-suggests it opens a file dialog that returns a file stream. It is really simple, except the notable instruction is that that its use has to be instantiated directly by a user action such as a key press or a click event. Secondly, this is also very important, we can't open two back-to-back dialogs with a single user-interaction - so we can't save a file and follow it by a open file dialog, this manifests in an interesting solution in the demo app.

ISaveFileViewService

This ViewService does as it reads, and is also pre-packaged in the toolkit. One important distinction I'd like to make with this ViewService is that, it is not an "integrated-visual" in a sense that a Status-Bar is. It, like with other dialogs and ChildWindows, is extraordinary in the sense that it somehow appears from sky as opposed to being part of the VisualTree. This distinction makes a difference in how an instance of the specific ViewService is initialized, instantiated and returned - so a Status-Bar in the VisualTree may need to be registered/discovered whereas a Save File Dialog can be new'ed up.

IConfirmMessageViewService

I've already described this ViewService above, and as duly noted it makes use of a ChildWindow. The interesting part of this ViewService is that it takes in a delegate-based callback and returns an IDisposable. The IDisposable is like a token that be used to dispose the dialog - so in some cases when you want to say draw-down the dialog (from your ViewModel) before the user has addressed it, you can call the dispose method on the token. This pattern is also useful in cases when you want to show information-based dialogs like "Uploading" that users can cancel or when finished it can be disposed via your ViewModel. Again, note this an example of non-integrated visual - it falls from the sky.

IStatusViewService

This ViewService is used to update the current "status" of the application, basically visualized via the Status-Bar (so it's an example of an integrated ViewService). The defining interface to this ViewService is very simple, have a look:

   1: public interface IStatusViewService
   2: {
   3:     IDisposable UpdateStatus(string status);
   4: }

Again like the confirmation ViewService this ViewService returns an IDisposable token that can be used to call-off your status update. So for example, when done saving you can trigger the dispose on the token and the status-bar will revert to its default message. However, if another update has been received post yours, the dispose is auto-magically called for you by the status-bar itself - so in that case the token wouldn't do anything. In this way, it can support multiple updates to the status whilst having only one retractable-update active. As a side-note, you have to be careful about the given implementation in the Demo as it can lead to memory-leaks if you indefinitely hold onto the token - so always dispose them or change the implementation to a weakly-referenced one.

IDocumentInfoViewService 

The Web Xcel is like an example of a Single Document Interface (SDI), and to abet that this ViewService helps with updating the title and 'IsDirty' status of the working document. Obviously the implementation of this is very simple here, but the point made is how a ViewModel that that manages the active worksheet can visually reflect the status in the Shell. Secondly, this ViewService (like the IStatusViewService) is directly implemented in the code-behind of the MainPage.xaml (i.e. the Shell also the RootVisual). I am not sure if the MVVM cops would like this, but to me this a UI detail and it can be implemented in the View itself.

Balloon Notification ITimeoutNotificationViewService

This ViewService is implemented as a notification balloon, as shown in the screenshot. It has two main features, one that it supports a timeout of your notifications - so basically it shows the balloon for the given period of time and then closes it. Secondly, it also supports queuing of notifications - so whilst a single notification is being shown it queues the the others and plays them one-by-one in a first-register-first-shown manner. Now, this ViewService is implemented as a control, which I find to be a reasonable way to component'ize and expose ViewServices .

IInteractiveNotificaitonViewService

This ViewService like it suggests allows for a visual notification with an interactive capability in that if the user "interacts" with it, a callback notifying the same is raised. As far as the consumer (like a ViewModel) is concerned the "interaction" is an implementation detail - which in Demo's case happens to be a click on a header-type panel.

  Header Notification

So above, if the User happens to click on the yellow header bar - it would raise a callback saying the user interacted with the message and so do your thing. On the other hand, if the close button is pressed the callback would return saying that the user did not "interact" so do the other thing. Also, because this ViewService is practically a single-instanced (it self-registers itself) service that potentially serves multiple concurrent-consumers, the notifications are queued in a FIFO manner to ensure User's individual viewership for each and ever notification.

ViewServices - Not There Yet

So in this post and accompanying demo we've covered how ViewServices help us to abstract, expose, and consume visually-implemented functionality. This in my experience is an important and often ignored piece of the MVVM puzzle, which I hope by semantically capturing in a ViewService implementation provides for a better handle. However, there are lots of other aspects to this, especially in relation to View-Composition and that is something that needs more work. For example, how can we get contextual tabs integrated into the ribbon when a User is say editing a cell - obviously, you can do it (with something like Prism regions), but what is needed (or what I want) is to keep the View-composition logic out of the ViewModel/Modules and have a XAML-based way to dynamically integrate/compose bits and pieces of the UI together.

Also, some of the other things I want to provide is a generic modal-dialog strap-up that fuse nicely with ViewModels akin to what Nikhil Kothari showed in his post, but for a broader spectrum of uses such as Wizards. Now, some of the solutions would require the full nRoute which avails the routing engine and view-composition capabilities - obviously because the wheel has already been invented there. Other than that, currently we're not doing dependency injection which is big-problem and I want to address it quickly - there are some tricky problem to it making it play nice.

In many ways, a lot of these things are still fresh from the oven - so going forward I'd like to have more scenarios-covered with better formalization of the View-ViewModel interaction.

View the Web Xcel Demo here.
Download the demo app source-code and the nRoute.Toolkit from Codeplex
(note you require the accompanying nRoute.Toolkit dll)

Icons in the demo app are from the Silk Icon collection by Mark James (Thanks).

Comments

trackback
DotNetShoutout
on 14-Oct-09 6:01 PM
Web Xcel Demo: View Services in nRoute

Thank you for submitting this cool story - Trackback from DotNetShoutout

trackback
DotNetKicks.com
on 14-Oct-09 6:03 PM
Web Xcel Demo: View Services in nRoute

You've been kicked (a good thing) - Trackback from DotNetKicks.com

trackback
uberVU - social comments
on 17-Oct-09 2:38 PM
Social comments and analytics for this post

This post was mentioned on Twitter by Orktane: Just Blogged - "Web Xcel Demo: View Services in nRoute" http://bit.ly/3BWtms

trackback
orktane
on 27-Oct-09 8:05 PM
I Command MVVM

I Command MVVM

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading