First | Next | Previous | Last | Glossary | About

# Programming with wxWindows - Using controls

- Adding functionality to the BasicFrame
- Adding a text control
- Adding a menu bar
- Adding a status bar
- The wxWindows classes we used
- Summary

## Introduction

In the previous session we looked at the basic architecture of wxWindows and built the most simple of applications. It didn't do much but it did demonstrate the basic functionality of a typical Windows program.

The next step is to add some more functionality so that we develop something remotely useful but most importantly, learn more about the wxWindows framework.

In this session we add a text control, a menu and a statusbar to BasicFrame. The first step is adding the text control.

Return to top of page

## Adding a text control

The application with a text control is shown here. The text control provides all the functionality of a text editor ie:

- cursor movement,
- text insert, selection and delete,
- cut, copy, paste.

and can, with some fairly straightforward programming, provide for:

- file open, save and save as,
- undo and redo operations,
- different fonts.

The simplest style of wxTextCtrl is just a single line text control which you might use in a dialog box to gather data from a user of your program. Changing the text control to something that will handle many lines is simply a matter of changing the style. Likewise changing the text control to something that will handle large amounts of data is also achieved by changing the style.

Here is the source code for our BasicFrame with a text control.

    #ifndef BASIC_H
    #define BASIC_H

    class BasicApplication : public wxApp
    {
      public:
    	virtual bool OnInit();
    };

    class BasicFrame : public wxFrame
    {
      public:
    	BasicFrame( const wxChar *title,
                    int xpos, int ypos,
                    int width, int height);
    	~BasicFrame();
        wxTextCtrl *theText;

    };
    #endif
| The header file is almost unchanged except for the line:
        wxTextCtrl *theText;
Here we declare that there will be a thing called theText which will be a pointer to a wxTextCtrl.
    #include <wx/wx.h>
    #include "basic.h"

    IMPLEMENT_APP(BasicApplication)

    bool BasicApplication::OnInit()
    {
      BasicFrame *frame
        = new BasicFrame
           ("wxWindows Basic Steps - Step 1:"
            " A simple application",
             50, 50, 200, 200);

      frame->Show(TRUE);
      SetTopWindow(frame);
      return TRUE;
    }

    BasicFrame::BasicFrame
                 (const wxChar *title,
                  int xpos, int ypos,
                  int width, int height)
                 : wxFrame( (wxFrame *) NULL, -1, title,
                            wxPoint(xpos, ypos),
                            wxSize(width, height))
    {
      theText = (wxTextCtrl *) NULL;

      theText
       = new wxTextCtrl
      	( this,
      	  -1,
      	  wxString("This is a text control\n\n"
      	           "The text control supports"
                       " basic text editing operations\n"
      	           "along with copy, cut, paste, "
                       "delete, select all and undo.\n\n"
      	           "Right click on the control"
                       " to see the pop-up menu.\n"
      	          ),
      	  wxDefaultPosition,
      	  wxDefaultSize,
      	  wxTE_MULTILINE
      	);
    }

    BasicFrame::~BasicFrame()
    {

    }
| In the constructor you can see that we create the text control with some default arguments and that there is a line:
    theText = (wxTextCtrl *) NULL;
which in this case is quite unnecessary but I have introduced just a reminder about pointer safety. As the complexity of your programs increases you will quite often declare pointers to objects that don't yet exist. Sometimes you fall into the trap of using one of these pointers before you have actually created the instance of the class you want to point at. The results of using an uninitialized pointer can range from mildly irritating to sickeningly frustrating. You could lose just seconds of your precious time or even hours. As a matter of course you should always initialize any pointer you have declared in an interface file. Other people will say "It doesn't matter, I intend to init the thing straight away!". I say - ignore them and avoid disturbing the even tenor of your life with bad pointers. I do this out of a sense of brotherly concern for your well-being. The call to the text control constructor theText = new wxTextCtrl( ... ) contains a parameter this which is a reference to the parent window. In this case this points to the parent frame that is being, or just has been, constructed.

## The text control class - wxTextCtrl

