Standard Dialogs

The base class for most Series 60 dialogs is CAknDialog . Series 60 does provide specialized dialog classes that can be used to construct forms, queries, and the like, but you can also create your own dialog classes based on CAknDialog . As you progress through this chapter, you will learn more about the specialized dialogs. It is important to note that, where appropriate, these dialog classes should be implemented in preference to writing your own dialog from scratch. There are, however, times when you will want to use a basic CAknDialog dialog ”for example, as your main application window for a dialog-based application ”so the mechanics of how to do so will be explained next .

In addition to creating a simple dialog based on CAknDialog , this section will also include information on how to create multipage dialogs, how to associate menus with your dialogs and now to insert a custom control into a dialog. The example application projects used in this section are SimpleDlg and CustomCtrlDlg .

Creating a Simple Dialog

The application SimpleDlg was introduced in Chapter 4 to explain the Dialog Architecture. It is used here to demonstrate the steps needed to create a standard dialog.

The example application is very simple ”its main application window is a dialog with a label showing the name of a game, as shown in Figure 6-1. It has an Options menu, with New Game and Exit as the menu choices. Selecting New Game from the menu causes a small dialog to appear which requests a player name, as shown in Figure 6-2. Selecting Ok, to dismiss the player name dialog, would theoretically start the game, but, as this is just a trivial example application, the associated game has not been written!

Figure 6-1. SimpleDlg main screen.


Figure 6-2. SimpleDlg player name dialog.


Defining the Resource

The key steps to create a simple dialog are to define a DIALOG resource and then create a dialog class derived from the base class CAknDialog that can execute the dialog. The resource specifies the layout of the dialog ”it is where you define the number of lines your dialog should have, the controls to be used, whether the dialog should be modal or not, the soft keys to be used, and the like. In SimpleDlg , the resource for the player name dialog is r_simpledlg_dialog , which you will find in the resource file SimpleDlg.rss :

 RESOURCE DIALOG r_simpledlg_player_name_dialog    {    flags = EEikDialogFlagNoDrag  EEikDialogFlagCbaButtons  EEikDialogFlagWait;    buttons = R_AVKON_SOFTKEYS_OK_CANCEL;    items =       {       DLG_LINE          {          id = EConfirmationNoteDlgCIdPlayerName;          type = EEikCtLabel;          control = LABEL             {             };          },       DLG_LINE          {          id = EConfirmationNoteDlgCIdPlayerNameEditor;          type = EEikCtEdwin;          control = EDWIN             {             maxlength = KMaxPlayerNameLength;             };          }       };    } 

There are three components defined for this particular dialog (you will see other options later in the chapter): flags , buttons , and items . The flags describe the properties of the dialog. In the SimpleDlg example they indicate that the dialog:

  • EEikDialogFlagNoDrag ” cannot be dragged

  • EEikDialogFlagCbaButtons ” uses soft keys

  • EEikDialogFlagWait ” is waiting

It is good practice to define a standard dialog as waiting. You should only make it nonwaiting if it is justified ”for example, in a main application window.


The buttons component specifies the soft keys to be used. As you can see from Figure 6-2, R_AVKON_SOFTKEYS_OK_CANCEL , labels the left soft key as Ok and the right soft key as Cancel .

The actual detail of what is contained in your dialog is defined by the items component. For each control you want in your dialog ”each label, edit box, and so on ”you define a DLG_LINE . In the SimpleDlg example, a label and an EDWIN control are displayed, so two DLG_LINE s are necessary. As a minimum, you should specify the following fields in each DLG_LINE :

  • id ” Use this to reference the dialog line within your application. You must enumerate in the application .hrh file.

  • type ” The type of control it contains (defined in an .HRh file, for example, avkon.hrh , or uikon.hrh ).

  • control ” The control used in this dialog line.

Unlike other Symbian OS platforms, Series 60 insists that each dialog line has a unique ID. If you do not specify one, or specify duplicates, the application will panic when you attempt to execute the dialog.


