16 August 2021

Fill up the Form (Part 3)

This is part 3 of my blogs on GFA-BASIC 32 Forms. The other parts focused on creating and interacting with a Form:

In this blog I focus on drawing in the form’s client area.

Putting something on the Form
Basically, a form is used for two purposes. It is either used as a dialog box by populating it with OCX-controls, or it is used to draw graphics. Usually, these basic approaches aren’t combined, although you could draw some graphics in the dialog box form as a background for the controls. For instance, you could assign a Picture to draw in the form’s background or change the background color for the form. However, mostly these things are separated; a form is either used as an input form or as a graphics window.
When a GB32 form contains OCX controls it’s behavior changes and the form starts handling control-navigation keys (Tab and arrow-keys). This means that both the system and GB32 take control of the focus. The focus can only be set to a control, not to the client area of the form. The form’s method SetFocus does not work anymore. A form without OCX controls does not change to this behavior and allows you to set the focus to the form, making it possible to develop a text editor for instance. (BTW it is a good habit to add an OCX-Form child window to the client-area of the base-form. The base-form is the container of the OCX-controls and the OCX-Form control is used for drawing or editing.)

Drawing on a Form
An application should paint in the client area when Windows posts (or sometimes sends) a WM_PAINT message. GB32 converts the WM_PAINT message to an event which can be processed in the _Paint event sub. There are applications (like drawing programs) that need to draw interactively in the client area, for instance after a mouse-event. Even so, an application would store the new drawing coordinates and color and then updates its client area to redraw in the _Paint event sub again. This is achieved by invalidating the portion of the form that needs to be redrawn followed by a form-refresh.

frm.Invalidate x, y, w, h
frm.Refresh

The Refresh method calls the UpdateWindow() API and sends a WM_PAINT message to the form directly, bypassing the message queue. By following this procedure, the form is repainted instantly and the app conforms to the rules of painting in the client-area.

Should you use AutoRedraw?
To make things easy VB naively introduced the AutoRedraw feature. Because of VB-compatibility AutoRedraw was also built into GB32. The AutoRedraw feature allows an application to draw outside the _Paint event sub. All subsequent drawings are copied to an offscreen bitmap which is then drawn to the client-area after the form receives a WM_PAINT message. There are some drawbacks to this approach. The application does no longer follow the UI-rules for drawing and it will be difficult to maintain and expand the application. A second drawback is the drop in performance. Each GB32 graphics command is executed twice, once to draw on the screen and once to draw in the offscreen bitmap. In addition, the drawing is limited to the use of GFA-BASIC 32 commands, only GB32 commands draw in the offscreen bitmap. When you use a Windows API drawing function to draw you must draw both on the screen and on the bitmap. For this, the form provides the hDC2 property, this property returns the handle of the GDI device context for the offscreen bitmap. It is only valid after executing the AutoRedraw command. The use of an API function could look like this:

~TextOut(frm.hDC, 0, 0, "Hello, Windows!", 15)
If frm.hDC2
  ~TextOut(frm.hDC2, 0, 0, "Hello, Windows!", 15)
EndIf

If the form uses scaling, the scaling must be applied by hand. Each coordinate and size value must be converted to the current scaling of the form.
Another thing to note is the size of the offscreen bitmap. The bitmap is created when the AutoRedraw command is executed taking the current size of the client area. When a form is later enlarged (resized) the automatic redrawing of the offscreen bitmap will not entirely fill up the client area of the form. This is demonstrated in the following picture where the picture on the right is a resized version of the left form.

AutoRedraw

AutoRedraw is an option to use for a quick and dirty test program, not for a serious application.

Use Direct2D instead
The GB32 drawing commands are wrappers for the Windows GDI drawing functions. However, GDI isn’t fast and isn’t suited for dpi-aware applications. More and more GDI-drawing is replaced by Direct2D drawing. Your application can use Direct2D as well. The Include directory contains a GB32 library Direct2D.lg32 that provides access to the system’s COM-based direct2d libraries. The commands and functions provide a GB32 compatible approach to drawing, so an application can be ported to Direct2D easily. The library commands and functions start with the letters D2 followed by the GB32 name of the drawing command. To set the colors you would use D2RgbColor instead of GB32’s RgbColor command. Instead of Line you would use D2Line, etc. The usage of the library is explained in the latest (English) CHM helpfile (don’t use the very old German hlp file anymore, it is outdated and does not contain OCX-properties and all the new stuff.)  You’ll find the Direct2D information by going to the Contents-tab and opening the Creating an application chapter.

Conclusion
An application either uses the form for input using OCX-controls or draws in the client-area in a response to a _Paint event. It is recommended to not use AutoRedraw. Even better, use Direct2D for drawing.