wxTextCtrl constructor
    wxTextCtrl
    ( wxWindow* parent,
      wxWindowID id,
      const wxString& value = "",
      const wxPoint& pos,
      const wxSize& size = wxDefaultSize,
      long style = 0,
      const wxValidator& validator,
      const wxString& name = "text"
    )

If you compare the constructor shown here with the example you will see that I have used a number of defaults, just as I did with the BasicFrame in the previous session.

The wxTextCtrl constructor exhibits the same kind of pattern as many of the wxWindows controls and this is a great strength of the wxWindows framework. Many of the new things you learn about the framework, for example the pattern of constructor declarations, can be used as a guide elsewhere in the framework. You've seen the wxFrame constructor, and can see that the wxTextCtrl constructor is very similar in both the order of the parameters it uses and the types of the parameters.

The wxTextCtrl introduces a new parameter that applies only for text: the wxValidator class. I won't describe the details of this class at the moment, it quite complex. Enough to say that we can use the validator to validate data which passes between program data structures and controls. You can use a validator to limit input to text only, or numeric for example.

The remaining parameters for the wxTextCtrl constructor are the same as for the wxFrame:

- wxWindow* parent - a pointer to the parent window, BUT it can't be NULL, whereas the frame could,
- wxWindowID id - an ID for the control,
- const wxString& value = "" - an initial value (see the example),
- const wxPoint& pos - the x and y location of the top-left corner,
- const wxSize& size = wxDefaultSize - the width and height dimensions,
- long style = 0 - the style (see below),
- const wxString& name = "text" - a name for the control.

#### wxTextCtrl window styles

- wxTE_PROCESS_ENTER - The control will generate the message wxEVENT_TYPE_TEXT_ENTER_COMMAND (otherwise pressing is either processed internally by the control or used for navigation between dialog controls).
- wxTE_PROCESS_TAB - The control will receieve EVT_CHAR messages for TAB pressed - normally, TAB is used for passing to the next control in a dialog instead. For the control created with this style, you can still use Ctrl-Enter to pass to the next control from the keyboard.
- wxTE_MULTILINE - The text control allows multiple lines.
- wxTE_PASSWORD - The text will be echoed as asterisks.
- wxTE_READONLY - The text will not be user-editable.
- wxHSCROLL - A horizontal scrollbar will be created. No effect under GTK+.

and to these we can add:

- wxTE_RICH - which will turn a plain text control, which has a limit of around 32K of data, into a richtext text control which can cater for goodness knows how much data. This is for Windows 95/98 only and is ignored on other platforms (for WinNT there is no limit for the plain mult-line text control).

A text control style, like a frame style, is the OR of a one or more styles, however the default style is 0, indicating that you will get a single-line text control no matter how big the width and height.

You should experiment with the wxTextCtrl styles just to get some idea of what you can and can't combine, for instance could you have a style of wxTE_MULTILINE | wxTE_PASSWORD?

To build the program:

Create the source files in a single directory

- basic.h
- basic.cpp
- basic_resources.rc
- Makefile

Run make in the directory where the files are stored

- make
- Run the program
- basic

You can download the files (wxbasic1.zip) if you wish.

## Adding a menu bar

Although the frame and text control give us quite a bit of program function we still have a few steps to take before we have anything remotely like a "real" application. The next thing to add is a menu bar so that we can prepare for our user to issue different commands to the program.

    #ifndef BASIC_H
    #define BASIC_H

    enum
    { BASIC_EXIT    =   1,
      BASIC_OPEN	= 100,
      BASIC_ABOUT	= 200
    };

    class BasicApplication : public wxApp
    {
    	...
    };

    class BasicFrame : public wxFrame
    {
      public:
    	BasicFrame( ... );
    	~BasicFrame();

        	wxTextCtrl *theText;
    	wxMenuBar  *menuBar;
    	wxMenu     *fileMenu;

    };
    #endif
| The header file is substantially the same except for the additions:
    enum
    { BASIC_EXIT    =   1,
      BASIC_OPEN	= 100,
      BASIC_ABOUT	= 200
    };
Note that I have cut some parts (shown with the ellipse ...) since they are unchanged. The enum declares some values which we will use to associate with a menu. The following lines:
    wxMenuBar  *menuBar;
    wxMenu     *fileMenu;
