Announcement

Collapse
No announcement yet.

Frame As Container

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

  • Frame As Container

    Working on app with a Treeview which I want to disable while doing some other operations. Disabling the TV makes
    it look ugly. In VB6 a Frame IS a container and you can put the TV in the frame, and disabling the frame, disables the
    TV without the ugly look.

    So I thought of using SetParent. Here is some code that tests SetParent with a frame and a button and it appears to work.
    Just wondering if there is any downside to this.
    Code:
    #Compile Exe
    #Dim All
    #Include Once "win32api.inc"
    %IDC_Frame = 100
    %IDC_Button = 101
    Function PBMain() As Long
     Local hDlg As DWord
     Dialog New Pixels, %HWND_Desktop, "Caption", , ,400, 400, %WS_OverlappedWindow To hDlg
     Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
     Select Case As Long CbMsg
      Case %WM_InitDialog
       Local hFrame, hButton As DWord
       Control Add Frame, CbHndl, %IDC_Frame, "", 0, 0, 100, 100
       Control Handle CbHndl, %IDC_Frame To hFrame
       Control Add Button, CbHndl, %IDC_Button, "Button", 10, 10, 50, 20
       Control Handle CbHndl, %IDC_Button To hButton
       SetParent hButton, hFrame 'make the button a child of the frame
       Control Disable CbHndl, %IDC_Frame 'disable the frame, which disables the button
       ' Control Hide CbHndl, %IDC_Frame 'likewise, hiding the frame also hides the button
     Case %WM_Command
      Select Case As Long CbCtl
       Case %IDCancel : Dialog End CbHndl
      End Select
     End Select
    End Function

  • #2
    Off Subject - hides control
    Toggle visibility (poor toggle code)
    Code:
    #INCLUDE ONCE "win32api.inc"
    %IDC_Frame   = 100
    %IDC_Button1 = 101
    %IDC_Button2 = 102
    FUNCTION PBMAIN() AS LONG
     LOCAL hDlg AS DWORD
     DIALOG NEW PIXELS, %HWND_DESKTOP, "Caption", , ,400, 400, %WS_OVERLAPPEDWINDOW TO hDlg
     LOCAL hFrame, hButton AS DWORD
     CONTROL ADD FRAME, hDlg, %IDC_Frame, "", 0, 0, 100, 100
     CONTROL ADD BUTTON, hDlg, %IDC_Button1, "Button1", 10, 10, 50, 20
     CONTROL ADD BUTTON, hDlg, %IDC_Button2, "Toggle", 10, 110, 50, 20
     CONTROL HANDLE hDlg, %IDC_Frame   TO hFrame
     CONTROL HANDLE hDlg, %IDC_Button1 TO hButton
     SetParent hButton, hFrame
     DIALOG SHOW MODAL hDlg CALL DlgProc
    END FUNCTION
    
    CALLBACK FUNCTION DlgProc() AS LONG
     STATIC OnOff AS LONG
     SELECT CASE AS LONG CBMSG
      CASE %WM_COMMAND
       SELECT CASE AS LONG CBCTL
        CASE %IDCANCEL   :DIALOG END CBHNDL
        CASE %IDC_BUTTON1:BEEP
    
        CASE %IDC_BUTTON2  'Toggle
         IF OnOff = 0 THEN CONTROL HIDE CBHNDL,%IDC_FRAME     :OnOff=1:FUNCTION = 1:EXIT FUNCTION
         IF OnOff = 1 THEN CONTROL NORMALIZE CBHNDL,%IDC_FRAME:OnOff=0:FUNCTION = 1:EXIT FUNCTION
    
       END SELECT
     END SELECT
    END FUNCTION
    Code:
    IF IsWindowVisible(ghFrame) THEN CONTROL HIDE CBHNDL,%IDC_FRAME ELSE CONTROL NORMALIZE CBHNDL,%IDC_FRAME
    This also hides so this does not answer if code in post #1 has any downside.
    Last edited by Mike Doty; 7 Apr 2021, 01:10 PM.
    How long is an idea?

    Comment


    • #3
      Any downside to post #1?
      How long is an idea?

      Comment


      • #4
        Mike, how would you capture the click of %IDC_Button1? Now that the parent of the button is hFrame the message never gets back to hDlg. In this code the BEEP never is triggered.

        Comment


        • #5
          I must have modified the code incorrectly when I changed to using a global.
          How long is an idea?

          Comment


          • #6
            I tried...
            Code:
             SetWindowLong(hFrame, %GWL_WNDPROC, GetWindowLong(hDlg, %GWL_WNDPROC))
            but that didn't work. Not sure how to read messages that don't have a callback.

            Comment


            • #7
              SubClassing the "Frame" control (or any control parent) and passing on messages to parent dialog works. Example below.
              '
              Code:
              #INCLUDE ONCE "win32api.inc"
              %IDC_Frame   = 100
              %IDC_Button1 = 101
              %IDC_Button2 = 102
              
              GLOBAL oldProc AS DWORD
              
              '====================================================================
              FUNCTION PBMAIN() AS LONG
                LOCAL hDlg, hFrame, hButton AS DWORD
              
                DIALOG NEW PIXELS, %HWND_DESKTOP, "Caption", , ,400, 400, %WS_OVERLAPPEDWINDOW TO hDlg
              
                ' A Frame control is actually Button with BS_GROUPBOX style...
                CONTROL ADD FRAME, hDlg, %IDC_Frame, "", 0, 0, 100, 100
                CONTROL ADD BUTTON, hDlg, %IDC_Button1, "Button1", 10, 10, 50, 20
                CONTROL ADD BUTTON, hDlg, %IDC_Button2, "Toggle", 10, 110, 50, 20
                CONTROL HANDLE hDlg, %IDC_Frame   TO hFrame
                CONTROL HANDLE hDlg, %IDC_Button1 TO hButton
              
                SetParent hButton, hFrame  ' then subclass parent Frame to pass on messages
                oldProc = SetWindowLong(hFrame, %GWL_WNDPROC, CODEPTR(FrameProc))
              
                DIALOG SHOW MODAL hDlg CALL DlgProc
              
                SetWindowLong hFrame, %GWL_WNDPROC, oldProc  ' un-subclass at exit
              
              END FUNCTION
              
              '====================================================================
              CALLBACK FUNCTION DlgProc() AS LONG
                STATIC OnOff AS LONG
                SELECT CASE AS LONG CBMSG
              
                CASE %WM_COMMAND
                 SELECT CASE AS LONG CBCTL
                  CASE %IDCANCEL    :DIALOG END CBHNDL
                  CASE %IDC_BUTTON1 : ? "Msg from Frame"  ' <- passed on from subclassed Frame
              
                  CASE %IDC_BUTTON2  'Toggle
                   IF OnOff = 0 THEN CONTROL HIDE CBHNDL,%IDC_FRAME     :OnOff=1:FUNCTION = 1:EXIT FUNCTION
                   IF OnOff = 1 THEN CONTROL NORMALIZE CBHNDL,%IDC_FRAME:OnOff=0:FUNCTION = 1:EXIT FUNCTION
              
                 END SELECT
               END SELECT
              END FUNCTION
              
              '======================== SUBCLASSED FRAME ==========================
              FUNCTION FrameProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
                                 BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
                ' pass on %WM_COMMAND messages to parent dialog
                IF wMsg = %WM_COMMAND THEN PostMessage(GetParent(hWnd), wMsg, wParam, lParam)
                FUNCTION = CallWindowProc(oldProc, hWnd, wMsg, wParam, lParam)
              END FUNCTION
              '

              Comment


              • #8
                If the OP doesn't need messages from the child control while it is disabled.
                The rest of the time the main dialog can be it's parent / receive messages.

                This version toggles the paternity at the same time as the enabled state..
                Code:
                #Compile Exe
                #Dim All
                #Include Once "win32api.inc"
                %IDC_Frame = 100
                %IDC_Button = 101
                 
                Function PBMain() As Long
                 Local hDlg As DWord
                  Dialog New Pixels, %HWND_Desktop, "RClick - toggle", ,,400,400, %WS_OverlappedWindow To hDlg
                  Dialog Show Modal hDlg Call DlgProc
                End Function
                '------------------/PBMain
                 
                CallBack Function DlgProc() As Long
                 Select Case As Long Cb.Msg
                  Case %WM_InitDialog
                   Static hFrame, hButton, Disabled As DWord
                   Control Add Frame, Cb.Hndl, %IDC_Frame, "Enabled", 0,0,100,100   ' NB frame same origin as hDlg
                   Control Handle Cb.Hndl, %IDC_Frame To hFrame
                   Control Add Button, Cb.Hndl, %IDC_Button, "Button", 20,20,50,20  ' Located in parent client area
                   Control Handle Cb.Hndl, %IDC_Button To hButton
                 
                  Case %WM_Command
                    Select Case As Long Cb.Ctl
                      Case %IDCancel : Dialog End Cb.Hndl
                      Case %IDC_Button : WinBeep 400,200
                    End Select
                 
                  Case %WM_ContextMenu                    ' Example: RClick = Event to trigger enabled state change
                    If Disabled Then                      ' Enable the Frame (and enable the Button
                      SetParent hButton, Cb.Hndl          ' make the button a child of the main dialog
                      Control Enable Cb.Hndl, %IDC_Frame  : Disabled = 0
                      Control Set Text Cb.Hndl, %IDC_Frame, "&Enabled"
                    Else
                      SetParent hButton, hFrame            ' make the button a child of the frame and disable
                      Control Disable Cb.Hndl, %IDC_Frame : Disabled = 1
                      Control Set Text Cb.Hndl, %IDC_Frame, "&Disabled"
                    End If
                 End Select
                End Function
                '------------------/DlgProc
                Rgds, Dave

                Comment


                • #9
                  Yes, there are other ways, but always fun to investigate. I played with it a little and found that the "Frame" control eats some messages, like %WM_SETFOCUS and keydown, etc. If one instead add a Button with %BS_GROUPBOX OR %WS_TABSTOP - which is exactly a Frame control - the "Frame" control's subclass can receive and pass on such messages to its child controls. Example (this one also passes on tab/focus to "frame's"next child control)
                  '
                  Code:
                  #INCLUDE ONCE "win32api.inc"
                  %IDC_Frame   = 100
                  %IDC_Button1 = 101
                  %IDC_Button2 = 102
                  
                  GLOBAL oldProc AS DWORD
                  
                  '====================================================================
                  FUNCTION PBMAIN() AS LONG
                    LOCAL hDlg, hFrame, hButton AS DWORD
                  
                    DIALOG NEW PIXELS, %HWND_DESKTOP, "Caption", , ,400, 400, %WS_OVERLAPPEDWINDOW TO hDlg
                  
                    ' A Frame control is actually Button with BS_GROUPBOX style...
                    ' CONTROL ADD FRAME, hDlg, %IDC_Frame, "&Frame", 0, 0, 100, 100
                    CONTROL ADD BUTTON, hDlg, %IDC_Frame, "Frame", 0, 0, 100, 100, %BS_GROUPBOX OR %WS_TABSTOP
                    CONTROL HANDLE hDlg, %IDC_Frame   TO hFrame
                  
                    CONTROL ADD BUTTON, hDlg, %IDC_Button1, "Button1", 10, 15, 70, 20
                    CONTROL ADD BUTTON, hDlg, %IDC_Button2, "Toggle", 10, 110, 70, 20
                    CONTROL HANDLE hDlg, %IDC_Button1 TO hButton
                  
                    SetParent hButton, hFrame  ' then subclass parent Frame to pass on messages
                    oldProc = SetWindowLong(hFrame, %GWL_WNDPROC, CODEPTR(FrameProc))
                  
                    DIALOG SHOW MODAL hDlg CALL DlgProc
                  
                    SetWindowLong hFrame, %GWL_WNDPROC, oldProc  ' un-subclass at exit
                  
                  END FUNCTION
                  
                  '====================================================================
                  CALLBACK FUNCTION DlgProc() AS LONG
                    STATIC OnOff AS LONG
                    SELECT CASE AS LONG CBMSG
                  
                    CASE %WM_COMMAND
                     SELECT CASE AS LONG CBCTL
                      CASE %IDCANCEL    :DIALOG END CBHNDL
                      CASE %IDC_BUTTON1 : ? "Msg from Frame"  ' <- passed on from subclassed Frame
                  
                      CASE %IDC_BUTTON2  'Toggle
                       IF OnOff = 0 THEN CONTROL HIDE CBHNDL,%IDC_FRAME     :OnOff=1:FUNCTION = 1:EXIT FUNCTION
                       IF OnOff = 1 THEN CONTROL NORMALIZE CBHNDL,%IDC_FRAME:OnOff=0:FUNCTION = 1:EXIT FUNCTION
                  
                     END SELECT
                   END SELECT
                  END FUNCTION
                  
                  '======================== SUBCLASSED FRAME ==========================
                  FUNCTION FrameProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
                                     BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
                    ' pass on %WM_COMMAND messages to parent dialog
                    SELECT CASE wMsg
                    CASE %WM_COMMAND  : PostMessage(GetParent(hWnd), wMsg, wParam, lParam)
                    CASE %WM_SETFOCUS : SetFocus(GetWindow(hWnd, %GW_CHILD)) ' pass on focus.
                    END SELECT
                  
                    FUNCTION = CallWindowProc(oldProc, hWnd, wMsg, wParam, lParam)
                  END FUNCTION
                  '

                  Comment


                  • #10
                    Hi Borje,

                    in your code, it would be nice to have draw a red border around the Frame control
                    to make it distinguishable from the main dialog. How do I do this?

                    Code:
                      ' A Frame control is actually Button with BS_GROUPBOX style...
                      ' CONTROL ADD FRAME, hDlg, %IDC_Frame, "&Frame", 0, 0, 100, 100
                      CONTROL ADD BUTTON, hDlg, %IDC_Frame, "Frame", _
                                 10, 5, 100, 100, %BS_GROUPBOX OR %WS_TABSTOP
                      CONTROL HANDLE hDlg, %IDC_Frame   TO hFrame

                    Comment


                    • #11
                      Thanks guys. I'll take a look at them. I was about to try subclassing or CONTROL ADD "BUTTON" so I could have a callback.

                      Tim: Not sure why but the page is now color gibberish but Pierre posted a Borje FrameAroundControl program here. (wish I could read it to be sure it was the correct post)

                      Comment


                      • #12
                        If you can't read it, here it is.
                        Code:
                        #COMPILE EXE '#Win 8.04#
                        #INCLUDE "WIN32API.INC"
                        
                        GLOBAL hDlg AS DWORD
                        
                        %Label01   = 111
                        %Label02   = 112
                        %Label03   = 113
                        
                        %Edit01    = 121
                        %Edit02    = 122
                        %Edit03    = 123
                        
                        %Button01  = 131
                        %Button02  = 132
                        %Button03  = 133
                        %Button04  = 134
                        %Button05  = 135
                        
                        %Erase     = -1
                        %EraseDraw =  0
                        %Draw      =  1
                        
                        SUB FrameAroundControl(BYVAL hDialog AS DWORD, BYVAL hControl AS DWORD, BYVAL Action AS LONG)
                         LOCAL  rcControl     AS RECT                'Action: %Erase = -1, %EraseDraw = 0, %Draw = 1
                         STATIC rcControlPrev AS RECT
                         STATIC hControlPrev  AS DWORD
                         LOCAL  hDC           AS DWORD
                         LOCAL  hBrush        AS DWORD
                         LOCAL  hPen          AS DWORD
                         LOCAL  COLOR         AS DWORD
                        
                         hDC = GetDC(hDialog)                            'Paint on dialog
                        
                         IF (Action <= %EraseDraw) AND hControlPrev THEN 'Erase previous rectangle if any
                           COLOR     = GetSysColor(%COLOR_3DFACE)        'Set background dialog color
                           rcControl = rcControlPrev                     'Set previous rectangle position and size
                           GOSUB DoRectangle                             'Draw the rectangle with background dialog color
                           hControlPrev  = 0                             'Do it only once
                         END IF
                        
                         IF Action >= %EraseDraw THEN                              'Draw rectangle
                           GetWindowRect(hControl, rcControl)                      'Get control's position and size on screen
                           MapWindowPoints(0, hDialog, BYVAL VARPTR(rcControl), 2) 'Convert to dialog coordinates
                           InflateRect(rcControl, 2, 2)                            'Increase to draw around control
                           COLOR = &H0000FF                                        'Set a red color
                           GOSUB DoRectangle                                       'Draw the rectangle
                           hControlPrev  = hControl                                'Backup handle for future erase
                           rcControlPrev = rcControl                               'Backup rectangle for future erase
                         END IF
                         ReleaseDC(hDialog, hDC)                                   'Clean up
                        
                         EXIT SUB '-------------------------------------------------------------------
                        
                         DoRectangle:
                           hPen   = CreatePen(%PS_SOLID, 1, COLOR)                 'Rectangle drawing 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, rcControl.nLeft, rcControl.nTop, rcControl.nRight, rcControl.nBottom) 'Draw the rectangle
                           SelectObject(hDC, hBrush)                               'Return original pen and brush
                           DeleteObject(SelectObject(hDC, hPen))                   'Clean up
                         RETURN
                        END SUB
                        
                        CALLBACK FUNCTION DlgProc() AS LONG
                          LOCAL ClientSizeX AS LONG
                          LOCAL ClientSizeY AS LONG
                        
                          SELECT CASE CBMSG
                          CASE %WM_INITDIALOG
                            FrameAroundControl(hDlg, GetFocus(), %Draw)
                          CASE %WM_PAINT
                            FrameAroundControl(hDlg, GetFocus(), %Draw) 'Repaint
                          CASE %WM_COMMAND
                            SELECT CASE CBCTL
                            CASE %Button01 TO %Button05
                              IF (CBCTLMSG = %BN_SETFOCUS) THEN
                                FrameAroundControl(hDlg, CBLPARAM, %EraseDraw)
                              END IF
                              IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                IF CBCTL = %Button05 THEN DIALOG END hDlg, 0
                              END IF
                            CASE %IDCANCEL
                              IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                DIALOG END hDlg, 0
                              END IF
                            CASE %IDOK
                              IF GetDlgCtrlID(GetFocus) = %Button04 THEN
                                DIALOG END hDlg, 0
                              ELSE
                                SetFocus GetNextDlgTabItem(hDlg, GetFocus, 0)
                                FrameAroundControl(hDlg, GetFocus(), %EraseDraw)
                              END IF
                            CASE %Edit01 TO %Edit03
                              SELECT CASE CBCTLMSG
                                CASE %EN_SETFOCUS
                                  FrameAroundControl(hDlg, CBLPARAM, %EraseDraw)
                               END SELECT
                            END SELECT
                          CASE %WM_SIZE
                            IF CBWPARAM <> %SIZE_MINIMIZED THEN
                              ClientSizeX = LO(WORD, CBLPARAM)
                              ClientSizeY = HI(WORD, CBLPARAM)
                              SetWindowPos(GetDlgItem(hDlg, %Button04), 0, 10, 200, ClientSizeX - 20, 50, %SWP_NOZORDER OR %SWP_NOMOVE)
                              FrameAroundControl(hDlg, GetFocus(), %EraseDraw) 'Repaint
                            END IF
                         END SELECT
                        END FUNCTION
                        
                        FUNCTION PBMAIN
                        
                          DIALOG FONT "Segoe UI", 9
                        
                          DIALOG NEW %HWND_DESKTOP, "Red focus FameControl", , , 170, 155, _
                          %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_SYSMENU OR %WS_THICKFRAME, 0 TO hDlg
                        
                          CONTROL ADD LABEL, hDlg, %Label01, "Name", 5, 5,  50,  10
                          CONTROL ADD TEXTBOX, hDlg, %Edit01, "Jean Leloup", 5, 16, 100, 12
                        
                          CONTROL ADD LABEL, hDlg, %Label02, "City", 5, 35,  50, 10
                          CONTROL ADD TEXTBOX, hDlg, %Edit02, "Natashquan", 5, 46, 100, 12
                        
                          CONTROL ADD LABEL, hDlg, %Label03, "Phone", 5, 65,  50, 10
                          CONTROL ADD TEXTBOX, hDlg, %Edit03, "123-456-7890", 5, 76, 100, 12
                        
                          CONTROL ADD BUTTON, hDlg, %Button01, "Nothing", 115, 14, 50, 15, _
                          %WS_GROUP OR %WS_TABSTOP OR %BS_CENTER OR %BS_VCENTER OR %BS_NOTIFY, %WS_EX_LEFT
                          CONTROL ADD BUTTON, hDlg, %Button02, "Nothing", 115, 45, 50, 15, _
                          %WS_TABSTOP OR %BS_CENTER OR %BS_VCENTER OR %BS_NOTIFY, %WS_EX_LEFT
                          CONTROL ADD BUTTON, hDlg, %Button03, "Nothing", 115, 75, 50, 15, _
                          %WS_TABSTOP OR %BS_CENTER OR %BS_VCENTER OR %BS_NOTIFY, %WS_EX_LEFT
                          CONTROL ADD BUTTON, hDlg, %Button04, "Control size proportional to dialog", 5, 100, 160, 25, _
                          %WS_TABSTOP OR %BS_CENTER OR %BS_VCENTER OR %BS_NOTIFY, %WS_EX_LEFT
                          CONTROL ADD BUTTON, hDlg, %Button05, "E&xit", 115, 135, 50, 15, _
                          %WS_TABSTOP OR %BS_CENTER OR %BS_VCENTER OR %BS_NOTIFY, %WS_EX_LEFT
                        
                          DIALOG SHOW MODAL hDlg, CALL DlgProc
                        END FUNCTION

                        Comment


                        • #13
                          Thank you Frank, but gotten problem as I tried to set focus on the "Frame" to colorized it .
                          But it fail and colorized the Button inside the frame itself ?

                          How to fix this ? here's the code which I combine with Pierre's

                          Code:
                           'Frame container.bas
                           'https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/806582-frame-as-container?p=806696#post806696
                          
                             ' Subclass a Frame inside a dialog and we can toggle to make
                             ' Frame control visible or invisible
                             ' Once the Frame is invisible, its button no longer works
                             ' The Frame control is actually a button control
                          
                          #INCLUDE ONCE "win32api.inc"
                          
                          %IDC_Frame    = 100
                          %IDC_FrameBtn = 101
                          %IDC_ToggleBtn = 102
                          
                          GLOBAL oldProc AS DWORD
                          GLOBAL hFrame  AS DWORD
                          
                          %Erase     = -1
                          %EraseDraw =  0
                          %Draw      =  1
                          
                          
                          
                          
                          '====================================================================
                          FUNCTION PBMAIN() AS LONG
                            LOCAL hDlg,  hButton AS DWORD
                          
                            DIALOG NEW PIXELS, %HWND_DESKTOP, "Caption",_
                                     , ,400, 400, %WS_OVERLAPPEDWINDOW TO hDlg
                          
                            DIALOG SET COLOR hDlg,%BLACK, %RGB_MINTCREAM
                          
                            ' A Frame control is actually Button with BS_GROUPBOX style...
                            ' CONTROL ADD FRAME, hDlg, %IDC_Frame, "&Frame", 0, 0, 100, 100
                            CONTROL ADD BUTTON, hDlg, %IDC_Frame, "Frame", _
                                       10, 5, 200, 100, %BS_GROUPBOX OR %WS_TABSTOP
                            CONTROL HANDLE hDlg, %IDC_Frame   TO hFrame
                          
                            ' place a button inside the Frame control
                            CONTROL ADD BUTTON, hDlg, %IDC_FrameBtn, "Button1", 10, 15, 70, 20
                            CONTROL HANDLE hDlg, %IDC_FrameBtn TO hButton
                          
                            CONTROL ADD BUTTON, hDlg, %IDC_ToggleBtn,_
                               "Toggle Frame visibility", 10, 210, 130, 20
                          
                          
                            ' then subclass parent Frame to pass on messages
                            SetParent hButton, hFrame
                            oldProc = SetWindowLong(hFrame, %GWL_WNDPROC, CODEPTR(FrameProc))
                          
                            DIALOG SHOW MODAL hDlg CALL DlgProc
                          
                            ' un-subclass at exit
                            SetWindowLong hFrame, %GWL_WNDPROC, oldProc
                          
                          END FUNCTION
                          
                          '===================================================
                          CALLBACK FUNCTION DlgProc() AS LONG
                            STATIC FlagOnOff AS LONG
                          
                            SELECT CASE AS LONG CB.MSG
                          
                            CASE %WM_COMMAND
                             SELECT CASE AS LONG CB.CTL
                          
                               CASE %WM_INITDIALOG
                                FrameAroundControl(CB.HNDL, CB.LPARAM, %Draw)
                          
                            CASE %WM_PAINT
                                FrameAroundControl(CB.HNDL, CB.LPARAM, %Draw) 'Repaint
                          
                          
                              CASE %IDCANCEL
                                    DIALOG END CB.HNDL
                          
                              CASE %IDC_FrameBtn
                                  ' <- passed on from subclassed Frame
                                   ? "Msg from Frame"
                          
                              CASE %IDC_ToggleBtn
                                  'Toggle frame visibility
                               IF FlagOnOff = 0 THEN
                                   CONTROL HIDE CB.HNDL,%IDC_FRAME
                                   FlagOnOff=1
                                   FUNCTION = 1
                                   EXIT FUNCTION
                               END IF
                               IF FlagOnOff = 1 THEN
                                   CONTROL NORMALIZE CB.HNDL,%IDC_FRAME
                                   SetFocus hFrame  ' GetNextDlgTabItem(CB.HNDL, GetFocus, 0)
                                   FrameAroundControl(CB.HNDL, GetFocus(), %EraseDraw)
                                   FlagOnOff =0
                                   FUNCTION = 1
                                   EXIT FUNCTION
                               END IF
                          
                             END SELECT
                           END SELECT
                          END FUNCTION
                          
                          
                          '======================== SUBCLASSED FRAME ==========================
                          FUNCTION FrameProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
                                             BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
                            ' pass on %WM_COMMAND messages to parent dialog
                            SELECT CASE wMsg
                                CASE %WM_COMMAND
                                     PostMessage(GetParent(hWnd), wMsg, wParam, lParam)
                          
                                CASE %WM_SETFOCUS
                                     SetFocus(GetWindow(hWnd, %GW_CHILD)) ' pass on focus.
                          
                            END SELECT
                          
                            FUNCTION = CallWindowProc(oldProc, hWnd, wMsg, wParam, lParam)
                          END FUNCTION
                          
                          
                          
                          SUB FrameAroundControl(BYVAL hDialog AS DWORD, BYVAL hControl AS DWORD, BYVAL Action AS LONG)
                           LOCAL  rcControl     AS RECT                'Action: %Erase = -1, %EraseDraw = 0, %Draw = 1
                           STATIC rcControlPrev AS RECT
                           STATIC hControlPrev  AS DWORD
                           LOCAL  hDC           AS DWORD
                           LOCAL  hBrush        AS DWORD
                           LOCAL  hPen          AS DWORD
                           LOCAL  ConColor         AS DWORD
                          
                           hDC = GetDC(hDialog)                            'Paint on dialog
                          
                           IF (Action <= %EraseDraw) AND hControlPrev THEN 'Erase previous rectangle if any
                             ConColor     =  %RGB_YELLOW 'GetSysColor(%COLOR_3DFACE)        'Set background dialog color
                             rcControl = rcControlPrev                     'Set previous rectangle position and size
                             GOSUB DoRectangle                             'Draw the rectangle with background dialog color
                             hControlPrev  = 0                             'Do it only once
                           END IF
                          
                           IF Action >= %EraseDraw THEN                              'Draw rectangle
                             GetWindowRect(hControl, rcControl)                      'Get control's position and size on screen
                             MapWindowPoints(0, hDialog, BYVAL VARPTR(rcControl), 2) 'Convert to dialog coordinates
                             InflateRect(rcControl, 2, 2)                            'Increase to draw around control
                             ConColor = &H0000FF                                        'Set a red color
                             GOSUB DoRectangle                                       'Draw the rectangle
                             hControlPrev  = hControl                                'Backup handle for future erase
                             rcControlPrev = rcControl                               'Backup rectangle for future erase
                           END IF
                           ReleaseDC(hDialog, hDC)                                   'Clean up
                          
                           EXIT SUB '-------------------------------------------------------------------
                          
                           DoRectangle:
                             hPen   = CreatePen(%PS_SOLID, 1, ConColor)                 'Rectangle drawing 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, rcControl.nLeft, rcControl.nTop, rcControl.nRight, rcControl.nBottom) 'Draw the rectangle
                             SelectObject(hDC, hBrush)                               'Return original pen and brush
                             DeleteObject(SelectObject(hDC, hPen))                   'Clean up
                           RETURN
                          END SUB

                          Comment


                          • #14
                            Aha, found the problem it lies inside the FUNCTION FrameProc

                            by commenting out these lines I got the red border over the "Frame"

                            Code:
                                '  CASE %WM_SETFOCUS
                                     '  SetFocus(GetWindow(hWnd, %GW_CHILD)) ' pass on focus.

                            Comment


                            • #15
                              My latest rendition, only issue is that it doesn't show the external red frame at the start of the program
                              until I clicked the toggle button

                              Now, how do I placed the external red frame when the program begins?


                              Code:
                               'Frame container.bas
                               'https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/806582-frame-as-container?p=806696#post806696
                              
                                 ' Subclass a Frame inside a dialog and we can toggle to make
                                 ' Frame control visible or invisible
                                 ' Once the Frame is invisible, its button no longer works
                                 ' The Frame control is actually a button control
                              
                              #INCLUDE ONCE "win32api.inc"
                              
                              %IDC_Frame    = 100
                              %IDC_FrameBtn = 101
                              %IDC_ToggleBtn = 102
                              
                              GLOBAL oldProc AS DWORD
                              GLOBAL hFrame  AS DWORD
                              
                              %FlagErase     = -1
                              %FlagEraseDraw =  0
                              %FlagDraw      =  1
                              
                              
                              
                              
                              '====================================================================
                              FUNCTION PBMAIN() AS LONG
                                LOCAL hDlg,  hButton AS DWORD
                              
                                DIALOG NEW PIXELS, %HWND_DESKTOP, "Caption",_
                                         , ,400, 400, %WS_OVERLAPPEDWINDOW TO hDlg
                              
                                DIALOG SET COLOR hDlg,%BLACK, %RGB_MINTCREAM
                              
                                ' A Frame control is actually Button with BS_GROUPBOX style...
                                ' CONTROL ADD FRAME, hDlg, %IDC_Frame, "&Frame", 0, 0, 100, 100
                                CONTROL ADD BUTTON, hDlg, %IDC_Frame, "Frame", _
                                           15, 15, 200, 100, %BS_GROUPBOX OR %WS_TABSTOP
                                CONTROL HANDLE hDlg, %IDC_Frame   TO hFrame
                              
                                ' place a button inside the Frame control
                                CONTROL ADD BUTTON, hDlg, %IDC_FrameBtn, "Button1", 10, 25, 70, 20
                                CONTROL HANDLE hDlg, %IDC_FrameBtn TO hButton
                              
                                CONTROL ADD BUTTON, hDlg, %IDC_ToggleBtn,_
                                   "Toggle Frame visibility", 10, 210, 130, 20
                              
                              
                                ' then subclass parent Frame to pass on messages
                                SetParent hButton, hFrame
                                oldProc = SetWindowLong(hFrame, %GWL_WNDPROC, CODEPTR(FrameProc))
                              
                                DIALOG SHOW MODAL hDlg CALL DlgProc
                              
                                ' un-subclass at exit
                                SetWindowLong hFrame, %GWL_WNDPROC, oldProc
                              
                              END FUNCTION
                              
                              '===================================================
                              CALLBACK FUNCTION DlgProc() AS LONG
                                STATIC FlagOnOff AS LONG
                              
                                SELECT CASE AS LONG CB.MSG
                                  CASE %WM_INITDIALOG
                                   SetFocus hFrame
                                   FrameAroundControl(CB.HNDL, GetFocus(), %FlagDraw)
                              
                              
                                CASE %WM_COMMAND
                                 SELECT CASE AS LONG CB.CTL
                              
                              
                                  CASE %IDCANCEL
                                        DIALOG END CB.HNDL
                              
                                  CASE %IDC_FrameBtn
                                      ' <- passed on from subclassed Frame
                                       ? "Msg from Frame"
                              
                                  CASE %IDC_ToggleBtn
                                      'Toggle frame visibility
                                   IF FlagOnOff = 0 THEN
                                       SetFocus hFrame
                                       FrameAroundControl(CB.HNDL, GetFocus(), %FlagErase)
                                       CONTROL HIDE CB.HNDL,%IDC_FRAME
                                       FlagOnOff=1
                                       FUNCTION = 1
                                       EXIT FUNCTION
                                   END IF
                                   IF FlagOnOff = 1 THEN
                                       CONTROL NORMALIZE CB.HNDL,%IDC_FRAME
                                       SetFocus hFrame
                                       FrameAroundControl(CB.HNDL, GetFocus(), %FlagDraw)
                                       FlagOnOff =0
                                       FUNCTION = 1
                                       EXIT FUNCTION
                                   END IF
                              
                                 END SELECT
                               END SELECT
                              END FUNCTION
                              
                              
                              '======================== SUBCLASSED FRAME ==========================
                              FUNCTION FrameProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
                                                 BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
                                ' pass on %WM_COMMAND messages to parent dialog
                                SELECT CASE wMsg
                                    CASE %WM_COMMAND
                                         PostMessage(GetParent(hWnd), wMsg, wParam, lParam)
                              
                                  '  can't have this focus on the button1 if want to draw the
                                  '  external red frame
                                  '  CASE %WM_SETFOCUS
                                       '  SetFocus(GetWindow(hWnd, %GW_CHILD)) ' pass on focus.
                              
                                END SELECT
                              
                                FUNCTION = CallWindowProc(oldProc, hWnd, wMsg, wParam, lParam)
                              END FUNCTION
                              
                              
                              
                              SUB FrameAroundControl(BYVAL hDialog AS DWORD, BYVAL hControl AS DWORD, BYVAL Action AS LONG)
                               LOCAL  rcControl     AS RECT                'Action: %FlagErase = -1, %FlagEraseDraw = 0, %FlagDraw = 1
                               STATIC rcControlPrev AS RECT
                               STATIC hControlPrev  AS DWORD
                               LOCAL  hDC           AS DWORD
                               LOCAL  hBrush        AS DWORD
                               LOCAL  hPen          AS DWORD
                               LOCAL  ConColor         AS DWORD
                              
                               hDC = GetDC(hDialog)                            'Paint on dialog
                               IF (Action < %FlagEraseDraw) AND hControlPrev THEN 'Erase previous rectangle if any
                                 ConColor     =  %RGB_MINTCREAM                'Set original background dialog color
                                 rcControl = rcControlPrev                     'Set previous rectangle position and size
                                 GOSUB DoRectangle                             'Draw the rectangle with background dialog color
                                 hControlPrev  = 0                             'Do it only once
                               END IF
                              
                               IF Action >= %FlagEraseDraw THEN                              'Draw rectangle
                                 GetWindowRect(hControl, rcControl)                      'Get control's position and size on screen
                                 MapWindowPoints(0, hDialog, BYVAL VARPTR(rcControl), 2) 'Convert to dialog coordinates
                                 InflateRect(rcControl, 6, 6)                            'Increase to draw around control
                                 ConColor = &H0000FF                                        'Set a red color
                                 GOSUB DoRectangle                                       'Draw the rectangle
                                 hControlPrev  = hControl                                'Backup handle for future erase
                                 rcControlPrev = rcControl                               'Backup rectangle for future erase
                               END IF
                               ReleaseDC(hDialog, hDC)                                   'Clean up
                              
                               EXIT SUB
                              
                               DoRectangle:
                                 hPen   = CreatePen(%PS_SOLID, 1, ConColor)                 'Rectangle drawing 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, rcControl.nLeft, rcControl.nTop, rcControl.nRight, rcControl.nBottom) 'Draw the rectangle
                                 SelectObject(hDC, hBrush)                               'Return original pen and brush
                                 DeleteObject(SelectObject(hDC, hPen))                   'Clean up
                               RETURN
                              END SUB

                              Comment

                              Working...
                              X