Announcement

Collapse

New Sub-Forum

In an effort to help make sure there are appropriate categories for topics of discussion that are happening, there is now a sub-forum for databases and database programming under Special Interest groups. Please direct questions, etc., about this topic to that sub-forum moving forward. Thank you.
See more
See less

Managing ~50 TextBoxes ...

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Managing ~50 TextBoxes ...

    I'm in the middle stage of conversion of huge database project written in VB to PBWIN.
    Now, stuck in conversion of GUI logic with approx 50 textbox controls.
    They're all have following methods:
    • Active textbox have different border and background color
    • ENTER key used to accept/validate input
    • Up and Down arrow keys used to switch to another control
    • F1 and F5 keys used to activate drop down lists/treeviews/calendar/forms etc.
    • DblClick used to show additional information
    • Tooltips for quick info

    In VB code all textbox controls groupped into control array with their indexes, so few subroutines and events doing all stuff above.

    I want to know how to subclass 50 textboxes without declaring of 50 subroutines to manage all listed methods.

    Thanks,
    Aslan.

  • #2
    50 textboxes is a lot if you ask me. Especially if they all hold basically the same data (As seen with below sample that they all look the same except the #)

    Anyways, you don't need 50 functions all doing the same thing, just one function and pass a parameter that says which control you want to do something with.

    Just for starters, try the below and you will see what I mean.
    Code:
    #PBFORMS CREATED V1.51
    '------------------------------------------------------------------------------
    ' The first line in this file is a PB/Forms metastatement.
    ' It should ALWAYS be the first line of the file. Other
    ' PB/Forms metastatements are placed at the beginning and
    ' end of "Named Blocks" of code that should be edited
    ' with PBForms only. Do not manually edit or delete these
    ' metastatements or PB/Forms will not be able to reread
    ' the file correctly.  See the PB/Forms documentation for
    ' more information.
    ' Named blocks begin like this:    #PBFORMS BEGIN ...
    ' Named blocks end like this:      #PBFORMS END ...
    ' Other PB/Forms metastatements such as:
    '     #PBFORMS DECLARATIONS
    ' are used by PB/Forms to insert additional code.
    ' Feel free to make changes anywhere else in the file.
    '------------------------------------------------------------------------------
    
    #COMPILE EXE
    #DIM ALL
    
    '------------------------------------------------------------------------------
    '   ** Includes **
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN INCLUDES
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "WIN32API.INC"
    #ENDIF
    #PBFORMS END INCLUDES
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Constants **
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN CONSTANTS
    %IDD_DIALOG1   =  101
    %IDC_TEXTBOX1  = 1001
    %IDC_TEXTBOX2  = 1002
    %IDC_TEXTBOX3  = 1003
    %IDC_TEXTBOX4  = 1004
    %IDC_TEXTBOX5  = 1005
    %IDC_TEXTBOX6  = 1006
    %IDC_TEXTBOX7  = 1007
    %IDC_TEXTBOX8  = 1008
    %IDC_TEXTBOX9  = 1009
    %IDC_TEXTBOX10 = 1010
    %IDC_TEXTBOX11 = 1011
    %IDC_TEXTBOX12 = 1012
    %IDC_TEXTBOX13 = 1013
    %IDC_TEXTBOX14 = 1014
    %IDC_TEXTBOX15 = 1015
    %IDC_TEXTBOX16 = 1016
    %IDC_TEXTBOX17 = 1017
    %IDC_TEXTBOX18 = 1018
    %IDC_TEXTBOX19 = 1019
    %IDC_TEXTBOX20 = 1020
    %IDC_TEXTBOX21 = 1021
    %IDC_TEXTBOX22 = 1022
    %IDC_TEXTBOX23 = 1023
    %IDC_TEXTBOX24 = 1024
    %IDC_TEXTBOX25 = 1025
    %IDC_TEXTBOX26 = 1026
    %IDC_TEXTBOX27 = 1027
    %IDC_TEXTBOX28 = 1028
    %IDC_TEXTBOX29 = 1029
    %IDC_TEXTBOX30 = 1030
    %IDC_TEXTBOX31 = 1031
    %IDC_TEXTBOX32 = 1032
    %IDC_TEXTBOX33 = 1033
    %IDC_TEXTBOX34 = 1034
    %IDC_TEXTBOX35 = 1035
    %IDC_TEXTBOX36 = 1036
    %IDC_TEXTBOX37 = 1037
    %IDC_TEXTBOX38 = 1038
    %IDC_TEXTBOX39 = 1039
    %IDC_TEXTBOX40 = 1040
    %IDC_TEXTBOX41 = 1041
    %IDC_TEXTBOX42 = 1042
    %IDC_TEXTBOX43 = 1043
    %IDC_TEXTBOX44 = 1044
    %IDC_TEXTBOX45 = 1045
    %IDC_TEXTBOX46 = 1046
    %IDC_TEXTBOX47 = 1047
    %IDC_TEXTBOX48 = 1048
    %IDC_TEXTBOX49 = 1049
    %IDC_TEXTBOX50 = 1050
    #PBFORMS END CONSTANTS
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Declarations **
    '------------------------------------------------------------------------------
    DECLARE CALLBACK FUNCTION ShowDIALOG1Proc()
    DECLARE FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
    DECLARE FUNCTION DoSomethingWithClick(ControlClicked AS LONG) AS LONG
    #PBFORMS DECLARATIONS
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Main Application Entry Point **
    '------------------------------------------------------------------------------
    FUNCTION PBMAIN()
        ShowDIALOG1 %HWND_DESKTOP
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** CallBacks **
    '------------------------------------------------------------------------------
    CALLBACK FUNCTION ShowDIALOG1Proc()
    
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                ' Initialization handler
    
            CASE %WM_NCACTIVATE
                STATIC hWndSaveFocus AS DWORD
                IF ISFALSE CBWPARAM THEN
                    ' Save control focus
                    hWndSaveFocus = GetFocus()
                ELSEIF hWndSaveFocus THEN
                    ' Restore control focus
                    SetFocus(hWndSaveFocus)
                    hWndSaveFocus = 0
                END IF
    
            CASE %WM_COMMAND
                ' Process control notifications
                SELECT CASE AS LONG CBCTL
                    CASE %IDC_TEXTBOX1, %IDC_TEXTBOX2, %IDC_TEXTBOX3, %IDC_TEXTBOX4, %IDC_TEXTBOX5, _
                         %IDC_TEXTBOX6, %IDC_TEXTBOX7, %IDC_TEXTBOX8, %IDC_TEXTBOX9, %IDC_TEXTBOX10, _
                         %IDC_TEXTBOX11, %IDC_TEXTBOX12, %IDC_TEXTBOX13, %IDC_TEXTBOX14, %IDC_TEXTBOX15, _
                         %IDC_TEXTBOX16, %IDC_TEXTBOX17, %IDC_TEXTBOX18, %IDC_TEXTBOX19, %IDC_TEXTBOX20, _
                         %IDC_TEXTBOX21, %IDC_TEXTBOX22, %IDC_TEXTBOX23, %IDC_TEXTBOX24, %IDC_TEXTBOX25, _
                         %IDC_TEXTBOX26, %IDC_TEXTBOX27, %IDC_TEXTBOX28, %IDC_TEXTBOX29, %IDC_TEXTBOX30, _
                         %IDC_TEXTBOX31, %IDC_TEXTBOX32, %IDC_TEXTBOX33, %IDC_TEXTBOX34, %IDC_TEXTBOX35, _
                         %IDC_TEXTBOX36, %IDC_TEXTBOX37, %IDC_TEXTBOX38, %IDC_TEXTBOX39, %IDC_TEXTBOX40, _
                         %IDC_TEXTBOX41, %IDC_TEXTBOX42, %IDC_TEXTBOX43, %IDC_TEXTBOX44, %IDC_TEXTBOX45, _
                         %IDC_TEXTBOX46, %IDC_TEXTBOX47, %IDC_TEXTBOX48, %IDC_TEXTBOX49, %IDC_TEXTBOX50
                             SELECT CASE CBCTLMSG
                                  CASE %EN_CHANGE
                                       DoSomethingWithClick  CBCTL
                                       EXIT FUNCTION
                             END SELECT
                END SELECT
        END SELECT
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Dialogs **
    '------------------------------------------------------------------------------
    FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
    
    #PBFORMS BEGIN DIALOG %IDD_DIALOG1->->
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW hParent, "Dialog1", 70, 70, 231, 114, %WS_POPUP OR %WS_BORDER _
            OR %WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR %WS_SYSMENU OR _
            %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_CLIPSIBLINGS OR _
            %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _
            %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR _
            %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "TextBox1", 5, 5, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, "TextBox2", 5, 15, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX3, "TextBox3", 5, 25, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX4, "TextBox4", 5, 35, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX5, "TextBox5", 5, 45, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX6, "TextBox6", 5, 55, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX7, "TextBox7", 5, 65, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX8, "TextBox8", 5, 75, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX9, "TextBox9", 5, 85, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX10, "TextBox10", 5, 95, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX11, "TextBox11", 50, 5, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX12, "TextBox12", 50, 15, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX13, "TextBox13", 50, 25, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX14, "TextBox14", 50, 35, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX15, "TextBox15", 50, 45, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX16, "TextBox16", 50, 55, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX17, "TextBox17", 50, 65, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX18, "TextBox18", 50, 75, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX19, "TextBox19", 50, 85, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX20, "TextBox20", 50, 95, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX21, "TextBox21", 95, 5, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX22, "TextBox22", 95, 15, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX23, "TextBox23", 95, 25, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX24, "TextBox24", 95, 35, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX25, "TextBox25", 95, 45, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX26, "TextBox26", 95, 55, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX27, "TextBox27", 95, 65, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX28, "TextBox28", 95, 75, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX29, "TextBox29", 95, 85, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX30, "TextBox30", 95, 95, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX31, "TextBox31", 140, 5, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX32, "TextBox32", 140, 15, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX33, "TextBox33", 140, 25, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX34, "TextBox34", 140, 35, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX35, "TextBox35", 140, 45, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX36, "TextBox36", 140, 55, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX37, "TextBox37", 140, 65, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX38, "TextBox38", 140, 75, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX39, "TextBox39", 140, 85, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX40, "TextBox40", 140, 95, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX41, "TextBox41", 185, 5, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX42, "TextBox42", 185, 15, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX43, "TextBox43", 185, 25, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX44, "TextBox44", 185, 35, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX45, "TextBox45", 185, 45, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX46, "TextBox46", 185, 55, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX47, "TextBox47", 185, 65, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX48, "TextBox48", 185, 75, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX49, "TextBox49", 185, 85, 40, 10
        CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX50, "TextBox50", 185, 95, 40, 10
    #PBFORMS END DIALOG
    
        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
    
    #PBFORMS BEGIN CLEANUP %IDD_DIALOG1
    #PBFORMS END CLEANUP
    
        FUNCTION = lRslt
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION DoSomethingWithClick(ControlClicked AS LONG) AS LONG
         MSGBOX "You Clicked " + STR$(ControlClicked)
    END FUNCTION
    To fire the event, change the text in any of the textboxes (edit)
    Last edited by Cliff Nichols; 13 Oct 2007, 06:02 PM. Reason: Forgot to add
    Engineer's Motto: If it aint broke take it apart and fix it

    "If at 1st you don't succeed... call it version 1.0"

    "Half of Programming is coding"....."The other 90% is DEBUGGING"

    "Document my code????" .... "WHYYY??? do you think they call it CODE? "

    Comment


    • #3
      Your identifiers are sequentially numbered so just use:

      Code:
      SELECT CASE AS LONG CBCTL
         CASE %IDC_TEXTBOX1 To %IDC_TEXTBOX50
      ...
      ...
      Paul Squires
      FireFly Visual Designer (for PowerBASIC Windows 10+)
      Version 3 now available.
      http://www.planetsquires.com

      Comment


      • #4
        Since your TextBoxes have some non-standard behaviour, it might be easier to either (1) Create a custom control based on a TextBox, or (2) Superclass the TextBox (Edit) control in order to create your own class of control. Semen M posted code in these forums on superclassing.
        Paul Squires
        FireFly Visual Designer (for PowerBASIC Windows 10+)
        Version 3 now available.
        http://www.planetsquires.com

        Comment


        • #5
          I want to know how to subclass 50 textboxes without declaring of 50 subroutines to manage all listed methods.
          If the ID's are sequential subclassing them is a simple loop, if not, yes a few more lines of code. Hoeever, all the text boxes can share the same subclass callback. In the subclass callback function you can then process the action of the keys mentioned based on the handle of the particular text box.

          Some snips ... think they are all editd for your case.

          Code:
          FOR i& = %ID_TEXTBOX_1 TO %ID_TEXTBOX_50
              'just puts the current calback CODEPTR value in USER memory 1 on the control
              'as it subclasses he TB's
              CONTROL SET USER hDlg, i&,1, _
                      SetWindowLong(GetDlgItem(hDlg, i&), %GWL_WNDPROC, CODEPTR(SubClassTEXTBOX))
          NEXT i&
          
          'In the SubClassTEXTBOX callback  (adapted from PB's Samples\DDT\Graphic\BoltCalc.bas)
          CALLBACK FUNCTION SubClassTEXTBOX ()
              LOCAL hParent,X AS DWORD
              hParent = GetWindowLong(CBHNDL,%GWL_HWNDPARENT)
              
              SELECT CASE CBMSG
          
                  CASE %WM_KEYUP      CASE %WM_KEYUP
                      ' Keep in mind this is only dealing with the edit textboxes.
                      IF CBWPARAM = %VK_RETURN OR CBWPARAM = %VK_DOWN THEN
                          ' All we want to do is tab to the next control, does not check for
                          '%ID_TEXTBOX_50, but could be like the cases for %VK_UP
                          DIALOG POST hParent, %WM_NextDlgCtl, _
                                      GetNextDlgTabItem(hParent, CBHNDL, %FALSE), %TRUE
                      ELSEIF CBWPARAM = %VK_UP AND CBHNDL <> %ID_TEXTBOX_1 THEN
                          ' Move up one as long this is not the top edit textbox.
                          DIALOG POST hParent, %WM_NextDlgCtl, _
                                      GetNextDlgTabItem(hParent, CBHNDL, %TRUE), %TRUE
                      ELSEIF CBWPARAM = %VK_UP AND CBHNDL = %ID_TEXTBOX_1 THEN
                          ' This is the top edit textbox, so cycle focus
                          ' to the the last box ... or another control.
                          DIALOG POST hParent, %WM_NextDlgCtl, %ID_TEXTBOX_50, %TRUE
                      ELSEIF CBWPARAM = %VK_F1 THEN ....
                      '  maybe here you need to select a case based on the textbox that has focus
                          SELECT CASE AS LONG GetWindowLong(CBHNDL,%GWL_ID)
                              CASE %ID_TEXTBOX_1 TO %ID_TEXTBOX_10
                                  'do something
                              CASE %ID_TEXTBOX_11 TO %ID_TEXTBOX_50
                                  'do something
                          END SELECT
                      '''
                      ELSEIF CBWPARAM = %VK_FN THEN ....
                          'some code
                      END IF
                  CASE %WM_CHAR
                  '.... filter any allowed or disallowed chaacters here
                  CASE %WM_PASTE
                  
              END SELECT
              DIALOG GET USER CBHNDL, 1 TO x
              FUNCTION = CallWindowProc(x, CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
          END FUNCTION
          
          'in the primary callback in the %WM_DESTROY case
              FOR i& = %ID_TEXTBOX_1 TO %ID_TEXTBOX_50
                  'jremove subclassing
                  CONTROL GET USER CBHNDL,i&, 1 TO dwoldcb
                  SetWindowLong GetDlgItem(CBHNDL, i&),    %GWL_WNDPROC, dwoldcb
              NEXT i&
          Rick Angell

          Comment


          • #6
            Thanks for helping!

            ID's will be stored in array, so subclasing shouldn't be a problem.
            Can I change the visual appereance of textbox in subclass module?

            Comment


            • #7
              Hi Aslan,

              I had time to spare, so I pasted some code from Börje Hagsten, Edwin Knoppert and Semen Matusovski
              and played with it a little.
              You may try the result for a little study if you feel like it...

              You will have 50 textboxs, a red rectangle
              and a different background color around the active one,
              Enter and F1 to F5 key is intercepted,
              the Up and Down arrows move focus from one control to another,
              double clicks are catched and Tooltips are there.

              Have fun...

              Code:
              #COMPILE EXE '#Win 8.04#
              #REGISTER NONE
              #DIM ALL
              #INCLUDE "Win32Api.inc" '#2005-01-27#
               
              'Thank to Börje Hagsten, Edwin Knoppert and Semen Matusovski
               
              GLOBAL hDlg         AS DWORD
              GLOBAL hToolTip     AS DWORD
              GLOBAL TextboxPreva AS DWORD
               
              %Textbox1        = 101
              %TextboxCount    = 50
              %TextboxRowCount = 10
               
              %TTS_ALWAYSTIP   = &H01
              %TTF_IDISHWND    = &H0001
              %TTF_SUBCLASS    = &H0010
              %TTM_ADDTOOL     = %WM_USER + 4
              %TTM_GETTOOLINFO = %WM_USER + 8
              %TTM_DELTOOL     = %WM_USER + 5
               
              DECLARE SUB InitCommonControls LIB "COMCTL32.DLL" ALIAS "InitCommonControls" ()
              '______________________________________________________________________________
               
              CALLBACK FUNCTION TextboxSubclassedProc() AS LONG
               LOCAL id AS DWORD
               
               SELECT CASE CBMSG
                 id = GetDlgCtrlID(CBHNDL)
               
                 CASE %WM_KEYUP
                   SELECT CASE CBWPARAM
               
                     CASE %VK_RETURN 'Enter pressed
                       DIALOG SET TEXT hDlg, "Enter on" & STR$(id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_ESCAPE 'Esc pressed
                       DIALOG SET TEXT hDlg, "Escape on" & STR$(id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_TAB 'Tab pressed
                       'id = id + %TextboxRowCount - 1
                       'IF id >= %Textbox1 + %TextboxCount THEN
                       '  id = id - %TextboxCount
                       'END IF
                       'SetFocus GetDlgItem(hDlg, id)
                       'EXIT FUNCTION 'Do not forward key
               
                   END SELECT
               
                 CASE %WM_KEYDOWN
                   SELECT CASE CBWPARAM
               
                     CASE %VK_UP 'Up arrow pressed
                       DECR id
                       IF id < %Textbox1 THEN
                         id = %Textbox1 + %TextboxCount - 1
                       END IF
                       SetFocus GetDlgItem(hDlg, id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_DOWN 'Down arrow pressed
                       INCR id
                       IF id >= %Textbox1 + %TextboxCount THEN
                         id = %Textbox1
                       END IF
                       SetFocus GetDlgItem(hDlg, id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_HOME 'Home pressed
                       id = %Textbox1
                       SetFocus GetDlgItem(hDlg, id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_END 'End pressed
                       id = %Textbox1 + %TextboxCount - 1
                       SetFocus GetDlgItem(hDlg, id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_PGUP 'Page up pressed
                       id = ((id - %Textbox1) \ %TextboxRowCount) * %TextboxRowCount + %Textbox1
                       IF id < %Textbox1 THEN id = %Textbox1
                       SetFocus GetDlgItem(hDlg, id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_PGDN 'Page down pressed
                       IF id MOD %TextboxRowCount THEN
                         id = id + %TextboxRowCount - id MOD %TextboxRowCount
                       END IF
                       IF id > %Textbox1 + %TextboxCount - 1 THEN id = %Textbox1 + %TextboxCount - 1
                       SetFocus GetDlgItem(hDlg, id)
                       EXIT FUNCTION 'Do not forward key
               
                     CASE %VK_F1 TO %VK_F5  'F1 to F5 pressed
                       DIALOG SET TEXT hDlg, "F" & FORMAT$(CBWPARAM - %VK_F1 + 1) & " on" & STR$(id)
                       EXIT FUNCTION 'Do not forward key
               
                   END SELECT
               
                 CASE %WM_LBUTTONDBLCLK 'Double clicked
                   DIALOG SET TEXT hDlg, "Double click on" & STR$(id)
               
               END SELECT
               
               FUNCTION = CallWindowProc(TextboxPreva, CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
               
              END FUNCTION
              '______________________________________________________________________________
               
              SUB RectangleDraw(BYVAL hDlg AS DWORD, BYVAL hWnd AS DWORD, BYVAL clr AS DWORD)
               LOCAL hDC    AS DWORD
               LOCAL hBrush AS DWORD
               LOCAL hPen   AS DWORD
               LOCAL rc     AS RECT
               
               GetWindowRect hWnd, rc              'Get control's pos and size on screen
               MapWindowPoints 0, hDlg, rc, 2      'Map rect to dialog
               InflateRect rc, 2, 2                'Increase slightly to draw around control
               
               hDC = GetDc(hDlg)                   'Use dialog's DC since we want to paint on dialog
               hPen = CreatePen(%PS_SOLID, 1, clr) 'Create a pen for given color
               hPen = SelectObject(hDC, hPen)      'Select the new pen into the DC
               hBrush = SelectObject(hDC, GetStockObject(%NULL_BRUSH)) 'Use stock null brush for hollow rect
               
               Rectangle hDC, rc.nLeft, rc.nTop, rc.nRight, rc.nBottom 'Draw the rectangle
               
               SelectObject hDC, hBrush 'Return original pen and brush, then release DC
               DeleteObject SelectObject(hDC, hPen)
               ReleaseDc hDlg, hDC
               
              END SUB
              '______________________________________________________________________________
               
              CALLBACK FUNCTION DlgProc
               STATIC hControlFocus AS DWORD
               STATIC sthText       AS DWORD
               LOCAL  Looper        AS LONG
               STATIC stCol         AS LONG
               
               SELECT CASE CBMSG
               
                 CASE %WM_INITDIALOG
               
                 CASE %WM_NCACTIVATE
                   IF CBWPARAM THEN             'Application is getting focus
                     IF hControlFocus THEN      'Check if valid focus
                       SetFocus(hControlFocus)  'Restore focus on control
                       hControlFocus = 0
                     END IF
                   ELSE                         'Application is loosing focus
                     hControlFocus = GetFocus() 'Remember witch control have focus
                   END IF
               
                 CASE %WM_PAINT 'Must be able to repaint if dialog has
                   IF sthText THEN 'Been covered/uncovered by something else.
                     RectangleDraw CBHNDL, sthText, stCol
                   END IF
               
                 CASE %WM_COMMAND
                   SELECT CASE LOWRD(CBWPARAM)
               
                     CASE %Textbox1 TO %Textbox1 + %TextboxCount - 1
               
                       DIALOG SET TEXT CBHNDL, "Textbox id is" & STR$(GetDlgCtrlID(CBLPARAM))
               
                       SELECT CASE CBCTLMSG
                         CASE %EN_CHANGE
                         CASE %EN_UPDATE
               
                         CASE %EN_SETFOCUS
                           CONTROL SET COLOR CBHNDL, LOWRD(CBWPARAM), %BLACK, %YELLOW
                           CONTROL REDRAW CBHNDL, LOWRD(CBWPARAM)
                           stCol = RGB(255, 0, 0) 'Store color and handle for eventual %WM_PAINT
                           sthText = CBLPARAM
                           RectangleDraw CBHNDL, sthText, stCol
               
                         CASE %EN_KILLFOCUS
                           CONTROL SET COLOR CBHNDL, GetDlgCtrlID(CBLPARAM), %BLACK, %WHITE
                           CONTROL REDRAW CBHNDL, LOWRD(CBWPARAM)
                           RectangleDraw CBHNDL, CBLPARAM, GetSysColor(%COLOR_3DFACE)
                           sthText = 0  'Reset handle, we don't need to repeat this under WM_PAINT
               
                       END SELECT
                   END SELECT
               
                 CASE %WM_DESTROY
                   FOR Looper = 1 TO %TextboxCount
                     SetWindowLong GetDlgItem(hDlg, %Textbox1 + Looper - 1), %GWL_WNDPROC, TextboxPreva
                   NEXT
                   PostQuitMessage 0
                   FUNCTION = %FALSE
                   EXIT FUNCTION
               
                END SELECT
               
              END FUNCTION
              '______________________________________________________________________________
               
              FUNCTION PBMAIN()
               LOCAL Looper   AS LONG
               LOCAL ColPos   AS DWORD
               LOCAL RowPos   AS DWORD
               Local hControl AS DWORD
               Local ti       AS TOOLINFO
               LOCAL sToolTip AS STRING
               
               DIALOG NEW %HWND_DESKTOP ,"PB ver. " & Hex$(%PB_REVISION), , , 490, 175, _
                          %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg
               
               SetClassLong hDlg, %GCL_HICON, LoadIcon(BYVAL %NULL, BYVAL %IDI_INFORMATION)
               
               CALL InitCommonControls 'For tooltips
               
               FOR Looper = 1 TO %TextboxCount
                 CONTROL ADD TEXTBOX, hDlg, %Textbox1 + Looper - 1, "Textbox" & STR$(Looper), _
                                      15 + ColPos, 15 + RowPos, 60, 12
                 'Position section
                 IF Looper MOD %TextboxRowCount = 0 THEN
                   ColPos = ColPos + 100
                   RowPos = 0
                 ELSE
                   RowPos = RowPos + 15
                 END IF
                 'Subclass section
                 TextboxPreva = SetWindowLong(GetDlgItem(hDlg, %Textbox1 + Looper - 1), _
                                              %GWL_WNDPROC, CODEPTR(TextboxSubclassedProc))
                 'Tooltip section
                 hControl = GetDlgItem(hDlg, %Textbox1 + Looper - 1)
                 hToolTip = CreateWindowEx(0, "ToolTips_Class32", "", %TTS_ALWAYSTIP, 0, 0, 0, 0, hControl, _
                                           BYVAL 0, GetWindowLong(hDlg, %GWL_HINSTANCE), BYVAL 0)
                 ti.cbSize = Sizeof(ti)
                 ti.uFlags = %TTF_SUBCLASS OR %TTF_IDISHWND
                 ti.hWnd   = hDlg
                 ti.uId    = hControl
               
                 sToolTip = "Tooltip" & Str$(Looper)
                 ti.cbSize   = SIZEOF(ti)
                 ti.uFlags   = %TTF_SUBCLASS Or %TTF_IDISHWND
                 ti.hWnd     = hDlg 'GetParent(hWnd)
                 ti.uId      = hControl 'hWnd
                 ti.lpszText = STRPTR(sToolTip)
                 SendMessage hToolTip, %TTM_ADDTOOL, 0, BYVAL VARPTR(ti)
                 'Tooltip section end
               NEXT
               
               DIALOG SHOW MODAL hDlg CALL DlgProc
               
              END FUNCTION
              '______________________________________________________________________________
              Last edited by Pierre Bellisle; 14 Oct 2007, 03:25 PM.

              Comment


              • #8
                Pierre,
                Your code is cool! It does what I needed.

                Is it possible to avoid moving caret to left and right when pressing UP and DOWN keys? When I press up/down arrow keys quickly, the caret is moving inside the textbox.

                Comment


                • #9
                  Code:
                  CALLBACK FUNCTION TextboxSubclassedProc() AS LONG
                    {references to CBHNDL, CBLPARAM, etc)
                  Is the use of the "CALLBACK FUNCTION" syntax for other than the function specified in a DIALOG SHOW or CONTROL ADD statement officially supported?

                  I can see how and why it works, but that's not the same as "supported."

                  FWIW, the meaning of CBHNDL in a subclass procedure is not the same as the meaning of CBHNDL in DDT functions. In the posted subclasss procedure it's handle to the subclassed control; in standard "DDT" circumstances it's always a handle to the owner dialog. You handle that here with GetDlgCtrlID, but that CBHNDL here does not mean the same as CBHNDL there seems a high-risk behavior.

                  MCM
                  Michael Mattias
                  Tal Systems Inc. (retired)
                  Racine WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Aslan
                    Control arrays like in VB are not only easy to simulate in PB but as shown in the code examples but far more powerful, ie you can not only have arrays but sub arrays. Suppose you wanted to display or manipulate data from a data base record that had various information for 12 periods for that item then in VB you would create a control array for each of the pieces of information you were displaying. Lets say you now wanted to display more than one record at a time then in PB it is easy to create what in VB would be an array of arrays or multiple dimension array of controls which can even contain arrays of different types of controls but in PB you don’t need to allocate a single array.
                    It only requires understanding the concept that the handle number you give a control is only meaningful to you and not windows. They don’t need to be consecutive nor defined as constants, they can be a variable or the result of a calculation and interpreted similarly only limited by your imagination.
                    Here are some code snippets to enhance on the ideas already given
                    Code:
                    %dialog1
                    %checkbox1 = 200
                    %Textbox1 = 220
                    
                    DIALOG NEW hParent, "The Dialog", TO Hdlg   ' obviously position size etc included
                    FOR x = 0 TO 11
                        CONTROL ADD Hdlg, %checkbox1 + x, 'plus position size etc
                        CONTROL ADD Hdlg, %checkbox1 + x + 1000, 'plus position seize etc
                        CONTROL ADD Hdlg, %Textbox1 + x, 'plus position seize etc
                        CONTROL ADD Hdlg, %Textbox1 + x + 1000, 'plus position size etc
                    NEXT
                    
                    'and in the callback proc
                    CASE %checkbox1 TO %checkbox1 + 11, %checkbox1 + 1000 TO %checkbox1 + 1011
                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                            RecordClicked = CBCTL \ 1000
                            'etc
                    I currently supply a program with over 500 text boxes and 100 other types of controls in “arrays” including showing multiple recs as explained. The code is very concise as it only has to decode the control number to apply the changes to the correct control. I would put up a screen shot but I don’t think the new forum is allowing attachments

                    John

                    Comment


                    • #11
                      Callback Functions are reserved for use with Dynamic Dialog Technology (DDT) functions. DDT is only available in the PowerBASIC For Windows Compiler
                      AIUI this directly applies such that in a CALLBACK FUNCTION we can then use CBHNDL,CBCTL,CBMSG,CBCTLMSG,CBWPARAM,CBLPARM inside that CALLBACK function because DDT has encapsulated the conventional callback's hWnd,wMsg,....lParam function parameters that such a conventional function would have have to declare and support.

                      CBHNDL will also return the window handle for all other types of messages that flow through dialog callback functions.
                      In this case that also means the "window handle" of the child controls in the dialog as well if they are not by design using their own separate callbacks.

                      In the CBHNDL help, it shows how that function has a one restriction:
                      This function is only valid inside a Callback Function.
                      See the subject "callbacks" in the help file. (ProgrammingReference\Dynamic Dialog Tools\Callbacks) . Now consider what the subclass is doing. It is sitting in the "callback" message chain for the dialog and its control(s) for the purpose of handling some (subclassed) control's messages that would otherwise be handled by the dialog's internal RTL processing itself and never passed to the control receiving messages in the main dialog callback. However, with subclassing, messages that the parent dialog would pass on to the child controls are being passed through to in the original callback for the dialog in this snipped code.

                      There are many examples of using the PB DDT CALLBACK function for subclassing in the forums, including several by former PB staff. One of the first ones that I remember seeing as using CALLBACK function for subclassing was by Lance Edmonds. There are some sample code programs with subclassing in the SAMPLES that came with PB Windows. One that shows some of the code being used in the above snips is PBWin80\Samples\DDT\Graphic\BoltCalc.bas. Another is PBWin80\Samples\DDT\Interface Explorer in the Interface Explorer.inc file.

                      Since a Dialog Window does handle some messages that a conventional window callback would pass for handling, sometimes a bit more is needed, even in the subclass. In those cases where a subclass must handle all keys, the subclass should process the the %WM_GETDLGCODE message and return %DLGC_WANTALLKEYS to get all keyboard inputs:
                      Code:
                              CASE %WM_GETDLGCODE
                                    FUNCTION = %DLGC_WANTALLKEYS
                                    EXIT FUNCTION
                      Since the helpf ile is quite scant on subclassing, I suppose one might skeptically question the legitimacy of using CALLBACK versus a conventional approach. In pracatice though though are the many examples that do show subclassing using the DDT CALLBACK FUNCTION approach where the controls otherwise share the same callback as the DDT dialog.
                      Rick Angell

                      Comment


                      • #12
                        Richard,
                        Hey, always a pleasure to read you, interesting stuff you got there...


                        Aslan,
                        Above code updated...
                        Stopped the moving caret...
                        Added PageUp, PageDown, Home, End
                        Added control focus memorisation.

                        Comment


                        • #13
                          Thanks, Pierre. I always appreciate your contributions here in the forum. as well.

                          Michael has raised a point about "officially supported" though that is not directly covered per se with some quasi-legit "PB Seal of Approval" in the help, at least not where I have noticed. As far as suggesting that it might be risky behavior, respectfully ... it depends!

                          In fact if we look at the help for the CB functions cited, they reference their SDK counterpart parameters. MCM's point about the CBHNDL is well taken in the sense that anytime a control is subclassed or has its own callback from the get-go, CBHNDL should be the handle of the subclassed control, not the parent dialog.

                          One also should pay close attention to the use of CBCTL and CBCTLMSG helps for further guidance. But a goodly portion of the subclassing that newer PB'ers use with DDT seems to often be with text edit boxes. That said there are a lot of controls such as Listviews, Listboxes, et.al. that are also common candidates for subclassing in a DDT program in order to get the end result the app intends to achieve.
                          Rick Angell

                          Comment


                          • #14
                            MCM's point about the CBHNDL is well taken in the sense that anytime a control is subclassed or has its own callback from the get-go, CBHNDL should be the handle of the subclassed control, not the parent dialog.
                            I don't agree with that. CBHNDL should be whatever the doc says it is, and it says it's a handle to the owning dialog.

                            But when you use the 'CALLBACK FUNCTION' syntax for a subclass procedure, CBHNDL is no longer a handle to the owning dialog. So, one of the below is true:
                            • The doc is wrong or incomplete about what CBHNDL means.
                            • The doc is wrong or incomplete on the permitted uses of the 'CALLBACK FUNCTION' syntax.
                            • The compiler missed an edit, allowing the use of the 'CALLBACK FUNCTION' syntax when the function is not the object of any DIALOG SHOW or CONTROL ADD statement in the current code module.
                            • In point of fact nobody actually cares.
                            Michael Mattias
                            Tal Systems Inc. (retired)
                            Racine WI USA
                            [email protected]
                            http://www.talsystems.com

                            Comment


                            • #15
                              CBHNDL under Purpose:
                              In a Callback Function, return the window handle of the caller.
                              That is to the point and is right on. The caller for these subclasses here, are the controls themselves. What it appears you are referring to is how the Remarks section, carefully does not address a subclassed control, nor does the whole help file really address subclassing and subclass callbacks ... BECAUSE subclassing is considered a topic for advanced users (see last remarks here).

                              So in itself, in the remarks section for CBHNDL, the following line correctly applies to non-subclass callbacks:
                              If no control callback function is defined, the message is sent to the dialog callback function. In either type of callback, CBHNDL returns the window handle of the parent dialog.
                              In that context "control callback function" nor "dialog callback" should not automatically be assumed to exclude "subclass" callbacks, because the Purpose statment and the conventional callback example in the CBHNDL help show how it parallels explicitly the "conventional" callback parameters returned.

                              The help manual does not go into many advanced techniques (yet). But that does not mean that an advanced programmer, such as yourself, should not see that the expicit example of the conventional callback function syntax is exactly what is being dealt with there in a CALLBACK FUNCTION and its assocated value retrieval functions. Subject still to any limitations of a dialog box window DDT approach.

                              One could also note that the help file does not address the subject of subclassing except in (1) the Glossary and (2) a mention in the CONTROL ADD TEXTBOX help under the %ES_NUMBER remarks where it states:
                              subclassing a TextBox that does not use %ES_NUMBER and rejecting "unwanted" keystrokes is common practice among advanced programmers
                              If you question the use, then write a test program that addresses the situation here, duplicate it and prove that either the CALLBACK FUNCTION or conventional function do indeed do return the same results. Then maybe do what I did a long time ago. Ask for these subclassing and supperclassing topics to be discussed in the help file too. I know you know that routine
                              Rick Angell

                              Comment


                              • #16
                                Doc (8.03) also says (in bold print no less):
                                · Callback Functions are reserved for use with Dynamic Dialog Tools (DDT) functions.
                                Michael Mattias
                                Tal Systems Inc. (retired)
                                Racine WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                • #17
                                  Dynamic Dialog Tools (DDT) functions
                                  Yes, as noted earlier CBHNDL,CBMSG,CBCTL,CBWPARAM,CBLPARAM are definitely DDT functions. They are not MS Windows API functions, even though they simply ease the the need to have to use an SDK standard callback function prototype for the callback

                                  We are subclassing controls in a DDT dialog here last time I looked So from a DDT POV we are still in the "dialog" and dialog controls realm. There are lots of DDT functions and statments that deal with the dialog, controls and even basic messaging with them (DIALOG SEND, DIALOG POST, CONTROL SEND, CONTROL POST), etc. etc. etc. The key being that we are using DDT statements and functions to create and manage the GUI, as far as possible. That's really all we are doing here.

                                  But hey Michael, if it bothers you then just do the same identical thing with:
                                  Function SubClassProc(ByVal hWnd&, ByVal wMsg&, ByVal wParam&, ByVal lParam&) As Long
                                  Nobody will mind. In the fourms you find it done that way, even in otherwise DDT programs, as well as in other subclass examples where instead they use the DDT CALLBACK FUNCTION and associated functions. By using the longer SDK version of the standard message callback function you can avoid the benefit of using CBHNDL, CBMSG, CBWPARAM, CBLPARAM in a subclass. I did not specifically include CBCTL since the subclasses are not going to be seeing %WM_COMMAND messages as a non-subclassed callback would, either at the control or dialog level. So an advanced programmer may still need to use GetWindowLong, a global variable or a USER memory (control or dialog) to get the control ID&, if the ID& is needed in the code.

                                  Using the DDT specification for a CALLBACK FUNCTION has been around for a long time, here are a couple ... but the list is far longer if one wants to spend the time looking for them.

                                  Borje and Lance, around April 20, 2001:
                                  http://www.powerbasic.com/support/fo...ML/003698.html
                                  Borje:
                                  http://www.powerbasic.com/support/pb...bclass+textbox

                                  Lance:
                                  http://www.powerbasic.com/support/fo...ML/006684.html
                                  Last edited by Richard Angell; 16 Oct 2007, 08:56 AM. Reason: Changed example list, different link to one of Lance's examples
                                  Rick Angell

                                  Comment


                                  • #18
                                    Dear All,

                                    Many thanks for your solutions! It was really helpful.

                                    Comment


                                    • #19
                                      I did not specifically include CBCTL since the subclasses are not going to
                                      be seeing %WM_COMMAND messages as a non-subclassed callback would, either at
                                      the control or dialog level.
                                      That is not correct. If a top-level window with standard controls is subclassed, the subclassed procedure will receive WM_COMMAND messages.
                                      If a window with the WS_CHILD style set is subclassed, and if it has standard controls, the subclassed procedure will receive WM_COMMAND messages.
                                      By standards controls, I mean things like buttons, edit boxes, list boxes etc.
                                      A window with the WS_CHILD style could be an embedded modeless dialog or a complex control(one with embedded controls).

                                      So an advanced programmer may still need to use GetWindowLong, a global variable or a USER memory (control or dialog) to get
                                      the control ID&, if the ID& is needed in the code.
                                      Once you have the handle of a control, getting its ID is easy. Use GetDlgCtrlID. Therefore, if one has fifty subclassed edit controls, all using the same subclassed procedure, there is no need to store the identifiers of the controls.
                                      Dominic Mitchell
                                      Phoenix Visual Designer
                                      http://www.phnxthunder.com

                                      Comment


                                      • #20
                                        We are not subclassing the top level window in these examples, only the text edit boxes. %WS_CHILD in this arrangement does not cause the %WM_COMMAND to be passed on.

                                        Yes, using GetDlgCtrlID is another way, as for getting the control's parent, we can also use GetParent.
                                        Last edited by Richard Angell; 16 Oct 2007, 07:39 AM.
                                        Rick Angell

                                        Comment

                                        Working...
                                        X