Subclassing List Box in Symbian OS
This article explains how to subclass a list box in Symbian OS, especially in UIQ. One reason to subclass a list box is to have our own custom list box, for example you can display a double line list box (see Figure 1a). I will focus the discussion in this article on UIQ because Series 60 has already various types of list box, for instance CAknDoubleStyleListBox, CAknSingleHeadingStyleListBox, etc. In most cases, you can use one of these classes. However, in a few cases, you might need to subclass list box in Series 60 as well.
I will start this article with the architecture of list box in Symbian and then go through how to subclass a list box. At the end of this article, I will give a simple example of custom list box and how to use it (with the complete source code).
Before reading this article, I have a small comment. If you just want to port a Series 60 application that uses CAknXXXListBox to UIQ, there is a faster way. You might consider a tool called S2U, which is able to port Series 60 user interface to UIQ (see Further Reading section at the end of this article).

Architecture of List Box in Symbian
I won't discuss the architecture of Symbian list box in more detail (read one of the references at the end of this article if you want to know more about it). Like any other components in Symbian, the list box uses MVC (Model-View-Controller) model. Figure 2 shows the MVC model of the list box in Symbian.

As you can see here, the "controller" in this MVC model is CEikListBox. This is the class that you have to create to display a list box, although usually you will use one of its derived classes, for instance CEikTextListBox, CEikColumnListBox and CEikSnakingListBox.
The "model" of the list box, i.e. MListBoxModel, stores the data of your items. Basically it allows you to get and set the items of the list box. Since MLlistBoxModel is an interface, we have to create a model using one of its derived classes, for instance CTextListBoxModel.
The "view", i.e. CListBoxView, handles all the user interface stuff and displays the list box on the screen. Like the controller and the model, there are some derived classes of CListBoxView, for example CTextListBoxView.
From the controller class, CEikListBox, you can get the model using CEikListBox::Model() and get the view using CEikListBox::View(). This why you don't need to save the instance of the model and view in your application, you need to save the instance of the controller only.
How to Subclass List Box?
One of the ideas of subclassing is to have a customized "look and feel" of a control. From the MVC model explained above, you might think that we have to start with CListBoxView since it deals with the user interface. Yes, you are right. However, the real "drawing" operations are actually performed in another class derived from CListItemDrawer. You can get the instance of "item drawer" using CListBoxView::ItemDrawer().
Let's start with a simple example how to derive a class from CListItemDrawer. Assume that we want to subclass a "text" list box.
class CMyListItemDrawer: public CListItemDrawer
{
public:
CMyListItemDrawer(const CEikTextListBox& aListBox);
private: // CListItemDrawer
void DrawActualItem(TInt aItemIndex, const TRect& aActualItemRect,
TBool aItemIsCurrent, TBool aViewIsEmphasized,
TBool aViewIsDimmed, TBool aItemIsSelected) const;
private:
const CEikTextListBox& iListBox;
};
Yes, that's all you need to subclass a list box. You need a constructor and a method overridden from CListItemDrawer::DrawActualItem().
Let's take a look what is inside the constructor.
CMyListItemDrawer::CMyListItemDrawer(const CEikTextListBox& aListBox)
:CListItemDrawer(),
iListBox(aListBox)
{
// Store a GC for later use
iGc = &CCoeEnv::Static()->SystemGc();
SetGc(iGc);
}
The constructor takes a parameter of CEikTextListBox. The reason why I use CEikTextListBox is because we want to create a custom text list box. We need to store the reference to CEikTextListBox so that we can get the item text when we want to draw in DrawActualItem(). The constructor also sets the graphics context that will be used to draw the list box. Note that iGc is a member variable of CListItemDrawer, so you don't need to declare it.
The drawing operation in DrawActualItem() might look like this.
void CMyListItemDrawer::DrawActualItem(TInt aItemIndex,
const TRect& aActualItemRect, TBool /*aItemIsCurrent*/,
TBool /*aViewIsEmphasized*/, TBool /*aViewIsDimmed*/,
TBool /*aItemIsSelected*/) const
{
// Sets all the attributes, like font, text color
// and background color.
const CFont* font = CEikonEnv::Static()->NormalFont();
iGc->UseFont(font);
iGc->SetPenColor(iTextColor);
iGc->SetPenStyle(CGraphicsContext::ESolidPen);
iGc->SetBrushColor(iBackColor);
iGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
// Draws the item text.
TPtrC itemText = iListBox.Model()->ItemText(aItemIndex);
TInt baseline = (aActualItemRect.Height()
- font->HeightInPixels()) / 2
+ font->AscentInPixels();
iGc->DrawText(itemText, aActualItemRect, baseline);
}
There is nothing special in this method. Firstly, I set the attributes of the graphic context, like font, font color and background color. And secondly, I draw the text to the graphic context, iGc. Since it is a very simple example, I ignore most of the parameters, like aItemIsCurrent, aItemIsSelected, etc. In a real application, you need to consider these parameters too so that you have different colors for selected items or dimmed items.
There is a line in the code above that might be interesting, i.e. how this method gets the item text.
TPtrC itemText = iListBox.Model()->ItemText(aItemIndex);
It calls CEikTextListBox::Model() to get the model of this class, i.e. CTextListBoxModel. After that, it calls CTextListBoxModel::ItemText() to get the item text that should be drawn.
We have the "item drawer" to draw our custom list box, what's next? We have to tell our custom list box to use our "item drawer", instead of the default one. You can do this by overriding CEikTextListBox::CreateItemDrawerL(). Create an instance of the new item drawer in this method. See the following code.
class CMyListBox: public CEikTextListBox
{
private: // from CEikTextListBox
virtual void CreateItemDrawerL()
{ iItemDrawer = new (ELeave) CMyListItemDrawer(*this); }
};
That's all you need to do to subclass a list box in Symbian! To use our new custom list box, create a new instance of CMyListBox and the framework will do all the rest for you.
Example
Now it's time to take a look at a complete example. The example I will present here defines a custom list box which is able to display single line or double line items and optionally icons (see Figure 1). There is a link at the end of this article where you can download the complete source code. This custom list box is designed only as an example; do not use it in a real application.
The example of custom list box is declared in CustomListBox.h and defined in CustomListBox.cpp. The class of the custom list box is called CCustomListBox. You can create this custom list box in a similar way like you create an ordinary text list box. Firstly, create an instance of CCustomListbox.
// Creates a new custom list box.
iListBox = new (ELeave) CCustomListBox();
iListBox->ConstructL(this,
CEikListBox::ENoFirstLetterMatching
| CEikListBox::ENoExtendedSelection);
Secondly, sets the items text by calling CTextListBoxModel::SetItemTextArray().
_LIT(KItemTextDouble1, "0\tFirst item\tDescription of the first item");
_LIT(KItemTextDouble2, "1\tSecond item\tDescription of the second item");
CDesCArrayFlat* array = new (ELeave) CDesCArrayFlat(2);
CleanupStack::PushL(array);
array->AppendL(KItemTextDouble1);
array->AppendL(KItemTextDouble2);
CleanupStack::Pop();
iListBox->Model()->SetItemTextArray(array);
For now, don't bother about '\t' in the item text, I will explain it later. Basically it is the separator between the icon, the heading and the description.
Finally, if you want to set the icons for our custom list box, you can do it by calling CCustomListItemDrawer::SetIconArray().
// Creates a new icon array.
CArrayPtr<CGulIcon>* iconArray = new (ELeave) CArrayPtrFlat<CGulIcon>(2);
CleanupStack::PushL(iconArray);
iconArray->AppendL(iEikonEnv->CreateIconL(KIconFileName,
EMbmEikonFileclsd, EMbmEikonFileclsm);
CleanupStack::Pop(); // iconArray
// Sets the icon array.
CCustomListItemDrawer* itemDrawer = static_cast<CCustomListItemDrawer*>
(iListBox->View()->ItemDrawer());
itemDrawer->SetIconArray(iconArray); // transfer ownership
The SetIconArray() method will take the ownership of the icon array, so you don't need to delete it. CCustomListItemDrawer will take care of it.
The format of item text for single line list box look is "icon\ttext". For example, "0\tFirst item" means the item uses the first icon in the icon array and the text that will be displayed is "First item" (look at the first item in Figure 1b).
The format of item text for double line list box is "icon\theading\tdescription". For example, "1\tSecond item\tSecond description" means the item uses the second item in the icon array, the heading is "Second item" and the description is "Second description (look at the second item in Figure 1a).
If you don't have any icon, then simply put blank before the first tab, for example, "\tFirst item" or "\tFirst Item\tFirst description".
Download
Click the icon below to download the example of custom list box and how to use it.
Further Reading
- List Box Overview - An article from NewLC.com that explains the architecture of list box in Symbian. It also explains Series 60's list boxes in a very nice way.
- Series 60 Developer Platform: Avkon UI Resources - Listboxes v1.0 - The documentation of Series 60's list box from Forum Nokia.
- Peroon S2U - A free tool from Peroon that is able to port Series 60 application to UIQ automatically.