How to put a background image in a panel in wxWidgets

These days I was working in a program that will use wxWidgets framework.

Basically, the application will have many panels and all of them must have a background image.

In my mind I had 2 options:

  • for each panel I could put a background image in wxPanel plus all the controls that each panel required
  • create a base class derived from wxPanel that would contain the background image and make it the base class for each panel in the application.

The second approach is better because that way I’d eliminate code duplication. That was the path I followed.

How would that work? When a class that inherited from BaseScreen were created, always the paint event of BaseScreen would be called prior to the paint event from the child class.

This way the image would be draw in on top of it the controls from child class would be draw. It would give the effect of having a background image.

Bellow you can see both the code of BaseScreen and MainScreen (the child of BaseScreen)

Listing 1 – Header of BaseScreen (original version)

Listing 2 – Implementation of BaseScreen (original version)

Listing 3 – Header of MainScreen (original version)

Listing 4 – Implementation of MainScreen (original version)

You can see the result in picture 1. It seems that the background image is being draw over the controls. But why? Background image is draw in the base class and controls belongs to derived class. The paint event of base class should have been called prior paint event of child class.
wxProblem-01Picture 1 – Components from base class seems to be draw after the ones from child class
After struggling a couple of days I could not figure out what was causing this problem so I create the thread Base class drawing over derived class components in wxWidgets forum.

Fortunately, a fellow from forum kindly pointed me the right direction. I wouldn’t have solved this problem without his help, so he deserves all kudos.

He explained me that wxStaticBitmap would be treated by wxWidgets as a sibling of all other controls. According to him, in order to put controls on top of other images I had 2 options:

  • To make all controls be children of the wxStaticBitmap.
  • Make my base class derive from wxCustomBackgroundWindow< wxPanel > class the SetBackgroundBitmap() function set the background image.

A got the second route. Bellow you can see my new version of BaseScreen class and the result I got.

Listing 5 – Header of BaseScreen (second version)

Listing 6 – Implementation of BaseScreen (second version)

By inspecting picture 2 you can see that background image is not over components from child class. But now we have another problem: as the image I’m using is not of the exactly the same size of it’s parent window, it’s been draw more than once to fill the parent window. wxProblem-02Picture 2 – Background image is draw more than once in order to fill parent window

Note: This problem doesn’t happens when the image used by you has the same dimensions as its parent windows.

OK, so SetBackgroundBitmap() still wasn’t what I needed. I had take care of paint() function myself.

I changed the BaseScreen source code so I could handle painting as showed in listing 7.

You can see that now I’m responsible for positioning the image in the window. You can tweak this function to control the positioning of the component as you see it fits.

Note: in my case, user cannot redraw the window, so I could left the calculus of windows position out of pain() function. In case users were able to re-dimension the windows of your application, you must perform the calculus of image position in the paint() function.

Listing 7 – Painting function of BaseScreen

The full version source code of BaseScreen can be seen in listing 8 and 9.

Listing 8 – Header of BaseScreen (third version)

Listing 9 – Implementation of BaseScreen (third version)

You can see in picture 3 that I messed up something related to painting events.

wxProblem-03Picture 3 – wxStaticText is not painting letting it’s background as a transparent one.

I thought I could call SetBackgroundStyle(wxBG_STYLE_TRANSPARENT) on wxStaticText so I changed the code in constructor of MainScreen as in listing 10.

Listing 10 – 1st try to set the transparency for background of wxStaticText

Unfortunately it generated a runtime error:

So changed the constructor of MainScreen to the following:

Great! No morer runtime errors… but I saw no difference now to image on picture 3. Taking a look in the wxWidgets source code I discovered that flag wxBG_STYLE_TRANSPARENT only works on Mac.

Oh, gosh. Then I decided to take another approach: I created a custom class that inherits from wxStaticText and that will take care of it’s own painting routine.

In listing 11 and 12 you can see the final version of MainScreen and on listing 13 and 14 you see the full code of the custom class I created (I named it TransparentStaticText).

Listing 11 – Header of MainScreen (second version)

Listing 12 – Implementation of MainScreen (second version)

Listing 13 – Header of TransparentStaticText (first version)

Listing 14 – Implementation of TransparentStaticText (first version)

OK. Finally everything is working as I wanted it to. Picture 4 shows the final result.

wxProblem-04

Picture 4 – Now everything is working fine

In order to ease your life, I created a project into github. It contains full source code and is meant to be run on Eclipse under Windows but you can easily use other IDE or adapt the Eclipse project to run on Linux / Mac.

Please, try my games, play free on-line games on my site, tweet this post url and share it on facebook, google+ and other social medias.

This entry was posted in development, other and tagged , , . Bookmark the permalink.
  • Gonzo

    Thanks a milion man!!! I have been searching for quite a long time a tutorial like this.
    Great job!!