Announcement

Collapse
No announcement yet.

Proof of Concept Windows Controls With OOP

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

  • Proof of Concept Windows Controls With OOP

    Just put this together in an hour or so but it at least flushes out for me some of the issues concerning this idea I have had for some time, and that is wrapping some Sdk style control creation/interface elements in OOP machinery. I had intended trying it in C++ but now with this really awesome PowerBASIC compiler I tried it first with it. It seems to work as far as that goes. The idea is as follows. With Visual Basic it was really easy to interface with controls on a Form/Dialog. Everything was wrapped in OOP syntax. Also, controls were an application global resource. If you had a textbox on a Form named txtTextBox, it could be addressed from anywhere in the project. If you wanted to put text in it all you had to do was this...

    txtTextBox.Text = "Some Text In A Textbox"

    Certainly easier than...

    Local szBuffer As Asciiz*128
    szBuffer="Some Text In A Text Box"
    Call SetWindowText(hTextBox,szBuffer)

    Well, here is a very rough sketch of what it would look like in PowerBASIC OOP.

    Code:
    'For PowerBASIC Windows 9.0
    #Compile     Exe
    #Include     "Win32api.inc"
    %IDC_TEXT1   =1200
    
    Class CEdit
      Instance dwExStyle As Dword
      Instance dwStyle   As Dword
      Instance x         As Long
      Instance y         As Long
      Instance dwWidth   As Dword
      Instance dwHeight  As Dword
      Instance hEdit     As Dword
      Instance hParent   As Dword
      Instance hInstance As Dword
      Instance dwPtr     As Dword Ptr
      Instance Equate    As Long
    
      Interface IEdit : Inherit IUnknown
        Property Set Text(Byval strText As String)
          Local szBuffer As Asciiz*128
          szBuffer=strText
          Call SetWindowText(hEdit,szBuffer)
        End Property
      
        Method CreateWindow(hMain As Dword, iEquate As Dword, hIns As Dword) As Dword
          dwExStyle=0
          dwStyle=%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER
          x=55            :  y=40
          dwWidth=210     :  dwHeight=25
          hParent=hMain   :  Equate=iEquate
          hInstance=hIns  :  dwPtr=0
          hEdit=CreateWindowEx(dwExStyle,"edit","",dwStyle,x,y,dwWidth,dwHeight,hMain,iEquate,hInstance,Byval dwPtr)
          Method=hEdit
        End Method
        
        Method GetWindowHandle(hParent As Dword, iEquate As Dword) As Dword
          Method=GetDlgItem(hParent,iEquate)
        End Method
      End Interface
    End Class
    
                          
    Type WndEventArgs
      wParam As Long
      lParam As Long
      hWnd   As Dword
      hInst  As Dword
    End Type
    
    Declare Function FnPtr(wea As WndEventArgs) As Long
    
    Type MessageHandler
      wMessage As Long
      dwFnPtr As Dword
    End Type
    Global MsgHdlr() As MessageHandler
    Global txtTextBox As IEdit
    
     
    Function fnWndProc_OnCreate(wea As WndEventArgs) As Long
      Local pCreateStruct As CREATESTRUCT Ptr
      Local hEdit As Dword
      
      pCreateStruct=wea.lParam
      [email protected]
      txtTextBox=Class "CEdit"
      hEdit=txtTextBox.CreateWindow(wea.hWnd,%IDC_TEXT1,wea.hInst)
      txtTextBox.Text="Compile Without Compromise!"
      
      fnWndProc_OnCreate=0
    End Function
    
    
    Function fnWndProc_OnNotify(wea As WndEventArgs) As Long
      fnWndProc_OnNotify=0
    End Function
    
    
    Function fnWndProc_OnClose(wea As WndEventArgs) As Long
      Call PostQuitMessage(0)
      fnWndProc_OnClose=0
    End Function
    
    
    Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
      Static wea As WndEventArgs
      Register iReturn As Long
      Register i As Long
    
      For i=0 To 2
        If wMsg=MsgHdlr(i).wMessage Then
           wea.hWnd=hWnd: wea.wParam=wParam: wea.lParam=lParam
           Call Dword MsgHdlr(i).dwFnPtr Using FnPtr(wea) To iReturn
           fnWndProc=iReturn
           Exit Function
        End If
      Next i
    
      fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
    End Function
    
    
    Sub AttachMessageHandlers()
      ReDim MsgHdlr(2) As MessageHandler  'Associate Windows Message With Message Handlers
      MsgHdlr(0).wMessage=%WM_CREATE   :   MsgHdlr(0).dwFnPtr=CodePtr(fnWndProc_OnCreate)
      MsgHdlr(1).wMessage=%WM_NOTIFY   :   MsgHdlr(1).dwFnPtr=CodePtr(fnWndProc_OnNotify)
      MsgHdlr(2).wMessage=%WM_CLOSE    :   MsgHdlr(2).dwFnPtr=CodePtr(fnWndProc_OnClose)
    End Sub
    
    
    Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
      Local hWnd As Dword
      Local winclass As WndClassEx
      Local szAppName As Asciiz * 16
      Local Msg As tagMsg
    
      szAppName="Api Classes"
      Call AttachMessageHandlers()
      winclass.cbSize=SizeOf(winclass)
      winclass.style=%CS_HREDRAW Or %CS_VREDRAW
      winclass.lpfnWndProc=CodePtr(fnWndProc)
      winclass.cbClsExtra=0
      winclass.cbWndExtra=0
      winclass.hInstance=hIns
      winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
      winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
      winclass.hbrBackground=%COLOR_BTNFACE+1
      winclass.lpszMenuName=%NULL
      winclass.lpszClassName=VarPtr(szAppName)
      RegisterClassEx winclass
      hWnd=CreateWindow(szAppName,"Edit Class",%WS_OVERLAPPEDWINDOW,200,100,325,200,0,0,hIns,ByVal 0)
      Call ShowWindow(hWnd,iShow)
      While GetMessage(Msg,%NULL,0,0)
        TranslateMessage Msg
        DispatchMessage Msg
      Wend
    
      Function=msg.wParam
    End Function
    I hate the global though. However, that is the only way to so easily do it as in VB. I'll not have globals in my programs so I have to give that some thought. Properties or cbWndExtra bytes in one way or another should work.
    Fred
    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

  • #2
    Imo it's *very* easy to make a OOP style for window and control creation.
    But imo the ordinary way of coding will most likely result in smaller app's.

    A full blown object will always be present, normally you'll bring the few parts you need.

    When PB would introduce optimized static linking, then it may be interesting.
    Unless you want to create another 'VB' runtime?

    Borland uses inheritance, you may want to skip that and progam self-contained objects for each control type.
    This would keep the size low.
    hellobasic

    Comment


    • #3
      Creating a "window object engine" is a great idea to help people with the transition from VB. I estimate that a full blown engine with MDI support would probably be in the 200-250kb range. Specific control implementations would be more though. Heck, I might even create a library myself (I use procedural-style libraries at the moment)

      Anyone from the VB2PB project listening?
      kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

      Comment


      • #4
        Anyone from the VB2PB project listening?
        Are you asking in a roundabout way if you can help us out?
        Rod
        In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.

        Comment


        • #5
          I ever tried that and what frustrated me was a chicken and egg situation.
          A windowclass is a class by itself and is created on demand.
          Addtional oop stuff my need to set things after a control is created or before.
          When will what be done..?

          By itself a windowclass is fine though it lacks additional commands in oop style.
          Like Form1.Text1.Text = "Hello"

          It is possible to create an object reference right when the window is created and delete the reference when the window is getting destroyed.
          But then.. when to set the multiline style for an edit class?
          hellobasic

          Comment


          • #6
            I just updated my Wrapper. It's now a server.



            James

            Comment


            • #7
              I'm a little slow sometimes and have to actually see the code. Can't always think everything through to the end. I just added some to the code above and it just amonnts to piles of Get/Set property procedures instead of the very concise CreateWindowEx() call for each control. It certainly works though.

              The conclusion I'm coming too though is that the editors with code completion features that display all the Methods/Properties of an object when you hit the '.' have a lot to do with the popularity of OOP. That is certainly a seperate feature of an editor and not a feature of OOP, but there certainly has been a supportive role played by this editor technology in terms of the ascendence of OOP over these past 10 years. A comment Jose made to me sometime ago is certainly true, and that is that if you take away their code editors, some of these .NET programmers can't hardly write code.
              Fred
              "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

              Comment


              • #8
                True
                hellobasic

                Comment


                • #9
                  Originally posted by Kev Peel View Post
                  ...
                  Heck, I might even create a library myself (I use procedural-style libraries at the moment)

                  Anyone from the VB2PB project listening?

                  We're ALWAYS listening! Any chance you have a contribution in mind?

                  Stan
                  Do not go quiet into that good night,
                  ... Rage, rage against the dark.

                  Comment


                  • #10
                    The End.

                    Well, here's what I added to it and am talking about. I'm glad I finally did this and actually got to see how it works through. There is certainly no magic (other than what Mr. Zale does or how he does it with the compiler). It really does just add another layer that sits in between a direct function call and getting the control created. That would be most readily apparent if you would enter my "Control" class and just make a CreateWindow() Method, and simply put all the parameters from the basic Api CreateWindowEx() call in the CreateWindow() Method in the class, instead of having all those repetitous Property Get/Set Methods for setting each parameter like I have.

                    Nonetheless, perhaps there is some value to the approach in terms of the more complicated interfacing with a control in terms of SendMessage() calls and so on and so forth. These could be easily wrapped. But then so can they in regular procedural calls.

                    Personally, this may be as far as I want to take this. I most definitely am not interested in something like MFC where an entire app is wrapped within a class. I believe I'm going to stick with the basic Sdk style with my message cracker and routing routines as you see here. I'm beginning to believe that the merit of OOP is in designing classes around the objects for which an application is being built and leaving the actual Windows plumbing alone.

                    Code:
                    #Compile      Exe
                    #Include      "Win32api.inc"
                    %IDC_TEXT1    = 1201
                    %IDC_BUTTON1  = 1301
                    
                    Type WndEventArgs
                      wParam As Long
                      lParam As Long
                      hWnd   As Dword
                      hInst  As Dword
                    End Type
                    
                    
                    Type MessageHandler
                      wMessage As Long
                      dwFnPtr As Dword
                    End Type
                    
                    
                    Class CControl
                      Instance ExStyle              As Dword
                      Instance ClassName            As String
                      Instance WinStyle             As Dword
                      Instance x                    As Long
                      Instance y                    As Long
                      Instance Breadth              As Dword
                      Instance Height               As Dword
                      Instance hCtrl                As Dword
                      Instance hParent              As Dword
                      Instance hInstance            As Dword
                      Instance WindowCreationData   As Dword Ptr
                      Instance ControlID            As Long
                    
                      Interface IControl : Inherit IUnknown
                        Property Get ExStyle() As Dword               'Extended Style For CreateWindowEx()
                          Property=ExStyle
                        End Property
                        Property Set ExStyle(Byval dwExStyle As Dword)
                          ExStyle=dwExStyle
                        End Property
                    
                        Property Get ClassName() As String            'Class Name, i.e., edit, button, etc.
                          Property=ClassName
                        End Property
                        Property Set ClassName(Byval strClassName As String)
                          ClassName=strClassName
                        End Property
                    
                        Property Get Text() As String                 'Caption; Text Property of Edit Control
                          Local pszStr As Asciiz Ptr
                          Local iLen As Long
                          iLen=GetWindowTextLength(hCtrl)
                          Incr iLen
                          pszStr=GlobalAlloc(%GPTR,iLen)
                          Call GetWindowText(hCtrl,ByVal pszStr,iLen)
                          [email protected]
                          Call GlobalFree(pszStr)
                        End Property
                        Property Set Text(Byval strText As String)
                          Call SetWindowText(hCtrl,Byval Strptr(strText))
                        End Property
                    
                        Property Get WinStyle() As Dword              'Normal Window Style
                          Property=WinStyle
                        End Property
                        Property Set WinStyle(Byval dwWinStyle As Dword)
                          WinStyle=dwWinStyle
                        End Property
                    
                        Property Get x() As Long                      'x position of control
                          Property=x
                        End Property
                        Property Set x(Byval xCoord As Long)
                          x=xCoord
                        End Property
                    
                        Property Get y() As Long                      'y position of control
                          Property=y
                        End Property
                        Property Set y(Byval yCoord As Long)
                          y=yCoord
                        End Property
                    
                        Property Get Breadth() As Dword               'Width of control
                          Property=Breadth
                        End Property
                        Property Set Breadth(Byval dwWidth As Dword)
                          Breadth=dwWidth
                        End Property
                    
                        Property Get Height() As Dword                'Height of control
                          Property=Height
                        End Property
                        Property Set Height(Byval dwHeight As Dword)
                          Height=dwHeight
                        End Property
                    
                        Property Get hParent() As Dword               'Parent window of control
                          Property=hParent
                        End Property
                        Property Set hParent(Byval hParentWindow As Dword)
                          hParent=hParentWindow
                        End Property
                    
                        Property Get ControlID() As Long              'Control ID
                          Property=ControlID
                        End Property
                        Property Set ControlID(Byval iCtrlID As Long)
                          ControlID=iCtrlID
                        End Property
                    
                        Property Get WindowCreationData() As Dword    'Pointer goes here for custom controls, etc.
                          Property=WindowCreationData
                        End Property
                        Property Set WindowCreationData(Byval dwPtr As Dword Ptr)
                          WindowCreationData=dwPtr
                        End Property
                    
                        Method CreateWindow() As Dword                'Big create window call
                          hCtrl= _
                          CreateWindowEx _
                          ( _
                            ExStyle, _
                            Byval Strptr(ClassName), _
                            "", _
                            WinStyle, _
                            x, _
                            y, _
                            Breadth, _
                            Height, _
                            hParent, _
                            ControlID, _
                            GetModuleHandle(Byval %NULL), _
                            Byval WindowCreationData _
                          )
                          Method=hCtrl
                        End Method
                      End Interface
                    End Class
                    
                    
                    Declare Function FnPtr(wea As WndEventArgs) As Long
                    Global MsgHdlr()  As MessageHandler
                    Global txtTextBox As IControl
                    Global cmdButton  As IControl
                    
                    
                    Function fnWndProc_OnCreate(wea As WndEventArgs) As Long
                      'Setup Wrapped Edit Control
                      txtTextBox=Class "CControl"
                      txtTextBox.ClassName="edit"
                      txtTextBox.x=55                        : txtTextBox.y=40
                      txtTextBox.Breadth=210                 : txtTextBox.Height=25
                      txtTextBox.ExStyle=%WS_EX_CLIENTEDGE   : txtTextBox.WinStyle=%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER
                      txtTextBox.ControlID=%IDC_TEXT1        : txtTextBox.hParent=wea.hWnd
                      txtTextBox.WindowCreationData=%NULL
                      txtTextBox.CreateWindow()
                      txtTextBox.Text="Compile Without Compromise!"
                    
                      'Setup Wrapped Button Control
                      cmdButton=Class "CControl"
                      cmdButton.ClassName="button"
                      cmdButton.x=118                        : cmdButton.y=100
                      cmdButton.Breadth=80                   : cmdButton.Height=25
                      cmdButton.ExStyle=0                    : cmdButton.WinStyle=%WS_CHILD Or %WS_VISIBLE
                      cmdButton.ControlID=%IDC_BUTTON1       : cmdButton.hParent=wea.hWnd
                      cmdButton.WindowCreationData=%NULL
                      cmdButton.CreateWindow()
                      cmdButton.Text="Click Me!"
                    
                      fnWndProc_OnCreate=0
                    End Function
                    
                    
                    Function fnWndProc_OnCommand(wea As WndEventArgs) As Long
                      If Lowrd(wea.wParam)=%IDC_BUTTON1 And Hiwrd(wea.wParam)=%BN_CLICKED Then
                         MsgBox(txtTextBox.Text)
                      End If
                    
                      fnWndProc_OnCommand=0
                    End Function
                    
                    
                    Function fnWndProc_OnClose(wea As WndEventArgs) As Long
                      Call PostQuitMessage(0)
                      fnWndProc_OnClose=0
                    End Function
                    
                    
                    Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
                      Local wea As WndEventArgs
                      Register iReturn As Long
                      Register i As Long
                    
                      For i=0 To 2
                        If wMsg=MsgHdlr(i).wMessage Then
                           wea.hWnd=hWnd: wea.wParam=wParam: wea.lParam=lParam
                           Call Dword MsgHdlr(i).dwFnPtr Using FnPtr(wea) To iReturn
                           fnWndProc=iReturn
                           Exit Function
                        End If
                      Next i
                    
                      fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
                    End Function
                    
                    
                    Sub AttachMessageHandlers()
                      ReDim MsgHdlr(2) As MessageHandler  'Associate Windows Message With Message Handlers
                      MsgHdlr(0).wMessage=%WM_CREATE   :   MsgHdlr(0).dwFnPtr=CodePtr(fnWndProc_OnCreate)
                      MsgHdlr(1).wMessage=%WM_COMMAND  :   MsgHdlr(1).dwFnPtr=CodePtr(fnWndProc_OnCommand)
                      MsgHdlr(2).wMessage=%WM_CLOSE    :   MsgHdlr(2).dwFnPtr=CodePtr(fnWndProc_OnClose)
                    End Sub
                    
                    
                    Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
                      Local hWnd As Dword
                      Local winclass As WndClassEx
                      Local szAppName As Asciiz * 16
                      Local Msg As tagMsg
                    
                      szAppName="Api Classes"                                : Call AttachMessageHandlers()
                      winclass.lpszClassName=VarPtr(szAppName)               : winclass.lpfnWndProc=CodePtr(fnWndProc)
                      winclass.cbSize=SizeOf(winclass)                       : winclass.style=%CS_HREDRAW Or %CS_VREDRAW
                      winclass.cbClsExtra=0                                  : winclass.cbWndExtra=0
                      winclass.hInstance=hIns                                : winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
                      winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)   : winclass.hbrBackground=%COLOR_BTNFACE+1
                      winclass.lpszMenuName=%NULL
                      RegisterClassEx winclass
                      hWnd=CreateWindow(szAppName,"Control Objects",%WS_OVERLAPPEDWINDOW,200,100,325,200,0,0,hIns,ByVal 0)
                      Call ShowWindow(hWnd,iShow)
                      While GetMessage(Msg,%NULL,0,0)
                        TranslateMessage Msg
                        DispatchMessage Msg
                      Wend
                    
                      Function=msg.wParam
                    End Function
                    And the globals don't thrill me either.
                    Last edited by Fred Harris; 21 Aug 2008, 08:35 PM. Reason: Why have dumb static in WndProc()?
                    Fred
                    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                    Comment


                    • #11
                      We're ALWAYS listening! Any chance you have a contribution in mind?
                      Well I am seriously considering building a Window objects library OCX for free unlimited use. I've got so much code in libraries, hundreds of thousands of lines in fact, it would be nice to share it all by creating a series of object libraries, usable by PB (and other languages). No timeframe of course, but that's the general goal

                      Note: I must add I don't want to go down the 'open source' route, since 90% of the library code is also used in my commercial products and took years to amass.
                      kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                      Comment


                      • #12
                        Originally posted by Fred Harris View Post
                        I'm beginning to believe that the merit of OOP is in designing classes around the objects for which an application is being built and leaving the actual Windows plumbing alone.
                        Fwiw, when doing stuff in the C++ and Delphi worlds I've always found that to be the case. And a heckuva lot more efficient too.

                        Think about it - we've got DDT and all the other excellent 3rd party IDEs that make it easier to handle the plumbing. Why reinvent the wheel?

                        JS
                        John,
                        --------------------------------
                        John Strasser
                        Phone: 480 - 273 - 8798

                        Comment


                        • #13
                          Originally posted by Kev Peel View Post
                          ...
                          Note: I must add I don't want to go down the 'open source' route, since 90% of the library code is also used in my commercial products and took years to amass.
                          I certainly understand. I feel the same way about some of the apps I've written.

                          OTOH, if you have a 'free to use' library in mind, perhaps it would be worthwhile to give a vb2pb user the option of being able to use it without actually sharing the code. We could give the end-user the option of calling the library or using generically generated code. The nag screen would say something like "Kev Peel's Excellent OCX Library found. Do you want to generate code to use this library?" %MB_YESNOCANCEL.

                          We have discussed ways to encourage the use of PB. Availability of compatible tools by PB programmers is a big plus. After all, part of the original vb2pb concept is to promote the work of PB Programmers.
                          Do not go quiet into that good night,
                          ... Rage, rage against the dark.

                          Comment


                          • #14
                            Fred,
                            I have to disagree. You may be looking at this more as a C++ programmer which I also did at first. I am now looking at it for what it is: COM with PB implementation inheritance.

                            I tried the PBFC approach but now see it may not be the way to go.

                            Example. Base Class: cControl with all the basics (left,top,width.....). Next specific controls inheriting from cControl.List box has list, sort others...
                            Problem#1: not really a problem but code ugliness All references within Lbox to cControl need the Me. or MyBase. prefixes.
                            Problem#2: No Base class auto configuration. There is only one CLASS METHOD CREATE which is in the highest level derived class and this one cannot access "base class(es)" Instance variables. This is the main reason I did not got this route. I want default configurations so I don't have to manually call functions, sendmessages, every time I create a control.
                            Taking this to the height of laziness I wrapped the new DISPLAY OPENFILE so I don't have to set anything.
                            Just call the Objects GetFile Method and it defaults to *.* in the current directory.

                            At present I have one main cControl class. If I need specialization I use containment and create a specialized class within the cControl class and use compound objects to access. I do this with the RAGrid in my example. I also have all color,font,lists (for list box and combo box) built in. It keeps my callback cleaner. Speaking of which I love the CB.'s and probably will continue using a hybrid approach for the near future.

                            The server at present is under 60k but that includes RAGrid (not complete), and the complete pcre wrapper I posted in source before.

                            This is not meant to hijack just enlighten.

                            James

                            Comment

                            Working...
                            X