Basics on using the view server
Part 1: Basics

One of the most difficult things for a newcomer to Symbian programming is to get used to the fact that the whole operating system is built with object orientation and client-server architecture in mind from scratch. The good side is that the system is very robust and does not suffer from design problems, what makes the programming interfaces easy to use once you get used to them. The bad side, however, is that while you are still learning how to deal with the client-server and OO onipresence, performing even the more mundane tasks requires a lot of code, as in the example I'm going to show in this article: the Symbian OS view server.

The view server is part of the View Architecture (called "VA" from now on), which is itself part of the Symbian UI Control Framework. The VA makes easier for applications use each other's views, and also facilitates the communication between the operating system and applications. Most of the time, you will be using the VA to ease switching views in your multi-view applications, but it's also possible (and common) to use VA for using another application's views, such as the Calendar for selecting dates, Contacts for selecting names and phone numbers and so on. This article is focused on making your application use the view server to switch among your apps views, but in the last section I'll give an overview on how to use an external view from within your app.

General Info

What is a view?

Symbian OS UI framework follows the Model-View-Controller pattern, so that the basic structure for a GUI application is based on MVC since the beginning: the Application, Document and App UI classes form the Controller part, your Engine classes form the Model, and the View classes are, surprisingly, the View components of the architecture. That said, we can define the view as the class that has the responsibility of displaying the application data.

Are you forced to use the MVC architecture in your Symbian applications? No, you're not. You can bundle everything into the View classes and be all set. In fact, if your application is small (for example, having only a single view and a read-only model) that's exactly what you should do. J2ME applications of the recent past were all written this way, because of the resource constraints associated with the MIDP profile. But with the increase in devices processing power and memory, it is becoming very common to use MVC or a stripped-down version, called the Model-View pattern; so if your application has more than one view, or is actively writting and reading data to and from the model, you should apply the MVC pattern on it, and after the initial hassle you will notice that the code maintenance is easier and the application code is much more flexible, e.g., you can change all of your view classes, or even add new ones, and the model and the controller will remain pratically unchanged, what would not be true if everything was bundled within the View classes.

When to use views

If you application:
-  Has multiple screens that form complex navigational paths
-  Has to save data on every view switching, to update the model with the newly entered or updated data
-  Has to send data among screens or to external applications

Then you'll most likely have very good use for views and the view server. Otherwise, you can do fine with a single "screen" and some dialogs. In the cases above, views and the view server can help you by, respectively: 1) enabling partially automated view switching 2) calling methods when a given view is about to be activated or deactived, allowing you to save data at the right time 3) allowing you to send data to other application's views, such as a URL to a web browser.

Using views

A simple application, with just one view acting as container for some controls and dialogs, has the following structure:

View Server Class Structure 1
Fig. 1 - Single-view application class structure

The CViewServerApplication, CViewServerDocument, CViewServerAppUi and CViewServerAppView must be written by you and derive from the UI framework classes in order for you to have a complete GUI application. This is the basic skeleton for all Symbian GUI applications, with the view class derived from CCoeControl and containing some controls or drawing some text to the screen.

As this skeleton is for a single-view application, it does not need view switching or navigational paths, so there's no point in making it use the view server for this one, and this structure fits perfectly the application's needs.

However, let's say you want to add another view ("screen") to your application, containing other piece of information that the user might be interested on. The class structure now becomes the following:

View Server Class Structure 2
Fig. 2 - Multi-view application class structure

We have just added a CViewServerMenuView class, derived from CCoeControl (the basic class for every control in Symbian UI framework) and also linked to the CViewServerAppUi class. This is not the simplest application possible, so we have to start thinking of how you are going to perform the switching between the two views in order to provide navigation to the user.

Our first approach would be manual view switching: in CViewServerAppUi, we have to:

  1. Create a iCurrentView (of type CCoeControl* ) instance field, that will hold the view currently being displayed
  2. In HandleCommandL, we have to add logic to track the current view, switch among views, save the data modified by previous view, change the Menu and Command Button Actions (CBA).
  3. Make sure only the current view is visible, because all CCoeControls are visible by default, so we have to make sure only one of them is being displayed at a time.