Optionally an itemflags field can be specified in the DLG_LINE resource. This field determines the behavior of the line. For example, a pop-up field text control could have itemflags , indicating that it should open a pop-up window when the Selection key is pressed ”for example, EEikDlgItemTakesEnterKey EEikDlgItemOfferAllHotKeys .

You can find values for itemflags in uikon.hrh . Apart from the flags already seen, probably the most useful is the EEikDlgItemSeparatorBefore flag, which inserts a horizontal "separator" line before the dialog line.


Writing a Dialog Class

You need to write a class that can construct and execute the dialog, initialize the data in the controls, handle the data received from them, and determine how the dialog is dismissed. Other functionality can be added to the dialog class, such as methods for validating and saving the dialog data. It is also possible within this class to initialize the dialog with values at runtime, rather than statically defining them in resource. This allows you to use the same resource for different uses of the dialog. For example, in SimpleDlg , the text of the dialog's label is set dynamically.

It is common to provide a static method to execute a dialog, which wraps up the first-phase constructor and ExecuteLD() ”the function that loads, displays, and destroys the dialog. By convention, this method is called RunDlgLD() , and it passes on the TBool that is returned from ExecuteLD() .

The dialog class illustrated by the SimpleDlg example is called CSimpleDlgPlayerNameDialog , and it inherits from CAknDialog . It provides a static RunDlgLD() method as a means of constructing the dialog:

[View full width]
 
[View full width]
TBool CSimpleDlgPlayerNameDialog::RunDlgLD(TDes& aPlayerName) { CSimpleDlgPlayerNameDialog* playerNameDialog = new (ELeave) CSimpleDlgPlayerNameDialog (aPlayerName); return playerNameDialog->ExecuteLD(R_SIMPLEDLG_PLAYER_NAME_DIALOG); }

As the dialog is modal and waiting, you do not need to make it member data, as you will not be responsible for its deletion or for ensuring that it receives key events. It can therefore be declared locally, and it will delete itself as the last step of ExecuteLD() .

It may seem strange that the dialog is not placed onto the Cleanup Stack following its construction, especially given that it is held by a local stack-based pointer to an object on the heap and that ExecuteLD() is a leaving function. However, ExecuteLD() takes ownership of the dialog, wrapping up two further method calls: PrepareLC() and RunLD() . PrepareLC() puts a pointer to the dialog onto the Cleanup Stack and then completes the construction of the dialog. RunLD() displays the dialog and pops it off the Cleanup Stack. However, if you need to call any ( potentially ) leaving code before calling ExecuteLD() , you should put the dialog onto the Cleanup Stack and pop it off before the call to ExecuteLD() .

If your dialog is nonwaiting, then it will return immediately from ExecuteLD() without being deleted. Nonwaiting dialogs do not delete themselves at all, so it is quite safe to delete them in the destructor. Note that many Series 60 notes and dialogs use self-pointers to delete themselves ”see the subsection on Progress Notes later in this chapter.


Saving and Validating Dialog Data

