Subclassing List Box in Symbian

in
Platforms:
Keywords:

This article explains how to subclass a list box in Symbian, 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.

doublelistbox.jpg singlelistbox.jpg

Figure 1. The example of custom list box in UIQ.

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

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.

architecturelistbox-2.jpg

Figure 2. The architecture of 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;
        };

As you can see, we 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().

CArrayPtr<CGulIcon>* iconArray = new (ELeave) CArrayPtrFlat<CGulIcon>(2);
CleanupStack::PushL(iconArray);
CCustomListItemDrawer* itemDrawer = static_cast<CCustomListItemDrawer*>
        (iListBox->View()->ItemDrawer());
iconArray->AppendL(iEikonEnv->CreateIconL(KIconFileName,
        EMbmEikonFileclsd, EMbmEikonFileclsm));
CleanupStack::Pop(); // iconArray

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.

MyListBox.zip
MyListBox.zip

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.


> Subclassing List Box in Symbian

How to i compile this example ?

> Subclassing List Box in Symbian

You should be able to compile it from command line:

MyListBox\group>bldmake bldfiles MyListBox\group>abld build wins udeb

or

MyListBox\group>bldmake bldfiles MyListBox\group>abld build armi urel

> Subclassing List Box in Symbian

can i get the source code for this custom listbox for Series 60

> Subclassing List Box in Symbian

Currently I don't have the example for S60, but it should be very similar. Btw, S60 has already many types of list boxes. In many cases, you don't need to subclass it.

Antony

>can i use numbered listbox as an options menu in series 60?

hi, recently in google map application i've seen an options menu which has nubers along with the text.i've gone through the menulistbox and i tried to do this.but im not clear in my way.so can u please clarify me? othethan using the listbox as menu item ,is there any way to set numbers n graphics in options menu?

thank you

> Subclassing List Box in Symbian

Hi,

Is it possible to place a custome control as an item in the list box ?

like instead of placing the texts and the icons, we extend our list box in a way that it allows to embed some custom control in it ?

and later the framework makes the necessary redraws and things like as when a custom control is placed in a container?

Regards, Dr. Nice Tie.

> Subclassing List Box in Symbian

How can I create the multiselection list box similar to the SMS inbox. I want check box icons in front of each item which should toggle check/uncheck. Can I do this in UIQ?

Also "0\tFirst Item\tFirst Item Description" works fine. But can I use "0\tFirst Item\t2" for inserting two icons in the same line.?

> Subclassing List Box in Symbian

I think you should be able to have check option by specifying CEikListBox::EMultipleSelection when constructing the list box.

About inserting two icons in the same line, that is possible by modifying the CCustomListBox class. This example supports only one icon in the same line.

Antony

> Subclassing List Box in Symbian

have you got success in getting two icons in same line ? if you have done this then plz guide me about this i have to add second icon at the end of the line .can you send me the code plz. thanks

> Subclassing List Box in Symbian

can i use 3 line text in listbox? if you know plz inform me

> Subclassing List Box in Symbian

i encountered an error: qikon.rh:No such file or directory. Do i need to import something to my compiler to make it work? thanks...

> Subclassing List Box in Symbian

Hi,

This article is very good and works fine. But howto make gap from the edge and icon ? What change I have to make ?

Regards Ronny

Re: Subclassing List Box in Symbian

Hi antonypranata

Thanks For Giving the Sample example For SubClassing ListBoxs.

I tried the Your Example in S60 it works fine for CEikTextListBox Class,
but i want to do it for CEikColumnListBox . Just i replaced the CEikTextListbox With CEikColumnListBox in you r Example. its Complied. But While Runnig the Application i am Getting the EIKCTL Panic 4. i Debugged Code i am Getting The Panic at This Location

CCustomListItemDrawer::CCustomListItemDrawer(const CEikColumnListBox & aListBox)
:CListItemDrawer(),
iListBox(aListBox)
{
// Store a GC for later use
iGc = &CCoeEnv::Static()->SystemGc();
SetGc(iGc);
}
in the Example That U have Given. What May the Reason For This.

Thanks

Praveen