This approach is expressed in the code below:

/* ====================================================================
* File: ViewServerAppUi.cpp
* Created: 07/04/05
* Author:
* Copyright (c):  All rights reserved
* ==================================================================== */

//#include section

// ConstructL is called by the application framework
void CViewServerAppUi::ConstructL() {
 BaseConstructL();

 iAppView = CViewServerAppView::NewL(ClientRect());    
 iMenuView = CViewServerMenuView::NewL(ClientRect());

 AddToStackL(iAppView);
 iCurrentView = iAppView; //keeps track of the current view
 iCurrentView->MakeVisible(ETrue);
}

CViewServerAppUi::CViewServerAppUi() {

}

CViewServerAppUi::~CViewServerAppUi() {
 if(iCurrentView) {
   RemoveFromStack(iCurrentView);       
 }
 if(iAppView) {
   delete iAppView;
   iAppView = NULL;
 }
 if(iMenuView) {
   delete iMenuView;
   iMenuView = NULL;
 }

}

// handle any menu commands
void CViewServerAppUi::HandleCommandL(TInt aCommand) {
 switch(aCommand) {
   case EEikCmdExit:
   case EAknSoftkeyExit:
     Exit();
     break;
   <b>case EViewServerChangeCmd: {
       if(iCurrentView == iAppView) {
           RemoveFromStack(iCurrentView);
          iAppView->MakeVisible(EFalse);
          //code to change the menu to that of iMenuView
          ...
          //change the CBA to that of iMenuView
          ...
          // save data from previous view
          ...
          iCurrentView = iMenuView;       
          AddToStackL(iMenuView);
          iMenuView->MakeVisible(ETrue);
       }
       else {
          //implementing the same logic to switch
          // from iMenuView to iAppView
       }
       break;
}</b>
   default:
     Panic(EViewServerBasicUi);
     break;
 }
}

As you can see, switching between to views requires a lot of thought and code to control everything that must be changed, updated, saved and drawn. For a two view application like this, it is still a valid (but cumbersome) approach. However, think of adding five more views to this application: the CViewServerAppUi would end up bloated and subtle bugs could emerge from high amount of things and behaviors you'd have to control manually.

The Symbian UI Framework View Server can make this task much easier and clean, with small modifications in the source code, explained ahead. First of all, let's see the new class structure, already updated to use the view server:

View Server Class Structure 3
Fig. 3 - Multi-view application class structure, ready to be used with the View Server

As you can see, there's only a small change in the structure: the view classes now derive also from the mixing class MCoeView, which is the interface that must be implemented for a class in order to make it be activated/deactived by the view server. You view classes must now implement these three member functions from MCoeView:

TVwsViewId ViewId();
void ViewActivatedL(const TVwsViewId& aPrevViewId,TUid aCustomMessageId,const TDesC8& aCustomMessage);
void ViewDeactivated()

ViewId() member function is called by the framework and must return a TVvsViewId object, that uniquely identifies that view within the application. Notice that the view id does not have to be unique for all applications, but only for yours, what means it can follow a simple order such as 1,2, 3 etc.

ViewActivatedL is called with three parameters: a TVwsViewId&, which holds the view id of the previous view; a TUid, which holds the id of the message being passed to this view by the previous one, and a TDesC8&, which holds the actual message being passed. For example, your view could accept the TUid "0x1" as the view title, "0x2" as an e-mail address to which it would send an e-mail, and so on. The main tasks for this method would be: make your view visible (by calling MakeVisible(ETrue)), changing the Menu and the CBA, load data from the model, etc.

ViewDeactivated is called when your view has been deactivated by the view server, that is, another view is now having the opportunity to display itself on the screen. So in this function you should: make your view invisible and save any pending data to the model.

Here follows the code for a view-server-ready view:

