\chapter{Drawing on device contexts}\label{chapdrawing} \pagenumbering{arabic}% \setheader{{\it CHAPTER \thechapter: DRAWING ON DEVICE CONTEXTS}}{}{}{}{}{{\it CHAPTER \thechapter: DRAWING ON DEVICE CONTEXTS}}% \setfooter{\thepage}{}{}{}{}{\thepage}% \section{The concept of device contexts} Device contexts, commonly referred as DCs, represent more or less anything you can draw into, i.e. a window, a bitmap, the screen, a printer, a Postscript file, most recently even an SVG file. There is one abstract base class (wxDC) which defines the interface for all other classes so that drawing code for one device context can be used for all others as well - with certain limitation as the hardware specifies (e.g. you cannot read a pixel from a printer). \section{Drawing into windows} Let's start with the most simple case: you want to draw a line in a window. Or rather not the window, but its client area, the usually white or grey large area that is surrounded by the window's decorations such as its border which you normally would not want to draw over. In addition to defining classes that represent devices, wxWindows has a few of classes that define colours and so-called pens and brushes. A pen is used for drawing lines (which can be a curve or a rectangle) whereas brushes are used to paint areas, such as a filled rectangle or a filled circle. Indeed, you can use both at the same time for drawing a rectangle which is both filled and has a border. If you want to draw a red rectangle with a black border, you will do this: \begin{verbatim} void MyWindow::DrawSomething() { wxClientDC dc(this); dc.SetPen( *wxBLACK_PEN ); dc.SetBrush( *wxRED_BRUSH ); dc.DrawRectangle( 0, 0, 100, 100 ); } \end{verbatim} If you want to draw a rectangle without any border, you can use the special oen wxTRANSPARENT_PEN, if the rectangle is not supposed to be filled with any colour, you use the special brush wxTRANSPARENT_BRUSH. When using both these special classes, you could draw an invisible rectangle like this: \begin{verbatim} void MyWindow::DrawNothing() { wxClientDC dc(this); dc.SetPen( *wxTRANSPARENT_PEN ); dc.SetBrush( *wxTRANSPARENT_BRUSH ); dc.DrawRectangle( 0, 0, 100, 100 ); } \end{verbatim} Now what happens when you window gets obscured by another window and then returns to the surface again? The rectangle will not appear again because a window does not remember what has been drawn into it. Instead, your program has to remember what to draw and where and it will receive a so called wxPaintEvent indicating that some region has been unobscured and needs repainting. In order to catch such an event so that you can react appropriately to it, you will have to set up an event handler like this: \begin{verbatim} BEGIN_EVENT_TABLE(MyWindow, wxWindow) EVT_PAINT (MyWindow::OnPaint) END_EVENT_TABLE() void MyWindow::OnPaint( wxPaintEvent &event ) { wxPaintDC dc(this); dc.SetPen( *wxBLACK_PEN ); dc.SetBrush( *wxRED_BRUSH ); dc.DrawRectangle( 0, 0, 100, 100 ); } \end{verbatim} Note that this time, you have to use a wxPaintDC as these are used in connection with wxPaintEvents. Note also, that every such handler has to use a wxPaintDC even of you (for the moment) don't draw anything. If there is no such wxPaintDC, your program will not work under Windows. One difference between a wxPaintDC and a wxClientDC is that the wxPaintDC always sets a clipping region to the region of the window that was unobscured with the effect that all drawing commands will be clipped to that region. This leads to a reduction of flicker as only those areas of the window get redrawn, which actually need to get redrawn. \section{Querying the update region} Call me lazy: \begin{verbatim} BEGIN_EVENT_TABLE(MyWindow, wxWindow) EVT_PAINT (MyWindow::OnPaint) END_EVENT_TABLE() void MyWindow::OnPaint( wxPaintEvent &event ) { wxPaintDC dc(this); if (IsExposed( 0, 0, 100, 100)) { dc.SetPen( *wxBLACK_PEN ); dc.SetBrush( *wxRED_BRUSH ); dc.DrawRectangle( 0, 0, 100, 100 ); } } \end{verbatim}