For a dialog to be able to update application data, it will need a reference to the data. In the SimpleDlg application, RunDlgLD() takes a reference to a descriptor, which the dialog then modifies using the value in its editor control. The reference is stored as member data by the dialog during construction. A common place to validate and update the data is in the OkToExitL() method. This function will be called by the framework when any soft key other than Cancel is pressed. (You can force the framework to call OkToExitL() when Cancel has been pressed by setting the dialog's flags to include EEikDialogFlagNotifyEsc .) OkToExitL() must return Etrue if the dialog can be allowed to exit, and return EFalse if not.

The implementation of CSimpleDlgPlayerNameDialog::OkToExitL() is shown:

[View full width]
 
[View full width]
TBool CSimpleDlgPlayerNameDialog::OkToExitL(TInt aButtonId) { if (aButtonId == EAknSoftkeyOk) { CEikEdwin* editor = static_cast<CEikEdwin*>(ControlOrNull (EConfirmationNoteDlgCIdPlayerNameEditor)); if (editor) { editor->GetText(iPlayerName); } } return ETrue; }

Remember that each line in a dialog has an ID and a control. A handle on a particular control can be obtained using CEikDialog::ControlOrNull() , passing in the ID for the dialog line. In the SimpleDlg example, EConfirmationNoteDlgCIdPlayerNameEditor is passed in to the function to get the editor control. If the ID is valid, it returns a pointer to a CCoeControl , and this must be cast to the appropriate type ”in this case, to a CEikEdwin* .

You do not take ownership of the control despite the fact that the method returns a pointer to it.


CEikDialog::ControlOrNull() returns NULL if the supplied ID is invalid. This technique is useful, for example, if a control is available only in a particular locale. If however, an invalid ID is considered to be a programming error in your application, you should use the alternative CEikDialog::Control() to get a handle on the control ”this panics if the ID is invalid.

Once a handle on the control is available, its data can be obtained. In the SimpleDlg example, the text entered by the user for the player name is retrieved using CEikEdwin::GetText() . It is at this point that any validation should be performed on the data entered. In the SimpleDlg example, no validation occurs, and iPlayerName is set to the value in the editor.

Once the contents of the dialog are considered valid, Etrue should be returned to allow the dialog to dismiss, otherwise EFalse should be returned.

Initializing a Standard Dialog Dynamically

It is a common requirement to set items in a dialog dynamically ”for example, based on data passed into the dialog. This is performed in the PreLayoutDynInitL() method. The dialog framework calls this method prior to execution of the dialog. In the example player name dialog, this is where the text for the label is set:

[View full width]
 
[View full width]
void CSimpleDlgPlayerNameDialog::PreLayoutDynInitL() { CEikLabel* label = static_cast<CEikLabel*>(ControlOrNull (EConfirmationNoteDlgCIdPlayerName)); if (label) { HBufC* labelText = StringLoader::LoadLC(R_ENTER_NAME_TEXT); label->SetTextL(*labelText); CleanupStack::PopAndDestroy(labelText); } }

A handle on the appropriate control is obtained using the ControlOrNull() method. Its value is then set using an appropriate method. In the SimpleDlg example the data is set via a call to CEikEdwin::SetTextL() , passing in a value read from the application resource file.

If necessary, you can override another method, PostLayoutDynInitL() , in order to change the layout and sizes of a dialog's controls, or if you wish to start a timer just before the dialog is displayed.


Constructing and Executing a Dialog

A RunDlgLD() method encapsulates construction and execution of a dialog. Writing one allows you to construct and execute the dialog, and also determine how it is closed via its return value, in a single step. Alternatively, the functionality encapsulated in that method can be separated out by making a call to the first-phase constructor followed by a call to ExecuteLD() .

In SimpleDlg , CSimpleDlgAppUi::RunDlgLD() is called from the AppUi, when a new game command ( ESimpleDlgCmdNewGame ) is received.

 void CSimpleDlgAppUi::HandleCommandL(TInt aCommand)    {    switch (aCommand)       {       case ESimpleDlgCmdNewGame:          {          if (CSimpleDlgPlayerNameDialog::RunDlgLD(iPlayerName))             {             StartNewGameL();             }          break;          }       ... 

As the player name dialog is waiting and modal, ExecuteLD() , and therefore RunDlgLD() , will delete the dialog on return ”in other words, when the dialog has been dismissed. The dialog can be instantiated and held as a local variable.

Determining how a dialog was closed is done by checking the return value of ExecuteLD() , or RunDlgLD() if this has been implemented. A return value of EFalse indicates that Cancel, Back or No was pressed to dismiss the dialog; ETRue is returned otherwise ”for example, by the user selecting Ok or Accept .

Series 60 has a "dialog shutter," which it uses to shut all open dialogs when it terminates an application. All dialogs must respond to an escape key by dismissing themselves ”the dialog shutter sends up to 50 escape key events ( EEikBidCancel or EKeyEscape ) to the application to ensure that all dialogs specifying the EEikDialogNotifyEsc flag are dismissed.


The SimpleDlg example shows how to create a simple dialog in an application. In practice, the particular dialog constructed here would be better implemented using a data query (see the Queries section later in this chapter), but it serves to illustrate the basic concepts involved in using dialogs. In the remainder of this chapter, you will learn about the specialized dialog classes provided by Series 60 to meet common needs. Where possible, you should use these classes in preference to creating your dialog from scratch.

Multipage Dialogs

A dialog can be split into logical sections by using a multipage dialog . This allows the presentation of a dialog in an organized manner, with labeled tabs displayed in the navigation pane to clearly indicate the purpose of each page to the user. An example is shown in Figure 6-3. The navigation pane is explained further in Chapter 5.

Figure 6-3. A multipage dialog.


The framework provides the tabs and handles them automatically ”you just need to specify a pages field instead of an items field in your DIALOG resource. You also need to make sure that the dialog fills the entire application client rectangle by specifying the EEikDialog-FlagFillAppClientRect flag. Other-wise, the tabs will appear but will be dimmed. An example resource definition for a multipage dialog is shown below:

[View full width]
 
[View full width]
RESOURCE DIALOG r_myapp_player_dialog { flags = EEikDialogFlagNoDrag EEikDialogFlagCbaButtons EEikDialogFlagFillAppClientRect EEikDialogFlagWait; buttons = R_AVKON_SOFTKEYS_OPTIONS_BACK; pages = my_pages; }

The pages field of the resource is set to equal an ARRAY resource, my_pages . The array resource should also be defined in the resource file:

 RESOURCE ARRAY my_pages    {    items =       {       PAGE          {          text = "Name";          lines = my_name_lines;          },       PAGE          {          text = "Age";          lines = my_age_lines;          }       };    } 

The ARRAY has two PAGE resources, each page containing:

  • text, which will appear on the tab.

  • lines, which refers to an ARRAY of DLG_LINE resources.

 RESOURCE ARRAY my_name_lines    {    items =       {       DLG_LINE          {          id = EMyAppDlgCIdNameLabel;          type = EEikCtLabel;          control = LABEL             {             txt = "Enter your name:";             };          },          DLG_LINE          {          id = EMyAppDlgCIdNameEditor;          type = EEikCtEdwin;          control = EDWIN             {             avkon_flags = EAknEditorFlagNoEditIndicators;             maxlength = KMaxPlayerNameLength;             };          }       };    } ... 

Within the array, the dialog lines are almost the same as they would be in a single-page dialog. The exception is that editors should have turned off their edit indicator by setting their avkon_flags to EAknEditorFlagNoEditIndicators . If the edit indicators are not switched off, they will appear on top of the tab pane, thus obscuring it. The reason for this is that they have the highest priority on the navigation pane control stack. Chapters 5 and 8 provide further details on the navigation pane editor indicators.

For clarity, the example code used here does not take account of the need for localization, so the text for various fields is supplied literally. For commercial applications, you should supply all such user-visible text in separate localized files, as described in Chapters 2 and 4.


Defining a Menu for Your Dialog

When the player name dialog is displayed in the SimpleDlg application, the soft keys are labeled Ok and Cancel , as shown in Figure 6-2. A more complex dialog may need to have a menu associated with it, the menu being activated by an Options soft key.

To do this, define the menu in a MENU_BAR resource, ensuring that it has an Exit option. Set the DIALOG resource buttons to an appropriate value ”this means one containing an Options soft key, such as R_AVKON_SOFTKEYS_OPTIONS_BACK . The resource is then passed through to the dialog in its ConstructL() method. To dynamically configure the menu you have to override DynInitMenuPaneL() .

Note that if it is necessary to override the OkToExitL() function, then the overridden version of this function is responsible for displaying the menu. To handle custom menu commands, implement the ProcessCommandL() function. Further details on menus are available in Chapter 5.

Custom Controls in Dialogs

This subsection looks at an example, CustomCtrlDlg , which shows how a custom control can be added to a dialog.

The CustomCtrlDlg example displays a dialog in its main application window, which has a label showing the name of a game, as shown in Figure 6-4. When New Game is selected from the Options menu, a dialog appears showing a custom control. The custom control in this case simply draws a "star" on the screen, as shown in Figure 6-5. The new control is defined in the class CCustomCtrlDlgCustomControl and implements the Draw() function to draw the star shape. In a real application, this could be a control written elsewhere, which could be integrated into a dialog-based application.

Figure 6-4. CustomCtrlDlg main dialog.


Figure 6-5. CustomCtrlDlg custom control dialog.


The main architecture of this application should be familiar by now, as it is identical to that of SimpleDlg , so this subsection will just concentrate on the construction of the control. The first step, as before, is to define a layout in a DIALOG resource. A custom control is added to a DLG_LINE by defining an id and type . With a custom control, there is no need to define the control= element, as you would normally. The control ID should be defined in the .hrh file, and, as this is not a standard Series 60 control, you will need to define a type for your control in the .hrh file. An enum is created for each of the custom controls. To avoid clashing with standard controls, the ID numbering starts at 1000.

 RESOURCE DIALOG r_customctrldlg_custom_control_dialog    {    flags = EEikDialogFlagNoDrag  EEikDialogFlagCbaButtons  EEikDialogFlagWait;    buttons = R_AVKON_SOFTKEYS_OK_CANCEL;    items =       {       DLG_LINE          {          id = ECustomCtrlDlgDlgCIdCustomControl;          type = ECustomCtrlDlgCtCustomControl;          }       };    } 

Creating a Custom Control in a Dialog Class

In order to display a custom control, a dialog class is created, derived from CAknDialog , that provides an overridden version of CreateCustomControlL() . In CustomCtrlDlg it is CCustomCtrlDlgCustomControlDialog that provides the implementation:

 SEikControlInfo CCustomCtrlDlgCustomControlDialog::CreateCustomControlL(    TInt aControlType)    {    SEikControlInfo controlInfo;    controlInfo.iControl = NULL;    controlInfo.iTrailerTextId = 0;    controlInfo.iFlags = 0;       switch (aControlType)       {       case ECustomCtrlDlgCtCustomControl:          controlInfo.iControl = new (ELeave) CCustomCtrlDlgCustomControl();          break;       default:          break;       }    return controlInfo; } 

The dialog uses a control factory to create the controls it contains. The dialog framework will call CreateCustomControlL() if it finds a type in the DLG_LINE resource that it does not recognize.

CreateCustomControlL() panics by default, so you must override it if you define a DIALOG with a custom control.


As the method name suggests, CreateCustomControlL() constructs the custom control on behalf of the control factory. It has a parameter, aControlType , which indicates the control type . By convention, the value of this is used in a switch statement to determine which type of control to construct. In CustomCtrlDlg , the control CCustomCtrlDlgCustomControl is constructed when the argument is ECustomCtrlDlgCtCustomControl .

Interestingly, the method returns a struct , as opposed to an object, of type SEikControlInfo . The iControl field of this struct must be populated with your custom control object and then it can be returned.

To see the custom control displayed in the dialog, run the CustomCtrlDlg application and select NewGame from the Options menu.

The first part of this chapter has concentrated on creating standard dialogs by defining a layout in a DIALOG resource and implementing a dialog class derived from CAknDialog . In the remainder of the chapter the specialized dialog classes will be introduced, and example applications containing forms, notes, queries, and list dialogs will be analyzed .



Developing Series 60 Applications. A Guide for Symbian OS C++ Developers
Developing Series 60 Applications: A Guide for Symbian OS C++ Developers: A Guide for Symbian OS C++ Developers
ISBN: 0321227220
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net