Announcement

Collapse
No announcement yet.

Tab controls --Subclassing

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

  • Tab controls --Subclassing

    These tab controls work basically ok. Tab number 2 is subclassed so %WM_MOUSEMOVE events can be used to draw a vertical line that moves through the tab page with the mouse.

    Code:
    #COMPILE EXE
    #INCLUDE "commctrl.inc"
    #INCLUDE "TabDDT.inc"
    %IDC_TAB  = 1000
    %IDLABEL1 = 1
    
        GLOBAL hDlg AS LONG
        GLOBAL hTab AS LONG
        GLOBAL hTabDlg1 AS LONG
        GLOBAL hTabDlg2 AS LONG
        GLOBAL hTabDlg3 AS LONG
        GLOBAL point AS POINTAPI
        GLOBAL last_point AS POINTAPI
        GLOBAL hpen AS LONG
        GLOBAL hDc AS LONG
        GLOBAL DefaultTabProc AS LONG
        
    DECLARE CALLBACK FUNCTION MainDlgProc
    DECLARE FUNCTION TabSubClassProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
    
    FUNCTION PBMAIN
        LOCAL th AS TABHDR
        LOCAL TEXT AS STRING
    
        ' Main dialog
        DIALOG NEW 0, "DDT - Tab Example",,,400, 300, %WS_SYSMENU OR %WS_MINIMIZEBOX OR %DS_CENTER TO hDlg
    CONTROL ADD LABEL, hDlg,%IDLABEL1,"-------- ",5,5,60,12,0
    
        ' Tab control
        hTab = TabCtrl(hDlg, %IDC_TAB, 20, 20, 300, 250)
        ' Tab - dialog 1
        DIALOG NEW hTab, "Tab nr. 1",,,400, 300, %WS_CHILD OR %WS_VISIBLE  TO hTabDlg1
        ' Tab - dialog 2
        DIALOG NEW hTab, "Tab nr. 2",,,400, 300, %WS_CHILD OR %WS_VISIBLE TO hTabDlg2
        CONTROL ADD LABEL, hTabDlg2, -1, "This is Tab 2", 5,5,50,14
        ' Tab - dialog 3
        DIALOG NEW hTab, "Super tab number 3",,,400,300, %WS_CHILD OR %WS_VISIBLE TO hTabDlg3
        CONTROL ADD BUTTON, hTabDlg3, 100, "A button",50,50, 90,90
        th.lID = %IDC_TAB
        th.cDialogs = 3
    
        th.DlgHdr(0).hTabDlg       = hTabDlg1
        th.DlgHdr(0).lpfnCallback  = CODEPTR(cbTab1)
    
        th.DlgHdr(1).hTabDlg       = hTabDlg2
        th.DlgHdr(1).lpfnCallback  = CODEPTR(cbTab2)
    
        th.DlgHdr(2).hTabDlg       = hTabDlg3
        th.DlgHdr(2).lpfnCallback  = CODEPTR(cbTab3)
    
        InitTabControl hDlg, th
    
        DIALOG SHOW MODAL hDlg CALL MainDlgProc
    
    END FUNCTION
    
    CALLBACK FUNCTION MainDlgProc
        SELECT CASE CBMSG
            CASE %WM_INITDIALOG
            DefaultTabProc = SetWindowLong(hTabDlg2, %GWL_WNDPROC, CODEPTR(TabSubClassProc))
    
            CASE %WM_NOTIFY
                OnTabNotify CBHNDL, CBLPARAM
                 
    '        CASE %WM_COMMAND
    '             SELECT CASE LOWRD(CBWPARAM)
    '            END SELECT
      END SELECT
    
    END FUNCTION
    
    CALLBACK FUNCTION cbTab1
        SELECT CASE CBMSG
            CASE %WM_INITDIALOG
            CASE %WM_COMMAND
                SELECT CASE LOWRD(CBWPARAM)
               END SELECT
      END SELECT
    
    END FUNCTION
    
    
    CALLBACK FUNCTION cbTab2
      SELECT CASE CBMSG
             CASE %WM_INITDIALOG
             CASE %WM_COMMAND
                  SELECT CASE LOWRD(CBWPARAM)
                  END SELECT
      END SELECT
    
    END FUNCTION
    
    CALLBACK FUNCTION cbTab3
      SELECT CASE CBMSG
             CASE %WM_INITDIALOG
             CASE %WM_COMMAND
                  SELECT CASE LOWRD(CBWPARAM)
                  END SELECT
      END SELECT
    
    END FUNCTION
    
    
    FUNCTION TabSubClassProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
        BYVAL wParam AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG
    
           IF wMsg= %WM_SHOWWINDOW THEN
           Point.x=0
           Point.y=0
           ''Draw a couple lines
            hDC = GetDC(hTabDlg2)
            hpen = CreatePen (%PS_SOLID,2,RGB(255,255,255))
            SelectObject hDC,hpen
            SetROP2 hDC, %R2_COPYPEN
            MoveTo hDC, 100,200
            LineTo hDC, 200,300
            MoveTo hDC, 300,50
            LineTo hDC, 50,50
            ReleaseDC hTabDlg2, hDc
            DeleteObject hpen
            END IF
    
          IF wMsg= %WM_MOUSEMOVE THEN
            Last_Point=Point
            GetCursorPos Point
            TEXT$="X: "+STR$(POINT.X)+"  Y: "+STR$(POINT.Y)
            CONTROL SET TEXT hdlg,%IDLABEL1, TEXT$
            hDC = GetDC(hTabDlg2)
            hpen = CreatePen (%PS_SOLID,2,RGB(255,255,255))
            SelectObject hDC,hpen
            SetROP2 hDC, %R2_XORPEN
            MoveTo hDC, LAST_POINT.X-137,0
            LineTo hDC, LAST_POINT.X-137,350
            MoveTo hDC, POINT.X-137,0
            LineTo hDC, POINT.X-137,350
            ReleaseDC hTabDlg2, hDc
            DeleteObject hpen
             ELSE
              FUNCTION = CallWindowProc(DefaultTabProc, hWnd, wMSg, WParam, Lparam)
    END IF
    END FUNCTION
    
    '------- TabDDT.inc ---------------
    '----------------------------------
    ' DDT Tab Control
    ' Peter Stephensen 2000
    '----------------------------------
    %TRUE=1
    #IF NOT %DEF(%TAB_DLGMAX)
        %TAB_DLGMAX = 10                ' Maximal number of tab items
    #ENDIF
    
    #IF NOT %DEF(%TAB_LINES)
        %TAB_LINES = 2                  ' Number of lines in Tab control. Set this to 2
    #ENDIF                              ' if multiline-style is used
    
    TYPE DLGHDR
        hTabDlg AS LONG
        lpfnCallback AS LONG
    END TYPE
    
    TYPE TABHDR
        lID AS LONG                     ' Integer ID for Tab Control
        cDialogs AS LONG                ' Number of dialogs
        DlgHdr(%TAB_DLGMAX) AS DLGHDR   ' Dialogs
    END TYPE
    
    DECLARE SUB TabDisplay(hDlg AS LONG,TabID AS LONG,newTab AS LONG)
    
    GLOBAL pth AS TABHDR PTR
    
    FUNCTION TabCtrl(BYVAL hDlg AS LONG, BYVAL ID AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL xx AS LONG, BYVAL yy AS LONG) AS LONG
    
        LOCAL hTab AS LONG
    
        IF hDlg = 0 THEN EXIT FUNCTION
        CONTROL ADD "SysTabControl32", hDlg, ID, "", x, y, xx, yy, %WS_CHILD OR %WS_VISIBLE
        CONTROL HANDLE hDlg, ID TO hTab
    
        FUNCTION = hTab
    
    END FUNCTION
    
    FUNCTION InitTabControl(hDlg AS LONG, th AS TABHDR) AS LONG
    
        LOCAL tie AS TC_ITEM
        LOCAL rcTab AS RECT
        LOCAL i AS LONG
        LOCAL hInst AS LONG
        LOCAL hWndTab AS LONG
        LOCAL zBuffer AS ASCIIZ*128
    
        InitCommonControls
    
        pth = LocalAlloc(%LPTR, SIZEOF(@pth))
    
        hInst = GetModuleHandle(BYVAL 0)
        hWndTab = GetDlgItem(hDlg,th.lID)
    
        GetClientRect hwndTab,rcTab
    
        ' Add a tab for each of the child dialog boxes.
        FOR i = 0 TO th.cDialogs-1
            tie.mask = %TCIF_TEXT OR %TCIF_IMAGE
            tie.iImage = -1
            GetWindowText th.DlgHdr(i).hTabDlg, zBuffer, 128
            tie.pszText = VARPTR(zBuffer)
            TabCtrl_InsertItem hwndTab, i, tie
    
            MoveWindow th.DlgHdr(i).hTabDlg, _
                       4, _
                       %TAB_LINES*23, _
                       rcTab.nRight-rcTab.nLeft-11, _
                       rcTab.nBottom-rcTab.nTop-%TAB_LINES*23-3, _
                       %TRUE
        NEXT i
    
        @pth = th
        TabDisplay hDlg, th.lID, 0
    
    END FUNCTION
    
    SUB TabDisplay(hDlg AS LONG,TabID AS LONG, TabIndex AS LONG)
    
        STATIC PrevTabIndex AS LONG
        LOCAL i AS LONG
    
        IF PrevTabIndex THEN
            ShowWindow PrevTabIndex, %SW_HIDE
        END IF
    
        IF pth = 0 THEN EXIT SUB
    
        ShowWindow @pth.DlgHdr(TabIndex).hTabDlg, %SW_SHOW
        PrevTabIndex = @pth.DlgHdr(TabIndex).hTabDlg
    
    END SUB
    
    SUB OnTabNotify (BYVAL hDlg AS LONG, BYVAL lParam AS LONG )
    
        LOCAL lpNmh   AS NMHDR PTR
    
        IF pth = 0 THEN EXIT SUB
    
        lpNmh = lParam
        SELECT CASE @lpNmh.Code
        CASE %TCN_LAST TO %TCN_FIRST
            SELECT CASE @lpNmh.idFrom
            CASE @pth.lID
                SELECT CASE @lpNmh.Code
                CASE %TCN_SELCHANGE
                    TabDisplay hDlg, @pth.lID, TabCtrl_GetCurSel(GetDlgItem(hDlg,@pth.lID))
                END SELECT
            END SELECT
        END SELECT
    
    END SUB
    The problem I'm having is trying to get the "draw a couple lines" graphics in TabSubClassProc() to appear when tab 2 is initially selected. When selecting a different tab, the graphics flash and disappear. I'm sure the %WM_SHOWWINDOW is not appropriate to use here. The graphics are being drawn probably, but the tab control is just being drawn over them immediately after. Is there a message sent when the tab control is finished drawing itself that I can use to trigger my "draw a couple lines" section? Or am I taking care of this in the wrong place?
    Thanks,
    Todd Wasson

    P.S. Oh yes, one more thing! How can I get the tab callback functions (cbTab1,cbTab2,cbTab3) to work? Once they work, can they be used to "draw a couple lines"? Tab 2 should eventually show a graph that gets drawn when it is selected, then the mouse should control a vertical line that scrolls over it.

    ::TabDDT.INC now included here, thanks to Peter Stephenson.



    [This message has been edited by Todd Wasson (edited May 28, 2000).]
    Todd Wasson
    http://PerformanceSimulations.Com
    PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

  • #2
    Todd, I can't run your code because the TABDDT.INC file is missing. However, some general comments shpould help you here anyway:

    1. The parent of the tab control (the dialog) should have the style %WS_EX_CONTROLPARENT, and the individual tab pages (modeless dialogs) should have the style %DS_CONTROL. These "page dialogs" should be started with DIALOG SHOW MODELESS, and your app should provide a message pump that runs for the duration of the modeless dialogs.

    2. Drawing on dialogs (or controls) should only occur during %WM_PAINT. Therefore, your code should only be calculating drawing positions duing %WM_MOUSEMOVE, but then call InvalidateRect() followed by UpdateWindow() which causes a %WM_PAINT even to occur. Then, using the these calculated positions, your %WM_PAINT handler does the drawing. This ensures that your "lines" are repainted correctly if the dialog needs to be repainted because of any reason other than the forced repaint your code is issuing.

    3. The tab callback issues are probably caused by whatever is in TABDDT.INC. I suspect part of the problem relates to #1 and #4.

    4. The child modeless dialogs that form the tab pages should be children of the DIALOG that contains the tab control, NOT the tab control itself! This is opposite to how VB does things, but it is the correct way to do it with DDT and/or SDK-style applications.


    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>
    Lance
    mailto:[email protected]

    Comment


    • #3
      Todd --
      probably, you will understand, how to organize callback functions from my training code (based on sample from http://www.powerbasic.com/files/pub/pbwin/ )
      Code:
         #Compile Exe
         #Register None
         #Dim All
         #Include "commctrl.inc"
      
         %ID_TAB = 1001
         
         CallBack Function TabProc1
            Select Case CbMsg
               Case %WM_INITDIALOG
                  Control Add TextBox, CbHndl, -1, "Tab 1", 0, 0, 255, 140, , %WS_EX_CLIENTEDGE
            End Select
         End Function
         CallBack Function TabProc2
            Select Case CbMsg
               Case %WM_INITDIALOG
                  Control Add TextBox, CbHndl, -1, "Tab 2", 0, 0, 255, 140, , %WS_EX_CLIENTEDGE
            End Select
         End Function
         CallBack Function TabProc3
            Select Case CbMsg
               Case %WM_INITDIALOG
                  Control Add Button, CbHndl, -1, "Tab 3", 0, 0, 255, 140
            End Select
         End Function
      
         CallBack Function MainDlgProc
            Static LastIdx As Long, hWndTab() As Long
            Local tie As TC_ITEM, i As Long, Caption As String
            %nTabCh = 3
            Select Case CbMsg
               Case %WM_INITDIALOG
                  ReDim hWndTab(1 To %nTabCh)
                  For i = 1 To %nTabCh
                     Dialog New CbHndl, "", 20, 35, 255, 140, %DS_CONTROL Or %WS_CHILD To hWndTab(i)
                     Select Case i
                        Case 1
                           Dialog Show Modeless hWndTab(i) Call TabProc1: Caption = " One "
                        Case 2
                           Dialog Show Modeless hWndTab(i) Call TabProc2: Caption = " Two "
                        Case 3
                           Dialog Show Modeless hWndTab(i) Call TabProc3: Caption = " Three "
                     End Select
                     If i = 1 Then ShowWindow hWndTab(i), %SW_SHOW Else _
                        ShowWindow hWndTab(i), %SW_HIDE
                     tie.mask = %TCIF_TEXT Or %TCIF_IMAGE
                     tie.iImage = -1
                     tie.pszText = StrPtr(Caption)
                     TabCtrl_InsertItem GetDlgItem(CbHndl, %ID_TAB), i, tie
                  Next
                  SetFocus GetDlgItem(CbHndl, %ID_TAB)
               Case %WM_NOTIFY
                  Local lpNmh As NMHDR Ptr
                  lpNmh = CbLparam
                  Select Case @lpNmh.Code
                     Case %TCN_LAST To %TCN_FIRST
                        Select Case @lpNmh.idFrom
                           Case %ID_TAB
                             Select Case @lpNmh.Code
                                Case %TCN_SELCHANGE
                                   i = TabCtrl_GetCurSel(GetDlgItem(CbHndl,%ID_TAB))
                                   ShowWindow hWndTab(LastIdx + 1), %SW_HIDE
                                   LastIdx = i: ShowWindow hWndTab(LastIdx + 1), %SW_SHOW
                             End Select
                     End Select
                  End Select
            End Select
         End Function
      
         Function PbMain
            Local hDlg As Long
            InitCommonControls
            Dialog New 0, "DDT - Tab Example",,, 300, 200, _
               %WS_MINIMIZEBOX Or %WS_CAPTION Or %WS_SYSMENU, %WS_EX_CONTROLPARENT To hDlg
            Control Add "SysTabControl32", hDlg, %ID_TAB, "", 10, 10, 280, 180, %WS_CHILD Or %WS_VISIBLE
            Dialog Show Modal hDlg Call MainDlgProc
         End Function

      Comment


      • #4
        Lance, thank you for the tips on handling drawing via %WM_PAINT, InvalidateRect() and UpdateWindow(). This version works as planned.
        Code:
        #COMPILE EXE
        #INCLUDE "commctrl.inc"
        #INCLUDE "TabDDT.inc"
        %IDC_TAB  = 1000
        %IDLABEL1 = 1
        
            GLOBAL hDlg AS LONG
            GLOBAL hTab AS LONG
            GLOBAL hTabDlg1 AS LONG
            GLOBAL hTabDlg2 AS LONG
            GLOBAL hTabDlg3 AS LONG
            GLOBAL point AS POINTAPI
            GLOBAL last_point AS POINTAPI
            GLOBAL hpen AS LONG
            GLOBAL hDc AS LONG
            GLOBAL DefaultTabProc AS LONG
        
        DECLARE CALLBACK FUNCTION MainDlgProc
        DECLARE FUNCTION TabSubClassProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
        
        FUNCTION PBMAIN
            LOCAL th AS TABHDR
            LOCAL TEXT AS STRING
        
            ' Main dialog
            DIALOG NEW 0, "DDT - Tab Example",,,400, 300, %WS_SYSMENU OR %WS_MINIMIZEBOX OR %DS_CENTER TO hDlg
        CONTROL ADD LABEL, hDlg,%IDLABEL1,"-------- ",5,5,60,12,0
        
            ' Tab control
            hTab = TabCtrl(hDlg, %IDC_TAB, 20, 20, 300, 250)
            ' Tab - dialog 1
            DIALOG NEW hTab, "Tab 1",,,400, 300, %WS_CHILD OR %WS_VISIBLE  TO hTabDlg1
            ' Tab - dialog 2
            DIALOG NEW hTab, "Tab 2",,,400, 300, %WS_CHILD OR %WS_VISIBLE TO hTabDlg2
            CONTROL ADD LABEL, hTabDlg2, -1, "This is Tab 2", 5,5,50,14
            ' Tab - dialog 3
            DIALOG NEW hTab, "Tab 3",,,400,300, %WS_CHILD OR %WS_VISIBLE TO hTabDlg3
            CONTROL ADD BUTTON, hTabDlg3, 100, "A button",50,50, 90,90
            th.lID = %IDC_TAB
            th.cDialogs = 3
        
            th.DlgHdr(0).hTabDlg       = hTabDlg1
            th.DlgHdr(0).lpfnCallback  = CODEPTR(cbTab1)
        
            th.DlgHdr(1).hTabDlg       = hTabDlg2
            th.DlgHdr(1).lpfnCallback  = CODEPTR(cbTab2)
        
            th.DlgHdr(2).hTabDlg       = hTabDlg3
            th.DlgHdr(2).lpfnCallback  = CODEPTR(cbTab3)
        
            InitTabControl hDlg, th
        
            DIALOG SHOW MODAL hDlg CALL MainDlgProc
        
        END FUNCTION
        
        CALLBACK FUNCTION MainDlgProc
            
            SELECT CASE CBMSG
                CASE %WM_INITDIALOG
                DefaultTabProc = SetWindowLong(hTabDlg2, %GWL_WNDPROC, CODEPTR(TabSubClassProc))
        
                CASE %WM_NOTIFY
                    OnTabNotify CBHNDL, CBLPARAM
              END SELECT
        
        END FUNCTION
        
        CALLBACK FUNCTION cbTab1
            SELECT CASE CBMSG
                CASE %WM_INITDIALOG
                CASE %WM_COMMAND
                    SELECT CASE LOWRD(CBWPARAM)
                   END SELECT
          END SELECT
        
        END FUNCTION
        
        
        CALLBACK FUNCTION cbTab2
          SELECT CASE CBMSG
                 CASE %WM_INITDIALOG
                 CASE %WM_COMMAND
                      SELECT CASE LOWRD(CBWPARAM)
                      END SELECT
          END SELECT
        
        END FUNCTION
        
        CALLBACK FUNCTION cbTab3
          SELECT CASE CBMSG
                 CASE %WM_INITDIALOG
                 CASE %WM_COMMAND
                      SELECT CASE LOWRD(CBWPARAM)
                      END SELECT
          END SELECT
        
        END FUNCTION
        
        
        FUNCTION TabSubClassProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
            BYVAL wParam AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG
                 LOCAL  ps     AS PAINTSTRUCT
                IF wMsg= %WM_SHOWWINDOW THEN
                   BEEP
                Last_Point.x=-1
                Last_Point.y=-1
                END IF
                IF wMsg= %WM_PAINT THEN
                hdc = BeginPaint(hWnd, ps)
                hpen = CreatePen (%PS_SOLID,1,RGB(255,255,255))
                SelectObject hDC,hpen
                SetROP2 hDC, %R2_COPYPEN
                ''Draw graph here
                MoveTo hDC, 20,200
                LineTo hDC, 200,300
                MoveTo hDC, 300,50
                LineTo hDC, 50,50
                ''Draw vertical scrolling line here
                SetROP2 hDC, %R2_XORPEN
                MoveTo hDC, LAST_POINT.X,0
                LineTo hDC, LAST_POINT.X,350
                MoveTo hDC, POINT.X,0
                LineTo hDC, POINT.X,350
        
                DeleteObject hpen
                EndPaint hWnd, ps
                Last_Point=Point
                END IF
        
              IF wMsg= %WM_MOUSEMOVE THEN
                Point.X = LOWRD(lParam)
        '        Point.Y = HIWRD(lParam)
                InvalidateRect hWnd, BYVAL 0, %FALSE
                UpdateWindow hWnd
                TEXT$="X: "+STR$(POINT.X)+"  Y: "+STR$(POINT.Y)
                CONTROL SET TEXT hdlg,%IDLABEL1, TEXT$
                 ELSE
                  FUNCTION = CallWindowProc(DefaultTabProc, hWnd, wMSg, WParam, Lparam)
        END IF
        END FUNCTION
        Code:
        '------- TabDDT.inc ---------------
        '----------------------------------
        ' DDT Tab Control
        ' Peter Stephensen 2000
        '----------------------------------
        %TRUE=1
        #IF NOT %DEF(%TAB_DLGMAX)
            %TAB_DLGMAX = 10                ' Maximal number of tab items
        #ENDIF
        
        #IF NOT %DEF(%TAB_LINES)
            %TAB_LINES = 2                  ' Number of lines in Tab control. Set this to 2
        #ENDIF                              ' if multiline-style is used
        
        TYPE DLGHDR
            hTabDlg AS LONG
            lpfnCallback AS LONG
        END TYPE
        
        TYPE TABHDR
            lID AS LONG                     ' Integer ID for Tab Control
            cDialogs AS LONG                ' Number of dialogs
            DlgHdr(%TAB_DLGMAX) AS DLGHDR   ' Dialogs
        END TYPE
        
        DECLARE SUB TabDisplay(hDlg AS LONG,TabID AS LONG,newTab AS LONG)
        
        GLOBAL pth AS TABHDR PTR
        
        FUNCTION TabCtrl(BYVAL hDlg AS LONG, BYVAL ID AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL xx AS LONG, BYVAL yy AS LONG) AS LONG
        
            LOCAL hTab AS LONG
        
            IF hDlg = 0 THEN EXIT FUNCTION
            CONTROL ADD "SysTabControl32", hDlg, ID, "", x, y, xx, yy, %WS_CHILD OR %WS_VISIBLE
            CONTROL HANDLE hDlg, ID TO hTab
        
            FUNCTION = hTab
        
        END FUNCTION
        
        FUNCTION InitTabControl(hDlg AS LONG, th AS TABHDR) AS LONG
        
            LOCAL tie AS TC_ITEM
            LOCAL rcTab AS RECT
            LOCAL i AS LONG
            LOCAL hInst AS LONG
            LOCAL hWndTab AS LONG
            LOCAL zBuffer AS ASCIIZ*128
        
            InitCommonControls
        
            pth = LocalAlloc(%LPTR, SIZEOF(@pth))
        
            hInst = GetModuleHandle(BYVAL 0)
            hWndTab = GetDlgItem(hDlg,th.lID)
        
            GetClientRect hwndTab,rcTab
        
            ' Add a tab for each of the child dialog boxes.
            FOR i = 0 TO th.cDialogs-1
                tie.mask = %TCIF_TEXT OR %TCIF_IMAGE
                tie.iImage = -1
                GetWindowText th.DlgHdr(i).hTabDlg, zBuffer, 128
                tie.pszText = VARPTR(zBuffer)
                TabCtrl_InsertItem hwndTab, i, tie
        
                MoveWindow th.DlgHdr(i).hTabDlg, _
                           4, _
                           %TAB_LINES*23, _
                           rcTab.nRight-rcTab.nLeft-11, _
                           rcTab.nBottom-rcTab.nTop-%TAB_LINES*23-3, _
                           %TRUE
            NEXT i
        
            @pth = th
            TabDisplay hDlg, th.lID, 0
        
        END FUNCTION
        
        SUB TabDisplay(hDlg AS LONG,TabID AS LONG, TabIndex AS LONG)
        
            STATIC PrevTabIndex AS LONG
            LOCAL i AS LONG
        
            IF PrevTabIndex THEN
                ShowWindow PrevTabIndex, %SW_HIDE
            END IF
        
            IF pth = 0 THEN EXIT SUB
        
            ShowWindow @pth.DlgHdr(TabIndex).hTabDlg, %SW_SHOW
            PrevTabIndex = @pth.DlgHdr(TabIndex).hTabDlg
        
        END SUB
        
        SUB OnTabNotify (BYVAL hDlg AS LONG, BYVAL lParam AS LONG )
        
            LOCAL lpNmh   AS NMHDR PTR
        
            IF pth = 0 THEN EXIT SUB
        
            lpNmh = lParam
            SELECT CASE @lpNmh.Code
            CASE %TCN_LAST TO %TCN_FIRST
                SELECT CASE @lpNmh.idFrom
                CASE @pth.lID
                    SELECT CASE @lpNmh.Code
                    CASE %TCN_SELCHANGE
                        TabDisplay hDlg, @pth.lID, TabCtrl_GetCurSel(GetDlgItem(hDlg,@pth.lID))
                    END SELECT
                END SELECT
            END SELECT
        
        END SUB
        Do you see a problem with TabDDT.Inc (courtesy of Peter Stephensen 2000, thank you Peter) that causes the cbtab(1,2,3) functions not to work?
        Also, when using BeginPaint() and EndPaint(), is it necessary to DeleteObject for any pens created and ReleaseDC?
        When creating new graphs/displays for the other tab pages,
        would you recommend setting up seperate subclass functions to handle this via:
        Code:
         
        DefaultTabProc1 = SetWindowLong(hTabDlg1, %GWL_WNDPROC, CODEPTR(TabSubClassProc1))
        DefaultTabProc2 = SetWindowLong(hTabDlg2, %GWL_WNDPROC, CODEPTR(TabSubClassProc2))
        DefaultTabProc3 = SetWindowLong(hTabDlg3, %GWL_WNDPROC, CODEPTR(TabSubClassProc3))
        Or is there a better way?

        Semen, I like your code format better and will probably switch. It would be better for me to learn how tab controls work rather than rely on an Inc file. Will keep and study your example.

        Thank you, Lance and Semen,
        Todd Wasson





        [This message has been edited by Todd Wasson (edited May 28, 2000).]
        Todd Wasson
        http://PerformanceSimulations.Com
        PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

        Comment


        • #5
          Also, when using BeginPaint() and EndPaint(), is it necessary to DeleteObject for any pens created and ReleaseDC?
          Definitely! You should save the initial handle that is returned from the first SelectObject() call (the handle to the "existing" object), and restore it before terminating your %WM_PAINT handler.
          Also, you cannot delete any object that is still selected into a Device Context - the following psuedocode example demonstrates how to correctly handle the dynamic creation and destruction of GDI objects:
          Code:
          'psuedocode
          CASE %WM_PAINT
            hDC = BeginPaint(...)
            hBrush = CreateBrushIndirect(...)
            hBrushOld = SelectObject(hDC, hBrush)
            ...drawing code
            DeleteOnject SelectObject(hDC, hBrushOld)
            EndPaint <params>
          When creating new graphs/displays for the other tab pages, would you recommend setting up seperate subclass functions to handle this via:
          Subclassing it not usually required at all for this type of task... just a callback for the modeless dialog should be sufficient - it is all I've ever needed to do.

          I'm not at my DEV PC to run your code, but a quick look reveals that your code does not have a message pump for the child dialogs, so the events for these dialogs are never dispatched.

          The correct way to handle this would work like this:
          1. Create the parent dialog, and the tab control as a child control to that dialog. The dialog should have the extended %WS_EX_CONTROLPARENT style.
          2. Create the child (modeless) dialogs as children of the main dialog, NOT the tab control. These dialogs should have the %DS_CONTROL style.
          3. Use a DIALOG SHOW MODELESS statement for each of the child dialogs, and then hide each one with DIALOG SET STATE (or is it DIALOG SHOW STATE, I forget)
          4. Use DIALOG SHOW MODAL to launch the main dialog.
          5. In the dialog handler for the main dialog, process %WM_INITDIALOG and use PostMessage CBHNDL,%WM_USER + 999,0,0 to post a user-defined message to itself.
          6. Process %WM_USER + 999& and execute a DIALOG DOEVENTS message loop which should run until the child modeless dialogs are all destroyed. This means that this event handler should run all the time, but the callback will still be able to process other messages normally.

          The message pump for the modeless dialogs should look something like this:
          Code:
          SELECT CASE CBMSG
          CASE %WM_INTIDIALOG
            ... general init code goes here.
            PostMessage CHNDL, %WM_USER+999, 0, 0
            EXIT FUNCTION
          CASE %WM_USER + 999
            DO
              DIALOG DOEVENTS to nModelessCount& ' (check the syntax here... this is off the top of my head!)
            LOOP UNTIL nModelessCount& = 0
            EXIT FUNCTION
          The reason for using the custom message to kick the message pump off ensures that the pump's operation does not affect the operation of normal messages that the callback can process.
          I hope this helps. Tab controls are a challenge, but forgetting the message pump for modeless dialogs is a common mistake.

          There is another approach to this whole tab control application - simply use modeless dialogs for everything, and place the message pump in the main (PBMAIN) code.

          if this is a better choice for you, then the process would work like this:
          1. Create the parent dialog, and the tab control as a child control to that dialog. The dialog should have the extended %WS_EX_CONTROLPARENT style.
          2. Create the child (modeless) dialogs as children of the main dialog, NOT the tab control. These dialogs should have the %DS_CONTROL style.
          3. Use a DIALOG SHOW MODELESS statement for each of the child dialogs, and then hide each one with DIALOG SET STATE (or is it DIALOG SHOW STATE, I forget)
          4. Use DIALOG SHOW MODELESS to launch the main dialog.
          5. start the message pump right here in PBMAIN.
          Code:
          'psuedocode
          FUNCTION PBMAIN()
            DIALOG NEW 0,..., to hDlg
            CONTROL ADD "thetabcontrolclassname",..., to hTab
            DIALOG NEW hDlg,..., to hTabPage1
            DIALOG NEW hDlg,..., to hTabPage2
            DIALOG NEW hDlg,..., to hTabPage3
            DIALOG SHOW MODELESS hTabPage1 CALL ChildTabCallback1
            DIALOG SHOW MODELESS hTabPage2 CALL ChildTabCallback2
            DIALOG SHOW MODELESS hTabPage3 CALL ChildTabCallback3
            DIALOG SHOW MODELESS hDlg CALL ParentCallback
            DO
              DIALOG DOEVENTS To nModelessCOunt&
            LOOP UNTIL nModelessCount& = 0
          END FUNCTION
          That lot should keep you going for a while!


          ------------------
          Lance
          PowerBASIC Support
          mailto:[email protected][email protected]</A>
          Lance
          mailto:[email protected]

          Comment

          Working...
          X