declare that the BasicFrame class has two new data members. One is a menubar, the other a menu. The menu bar of course is the piece that sits at the top of the frame. The menu is a drop down menu.
    #include <wx/wx.h>
    #include "basic.h"

    IMPLEMENT_APP(BasicApplication)

    bool BasicApplication::OnInit()
    { ...
      return TRUE;
    }

    BasicFrame::BasicFrame
                 ( ... )
    {
      theText = (wxTextCtrl *) NULL;
      menuBar  = (wxMenuBar *) NULL;
      fileMenu = (wxMenu *)	NULL;

      theText = new wxTextCtrl( ... );

      fileMenu = new wxMenu;
      fileMenu->Append(BASIC_OPEN,  "&Open file");
      fileMenu->Append(BASIC_ABOUT, "&About");
      fileMenu->AppendSeparator();
      fileMenu->Append(BASIC_EXIT,  "E&xit");

      menuBar = new wxMenuBar;
      menuBar->Append(fileMenu, "&File");
      SetMenuBar(menuBar);
    }

    BasicFrame::~BasicFrame()
     ...
| The additions to the implementation file are shown here. We play safety first again:
      menuBar  = (wxMenuBar *) NULL;
      fileMenu = (wxMenu *)	NULL;
We create an instance of wxMenu
      fileMenu = new wxMenu;
We use the Append() and AppendSeparator() methods to add items to the new file menu:
      fileMenu->Append(BASIC_OPEN,  "&Open file");
      fileMenu->Append(BASIC_ABOUT, "&About");
      fileMenu->AppendSeparator();
      fileMenu->Append(BASIC_EXIT,  "E&xit");
Finally we create an instance of wxMenuBar, append the file menu to it and call BasicFrame's SetMenuBar() method to add the menu bar to the frame.
      menuBar = new wxMenuBar;
      menuBar->Append(fileMenu, "&File");
      SetMenuBar(menuBar);

Note how the ampersand & is used in strings like "E&xit" and "&File". This indicates which character in a menubar or menu will be the menu hotkey.

You might think our menubar doesn't do much at the moment apart from display itself when we click on it with mouse or press Alt-F. It will soon though when we look at events, meanwhile we need to add a statusbar.

To build the program:

Create the source files in a single directory

- basic.h
- basic.cpp
- basic_resources.rc
- Makefile

Run make in the directory where the files are stored

- make
- Run the program
- basic

You can download the files (wxbasic2.zip) if you wish.

## Adding a status bar

Adding a status bar couldn't be simpler. Add the line CreateStatusBar(3);:

      SetMenuBar(menuBar);
      CreateStatusBar(3);

This will add a status bar containing 3 fields to BasicFrame. We write a string value to a field with the SetStatusBarText("string", integer) method of wxFrame. Integer is 0, 1 or 2 in the case of a 3 field status bar.

If you make a small change to the previous example, ie add a third parameter to the Append() method:

      fileMenu->Append(BASIC_OPEN,  "&Open file", "Open an existing file");
      fileMenu->Append(BASIC_ABOUT, "&About", "Who wrote this!");
      fileMenu->AppendSeparator();
      fileMenu->Append(BASIC_EXIT,  "E&xit", "Stop wasting time.");

you will see the third parameter displayed in status bar field 0.

## The wxWindows classes we used

We only used two new classes wxMenuBar and wxMenu, and we use the CreateStatusBar() method of wxFrame which took care of the creating, positioning and so on of a status bar.

I will save looking at the classes wxMenuBar and wxMenu until later, the next thing we need to do is to see how we make our program respond to menu events.

## Summary

Something to do ...
Take the simple example from this session and add some more menus to it. Why do I do things like this?
      theText = (wxTextCtrl *) NULL;
What is the difference between:
    frame->Show(TRUE);
and
    frame.Show(TRUE);

In this session we enhanced our very basic application by adding some typical windows features. The interesting thing was how little effort to took. This is a sign of a well-designed framework.

We also saw how simple it is to modify the style of the text control and gained a hint of the flexibility of the wxMenu class.

Return to top of page

First | Next | Previous | Last | Glossary | About

Copyright © 1999 - 2001 David Beech