/* ====================================================================
* File: ViewServerAppView.cpp
* Created: 07/04/05
* Author:
* Copyright (c): , All rights reserved
* ==================================================================== */

//#includes go here

// Standard construction sequence
CViewServerAppView* CViewServerAppView::NewL(const TRect& aRect) {
 CViewServerAppView* self = CViewServerAppView::NewLC(aRect);
 CleanupStack::Pop(self);
 return self;
}

CViewServerAppView* CViewServerAppView::NewLC(const TRect& aRect) {
 CViewServerAppView* self = new (ELeave) CViewServerAppView;
 CleanupStack::PushL(self);
 self->ConstructL(aRect);
 return self;
}

void CViewServerAppView::ConstructL(const TRect& aRect) {
 // Create a window for this application view
 CreateWindowL();

 // Set the windows size
 SetRect(aRect);

 // Activate the window, which makes it ready to be drawn
 ActivateL();
}

// Draw this application's view to the screen
void CViewServerAppView::Draw(const TRect& /*aRect*/) const {
 CWindowGc& gc = SystemGc();
 TRect rect = Rect();
 TPoint point(0,0);
 const CFont* font = iEikonEnv->NormalFont();
 _LIT(KTitle,"ViewServer - View One");
 _LIT(KCredit,"by Rawsocket.org");

 gc.SetBrushColor(KRgbBlack);
 gc.Clear(rect);
 gc.SetPenColor(KRgbWhite);
 gc.UseFont(font);
 
 point.iX = rect.Width()/2 - font->TextWidthInPixels(KTitle)/2;
 point.iY = rect.Height()/2;
 
 gc.DrawText(KTitle,point);
 gc.DiscardFont();
 font = iEikonEnv->AnnotationFont();
 
 point.iX = rect.Width()/2 - font->TextWidthInPixels(KCredit)/2;
 point.iY += font->AscentInPixels() + 10;
 
 gc.UseFont(font);
 gc.DrawText(KCredit,point);
 gc.DiscardFont();
}

<b>
//returns the unique Id of this view
TVwsViewId CViewServerAppView::ViewId() const {
  return TVwsViewId(KUidViewServerApp,KViewId);
}

//sets the ordinal position of this window and makes the view visible
void CViewServerAppView::ViewActivatedL(const TVwsViewId& /*aPrevViewId*/,
                                   TUid /*aCustomMessageId*/,
                                   const TDesC8& /*aCustomMessage*/) {
 Window().SetOrdinalPosition(0);
 MakeVisible(ETrue);
}

//deactivates this view and makes it invisible
void CViewServerAppView::ViewDeactivated() {
 MakeVisible(EFalse);
}</b>

Notice the "Window().SetOrdinalPosition(0)" function call. It causes this window to be positioned in front of all other windows of the same priority as this one. It should not be mandatory, but I only could get the view server example to work using this technique. You should call it only for the first (main) view of your application, the other ones seem to have their ordinal position set automatically.

And that's pretty much everything you must do in your view classes to make them recognizable by the view server. But how the heck does the view server becomes aware of your views? The answer is that they must be registered with the view server. That can be done from the view itself (normally in its ConstructL() method), but it is more commonly done in the AppUI class, and that's the approach we take here:

/* ====================================================================
* File: ViewServerAppUi.cpp
* Created: 07/04/05
* Author:
* Copyright (c):  All rights reserved
* ==================================================================== */

//#includes

// ConstructL is called by the application framework
void CViewServerAppUi::ConstructL() {
 BaseConstructL();

 iAppView = CViewServerAppView::NewL(ClientRect());    
 iMenuView = CViewServerMenuView::NewL(ClientRect());

<b>  RegisterViewL(*iMenuView); //register view
 RegisterViewL(*iAppView); //register view</b>
   
<b>  AddToStackL(*iAppView,iAppView); //able to receive key events
 AddToStackL(*iMenuView,iMenuView); //able to receive key events</b>
 <b>SetDefaultViewL(*iAppView); //sets the default view</b>

 iCurrentView = KViewId; //tracks the current view
}

