Programming Guide:xppguide

Using windows as output devices Foundation

In the discussion of windows serving as output devices it is necessary to distinguish an application window (as provided by the XbpCrt and XbpDialog classes) from all other Xbase Parts having a visual representation. These, of course, are windows as well. The easiest way to achieve graphic output is by using an XbpCrt window. It serves as device context for its specialized presentation space and performs buffered screen output. Therefore, Gra..() functions can be used to draw in an XbpCrt window without the necessity of reacting to the xbeP_Paint event. This event is created by the operating system when a window needs to (partially) repaint itself.

Since an XbpDialog window does not buffer screen output, it must react to the xbeP_Paint message if Gra..() functions are used to draw in the window. The same is true for all other Xbase Parts. Regarding screen output, an XbpDialog window does not differ from a pushbutton or an XbpStatic object. The only difference is that an XbpDialog window is a compound object and drawing must be done in the drawing area below the title bar of the window ( XbpDialog:drawingArea). In an XbpStatic element, for example, screen output appears in the object itself.

In order to enable an Xbase Parts to react to the xbeP_Paint event, a code block must be assigned to the :paint instance variable. This code block must call a routine where graphic output is implemented. As an alternative, a user-defined class can be derived from an Xbase Part. In this case, the :paint() method must be overloaded and code for graphic output must be implemented in this method. Either possibility requires a presentation space to be associated with the Xbase Part when Gra..() functions are called. This can be a Micro presentation space returned by the :lockPS() method, or an XpbPresSpace object which is linked to the device context of the Xbase Part. It is the return value of the :winDevice() method.

The different aspects of graphic output in windows, or Xbase Parts, are illustrated in the example program CHARTS.PRG which is located in the ..\SOURCE\SAMPLES\GRAFIC directory. This program draws a simple line chart by connecting a set of points with lines using GraLine():

 // x and y values for a line chart in the range 0-300 
 aValues := ; 
   { { 30, 184}, ; 
     { 60,  84}, ; 
     { 90, 144}, ; 
     {120, 254}, ; 
     {150, 170}, ; 
     {180, 235}, ; 
     {210, 289}, ; 
     {240, 190}, ; 
     {270, 152}, ; 
     {300,  36}  } 

LineChart( , aValues ) 

PROCEDURE LineChart( oPS, aPoints ) 
   GraPos( oPS, {0,0} ) 
   AEval( aPoints, {|aPos| GraLine( oPS, , aPos )} ) 

This code would already work if an XbpCrt window was being used for display. The procedure LineChart() simply receives an array of values specifying xy coordinates for the points to be connected with lines. However, the CHART.PRG example program uses an XbpDialog and an XbpStatic object for display.

Output of the sample program CHART.PRG

The program draws the line chart first in the XbpDialog window and then in reduced size in the XbpStatic object (frame). The program code that displays the XbpDialog is given below. It consists of creation of the window, the definition of the :paint code block, display of the window and an event loop:

// Create window hidden 
oDlg := XbpDialog():new(,,{10,10},{400,400},,.F.) 
oDlg:title := "Line chart" 

// Define :paint code block 
oDlg:drawingArea:paint := ; 
    {|mp1,mp2,obj| mp1 := obj:lockPS(), ; 
             LineChart( mp1, aValues ), ; 
                   obj:unLockPS()       } 

// Display window 

// Process events 
DO WHILE nEvent <> xbeP_Close 
   nEvent := AppEvent( @mp1, @mp2, @oXbp ) 
   oXbp:handleEvent( nEvent, mp1, mp2 ) 

The :paint code block must be assigned to the drawing area of the XbpDialog object ( oDlg:drawingArea) because it displays the drawing. The code block calls the LineChart() procedure which draws the chart. Evaluation of the code block, however, takes place in the :handleEvent() method when the xbeP_Paint event is returned from AppEvent(). The following steps lead to the display of the line chart:

Calling oDlg:show() creates an xbeP_Paint event in the event queue 

AppEvent() returns xbeP_Paint and oXbp contains oDlg:drawingArea 

oXbp:handleEvent( xbeP_Paint, mp1, mp2 ) 
evaluates the :paint code block 

Eval( oXbp:paint, mp1, mp2, oXbp ) displays the line chart 

The final result of the xbeP_Paint event is that oDlg:drawingArea is passed to the :paint code block as third parameter. Inside the code block, a Micro PS is requested using :lockPS() which gets passed together with aValues to the LineChart() procedure. Calling a drawing routine in this way via the :paint code block also guarantees that the procedure is executed again when the window is covered temporarily by another one and regains focus. In this case, the operating system creates a new xbeP_Paint event and the :paint code block is evaluated again.

Displaying the line chart in the XbpStatic object follows the same logic: LinChart() is called from the :paint code block. But instead of a Micro PS, an XbpPresSpace object is used which is linked to the window device context of the XbpStatic object:

// Create static frame inside oDlg:drawingArea 
oXbp      := XbpStatic():new( oDlg:drawingArea,,{270,250}, {80,80} ) 

// Link presentation space to window device 
oPS := XbpPresSpace():new() 
oPS:create( oXbp:winDevice(), {310,310} ) 

// Set viewport to the size of the frame 
oPS:setViewPort( {0,0,80,80} ) 

oXbp:paint := {|| LineChart( oPS, aValues ) } 

The line chart is drawn inside the XbpStatic object at the exact same coordinates as in the XbpDialog object. The reason why it is completely visible is given by page size and viewport of the presentation space. The page is large enough to display all points (310*310 pixel size vs. range of 0-300). The page is scaled to the viewport which is as large as the static frame (80*80 pixel). Therefore, the chart is reduced in size but completely visible.


If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.