Sven's CA-Visual Objects - Seite

23.11.2012

eMail

eMail

Übersicht | Vorheriger

Advanced Win32 API programming techniques with VO.

Warning! This advanced techniques are only for developers, which are familiar with Win32 API programming.
 

This documentation describes only the VO interface to support these techniques.
Since VO 2.6 the SDK code of all VO classes is included, so you can verify yourself the statements from here.
You can find all things, which are discussed in this section, in the 'GUI Classes SDK'.

  1. Handling of Windows notifications and messages (events).
    To support Windows notifications and messages VO subclasses the original Window classes. This means VO redirects the original window procedure of the window to his own window procedure. This happens for the following VO classes:
     
    VO Class VO window procedure Description
    Control __WCControlProc This procedure is used for all controls, except the Databrowser
    DataBrowser __WCGBChildProc
    __WCGBNotifyProc
     
    DialogWindow __WCDialogProc
    __WCDragListDialogProc
    Be aware that __WCDialogProc is the dialog procedure and not the window procedure.
    __DocApp __WCDocAppWndProc Used for MDI child windows - created in ChildAppWindow
    ShellWindow __WCShellWndProc  
    TopAppWindow __WCTopAppWndProc  
    __WndApp __WCWndAppWndProc Used for application windows or child windows - created in ChildAppWindow

    All VO window procedures create an Event{} object and send this to the Dispatch() method of the corresponding class.
    Inside of the Dispatch() method VO handles the Windows notifications and messages. For many of this messages the Dispatch() method generates a specialized version of the Event{} object (KeyEvent{}, MouseEvent{}, ControlEvent{} ...) and send it to specialized methods of the class (KeyDown(), KeyUp(), MouseButtonClick() ...). We will name this methods  'VO event methods' later in this text.

    A lot of the Windows notifications and messages (events) can have or need a return value. VO has a preferential way for setting the event return value. It is simply setting the value of the EXPORT instance variable SELF:EventReturnValue. You have to do this in one of the VO event methods or in the Dispatch() method. The data type of SELF:EventReturnValue is LONG and the default value of SELF:EventReturnValue is always 0l.

    No rule without exception. Two exceptions to this rule exist:
     

    1. If you must return a return value of 0l in a VO event method of the class Control or one of its sub classes you have to return the method itself with the code RETURN 0l . Since VO 2.7 you can return the event return value generaly via the return statement in all control classes. The return value must be a LONG value. Other return value types have no effect and the value of the instance variable SELF:EventReturnValue is returned by default. This works only for control classes.
       
    2. If a control notify event method is called from the owner window and must return a value. Currently only three control methods exist, which are all called from the Window:ControlNotify() method.

      Control:CustomDraw(), ListView:__FindItem() and Control:ParentNotify()

      The reason for this exception is, that you have no access to the EventReturnValue variable of the window from which one of the above notify messages are coming.

    If you write your own Dispatch() method, you have to aware of the following, if you want return the correct event return value.

    METHOD Dispatch(oEvent) CLASS YourControl
      
    //a class which is an instance of class Control
      
    LOCAL oEvt AS Event

       oEvt := oEvent

      
    IF oEvt.uMsg = ....
         
    .....
         
    SELF:EventReturnValue := your value
         
    RETURN 1l
      
    ENDIF

       RETURN SUPER
    :Dispatch(oEvt)

    and

    METHOD Dispatch(oEvent) CLASS YourWindow
      
    //a class which is an instance of class Window
      
    LOCAL oEvt AS Event

       oEvt := oEvent

       IF oEvt.uMsg = ....
          .....
          SELF:EventReturnValue := your value
          RETURN SELF:EventReturnValue
       ENDIF

       RETURN SUPER
    :Dispatch(oEvt)
     

    In addition, you take into account, that you code each access to Event objects with strong typing to avoid type conversion mistakes of the VO compiler. This is the reason for the construct:

    METHOD Dispatch(oEvent) CLASS YourWindow
      
    LOCAL oEvt AS Event

       oEvt := oEvent

    If VO has no own event handler for a received event, VO calls the orginal windows procedure to process this event. This call is inside the Default() method for all window classes and since VO 2.7 also for all control classes. Sometimes it is very useful to process the default windows event processing befor your code begins. Simply call SELF:Default(oEvt) in your code. The method SELF:Default() set the EventReturnValue variable.

  2. Custom Draw.
    Custom Draw is not a common control; it is a service that many common controls provide. Custom Draw services allow an application greater flexibility in customizing a control's appearance. Your application can harness custom draw notifications to easily change the font used to display items or manually draw an item without having to do a full owner draw.

  3. All common controls that support custom draw send NM_CUSTOMDRAW notification messages at specific points during drawing operations. These notifications describe drawing operations that apply to the entire control as well as drawing operations specific to items within the control. Like many notification messages, NM_CUSTOMDRAW notifications are sent as WM_NOTIFY messages.
    This way is not very compfortable for VO developers, because they have to write the drawing code inside the owner window of the control.
    VO 2.7 goes a more compfortable way. It has a build in WM_NOTIFY handler, which redirects all NM_CUSTOMDRAW notifications back to the sending control by calling the method Control:CustomDraw().
    So the only thing you have to do, if you want use custom drawing for your control, is to write a CustomDraw() Method for your Control

    CLASS MyListView INHERIT ListView

    METHOD CustomDraw(lParam) CLASS MyListView
      
    LOCAL sNMLVCUSOMDRWAW AS _winNMLVCUSOMDRWAW
       
       sNMLVCUSOMDRWAW :=
    PTR(_CAST, lParam)

      
    ....

      
    RETURN CDRF_DODEFAULT

    This method get only the lParam parameter of the calling notification message.

    The lParam parameter will be the address of an _winNMCUSTOMDRAW structure or a control-specific structure that contains an _winNMCUSTOMDRAW structure as its first member. The following table illustrates the relationship between the controls and the structures they use.
  4. Structure Used by
    _winNMCUSTOMDRAW Rebar, trackbar, and header controls
    _winNMLVCUSTOMDRAW List-view controls
    _winNMTBCUSTOMDRAW Toolbar controls
    _winNMTTCUSTOMDRAW ToolTip controls
    _winNMTVCUSTOMDRAW Tree-view controls

    You return the CustomDraw() method with one of the custom drawing return values (for example: CDRF_DODEFAULT)

    You find more details about Custom Draw in the Platform SDK documentation. It is free and can be downloaded from the Microsoft homepage.

  5. Owner Drawing
    This is an other technique to customize the appearance of a control or a menu.
    It is based on the messages WM_DRAWITEM, WM_MEASUREITEM and only for menus WM_MENUCHAR.
    These messages are send to the the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed. Also some common controls supports Owner Drawing: TabControl, ListView and HeaderControl.
    To enable Owner Drawing a control specific window style have to be set. (Button -> BS_OWNERDRAW).
    VO 2.7 redirects the messages WM_DRAWITEM, WM_MEASUREITEM and WM_MENUCHAR back to the sending control (or menu) by calling the corresponding methods Control:ODDrawItem(), Control:ODMeasureItem() or for menus  Menu:ODMenuChar().
    So you can implement your Owner Drawing code direct inside the control.

    CLASS MyButton INHERIT PushButton

    METHOD ODDrawItem(oEvent) CLASS MyButton
      
    LOCAL oEvt      AS Event
      
    LOCAL sDrawItem AS _winDRAWITEMSTRUCT
      
    LOCAL hDC       AS PTR

       oEvt      := oEvent
       sDrawItem :=
    PTR(_CAST, oEvt:lParam)
       hDC       := sDrawItem .hDC

       //your drawing code

       RETURN NIL

    METHOD ODMeasureItem(oEvent) CLASS MyButton
       LOCAL oEvt         AS Event
       LOCAL sMeasureItem AS _winMEASUREITEMSTRUCT
      
       oEvt          := oEvent
       sMeasureItem  := PTR(_CAST, oEvt:lParam)

       //your measuring code

       RETURN NIL


    or for menus

    CLASS MyMenu INHERIT Menu

    METHOD ODDrawItem(oEvent) CLASS MyMenu
       LOCAL oEvt      AS Event
       LOCAL sDrawItem AS _winDRAWITEMSTRUCT
       LOCAL hDC       AS PTR

       oEvt      := oEvent
       sDrawItem := PTR(_CAST, oEvt:lParam)
       hDC       := sDrawItem .hDC

       //your drawing code

       RETURN NIL

    METHOD ODMeasureItem(oEvent) CLASS MyMenu
       LOCAL oEvt         AS Event
       LOCAL sMeasureItem AS _winMEASUREITEMSTRUCT
      
       oEvt         := oEvent
       sMeasureItem := PTR(_CAST, oEvt:lParam)

       //your measuring code

       RETURN NIL

    METHOD ODMeasureItem(oEvent) CLASS MyMenu
       LOCAL oEvt      AS Event
       LOCAL hMenu     AS PTR
       LOCAL wKey      AS WORD
       LOCAL wMenuType AS WORD

       oEvt      := oEvent
       hMenu     := PTR(_CAST, oEvt:lParam)
       wKey      := LoWord(oEvt:wParam)
       wMenuType := HiWord(oEvt:wParam)  //can be MF_POPUP or MF_SYSMENU

       RETURN lRet
      
    //lRet is the long event return value of this message handler
       //(MNC_IGNORE, MNC_CLOSE, MNC_EXECUTE or MNC_SELECT)


    All methods are getting the oEvent parameter which is an Event object of the original message.

    You find more details about Owner Drawing and the messages WM_DRAWITEM, WM_MEASUREITEM and WM_MENUCHAR in the Platform SDK documentation. It is free and can be downloaded from the Microsoft homepage.

  6. Unsupported control notify messages (WM_NOTIFY)
    The Windows API is a very large library and it is natural, that the VO GUI Classes can't support all possible notify messages.
    Since Version 2.7, VO has an easy mechanism to get these messages inside the control which have sent this notify message.
    Because all notify messages were send from a control the Window:ControlNotify() dispatcher redirects all unsupported notify messages back to the calling control to the method Control:ParentNotify(), if implemented.

    CLASS MyListView INHERIT ListView

    METHOD ParentNotify(nCode, lParam) CLASS MyListView
       
      
    //Your code

      
    RETURN lRet //lRet is the long event return value of this message handler

    The paramter nCode is the notification code and lParam is the lParam parameter of the original WM_NOTIFY message (in many cases the value of lParam is an address of a structure) . (Notice: It is possible to implement a ParentNotify() method of the base class Control.)
     
  7. Unsupported window messages of the VO Window class.
    The Windows API is a very large library and it is natural, that the VO GUI Classes can't support all possible window messages.
    Generally you have to write your own Dispatcher() method in a subclass of the class Window to get these messages.
    If you want to implement a more general issue, which should work for all kind of windows this way is very laborious, because you have to implement the same code in many classes.
    Since Version 2.7, VO redirects all unsupported messages to the method DispatchUnkown(), if this method is implemend. So it is possible to write a DispatchUnkown() method for the base class Window.

    METHOD DispatchUnkown(oEvent) CLASS Window

      
    //Your code, a possible event return value
       //have to be assigned to SELF:EventReturnValue


      
    RETURN NIL
     
  8. Drawing of the control background
    Since VO 2.7 you can write your own method PaintBackground() to paint the control background. If a method with this name exists, VO calls this method if it receives a WM_ERASEBKGND message. Because this method is not implemented in the GUI Classes library, you are able to write your own PaintBackground() method for the base class Control with the result that this method works for all controls.

    METHOD PaintBackground(hdc) CLASS Control
      
    //or any class which inherits from class Control

      
    //Your background drawing code

      
    RETURN TRUE

    The argument hdc is a pointer value which includes the handle of the windows device context to use with drawing operations.
    The method should return TRUE, if you have painted the background or FALSE if you did not.
     
  9. Drawing of the window background
    It exists a method Window:PaintBackground() which processes the drawing of the window background, but only if you have assigned a background brush to the window. If you want use your own background drawing you can implement a method named DrawBackground(). This method is called, if your window has no assigned background brush otherwise PaintBackground() becomes called.

    METHOD DrawtBackground(hdc, oWindow) CLASS YourWindow
       //or any class which inherits from class Window

      
    //Your background drawing code

      
    RETURN TRUE

    The argument hdc is a pointer value which includes the handle of the windows device context to use with drawing operations.
    The argument oWindow is the window object which has generated the WM_ERASEBKGND message. The oWindow object is normally identical with SELF except when SELF is a DataWindow. In this case oWindow can be the oSurface (__FormDialogWindow) object. In a DataWindow you should draw the background only if oWindow == oSurface, otherwise it is superfluous.
    The method should return TRUE, if you have painted the background or FALSE if you did not.


     
Home | Kontakt | Impressum | ©2012 Ingenieur-Büro Dipl. Ing. Sven Ebert