CViewServerAppUi::CViewServerAppUi() {

}

CViewServerAppUi::~CViewServerAppUi() {
 if(iAppView) {
   RemoveFromStack(iAppView);
   <b>DeregisterView(*iAppView);</b>
   delete iAppView;
   iAppView = NULL;
 }
 if(iMenuView) {
   RemoveFromStack(iMenuView);    
  <b> DeregisterView(*iMenuView);</b>
   delete iMenuView;
   iMenuView = NULL;
 }
}

// handle any menu commands
void CViewServerAppUi::HandleCommandL(TInt aCommand) {
 switch(aCommand) {
   case EEikCmdExit:
   case EAknSoftkeyExit:
     Exit();
     break;
   case EViewServerChangeCmd: {
       if(iCurrentView == KViewId) {
         iCurrentView = KMenuViewId;  
       }
       else {
         iCurrentView = KViewId;  
       }
       <b>ActivateViewL(TVwsViewId(KUidViewServerApp,iCurrentView));</b>
       break;
     }
   default:
     Panic(EViewServerBasicUi);
     break;
 }
}

First of all, after creating the views in CViewServerAppUi's ConstructL() member function, you need to register them with the view server. That is pretty simple, just call RegisterViewL() method with your views as parameters. Now the view server knows about them and will be able to activate/deactivate them when told to do so.

After that, you need to add your views that can receive key events, by using the AddToStackL() function. Note that the signature for this one is different from the previous example: first argument is a reference to a MCoeView, second is a pointer to a CCoeControl. That allows the case in which only a single control of the view can receive key events; but in this case we want all the controls receive those events, so we call the function passing a reference and a pointer to the same object.

What view must the server display first? You can set this using the SetDefaultViewL() method.

In the destructor, I must deregister the views prior removing them from the event stack and destroying them. This is done with a call to the DeregisterView() method.

In the HandleCommandL() function, I track the current view and, using the ActivateViewL() function, activate the "other" view, allowing for the behavior of switching back and forth between the views in this example.

And that's all it takes to benefit from using the view server on Symbian UI Framework.

Comments and improvements

If you need to change the Menu and the CBA for each given view, use the "ViewActivatedL()" method of MCoeView to do so. It is much easier and cleaner to do that this way, with this kind of code belonging to the view, not to the AppUi, because that reduces bloat on the AppUi, helps to separate view/presentation logic from control logic and of course, you don't need to guess what is the correct time to change the menu (before or after removing from the stack? before or after making the view visible? should I call DrawNow()? ), because the view server tells you exactly the right time to do that, which is when ViewActivatedL() is called.

Also, I really didn't need to track the current view in the AppUi. I could have built a separate menu and cba for each view and the view itself would communicate to the AppUi HandlerCommand method what is the next view to be activate, sending a different command instead of the global EViewServerChangeCmd used for both in this example. This improvement stays as an exercise to the reader :)

Passing parameters and messages to views will be explained in another article.

Using external views

In additional to intra-application view switching, you can also switch to an external application view, such as the browser or calendar, and even passing parameters to them. This is very well explained in the Utilizing External Application Views v1.0 document by Nokia. There you can find examples of activating the browser with a given URL, the Calendar with a specific date and many other examples.

Feedback

If you have anything to say about this article, found any mistakes or bugs, or simply want to comment on that, you are very welcome! :) Send me an e-mail or post a comment here.

[]s Daniel Rocha rawsocket.org

Tutorial posted July 8th, 2005 by dcrocha categories [ ]

Re: Basics on using the view server

Thanks Daniel,
Nice stuff!
I was just trying to See how ViewSrv-11 happen.
I know why it happen.(I mean by this is that suppose my UI-thread is busy in infinite loop, So how does ActiveObject of ViewSrv get event to paint.)Still it didn't clear.But this above tutorial is very nice.
yet I have not completed it.But its valuable to know.