No announcement yet.

Sub-classing a listbox

  • Filter
  • Time
  • Show
Clear All
new posts

  • Sub-classing a listbox

    I have a form with three sets of columns which have to be filled
    out by the user, who has to select from menus (listboxes) what is
    to go into each. Since this is a rather full house screen, I
    create the same listbox and a couple of controls for each set of
    columns, select an item and then, after removing the subclassing
    of the listbox, kill the lot and recreate for the next column.
    The shape and size of these differ from column to column, but
    the same equates are used in each case. The code looks like this:

    If n&>20 Then n&=20
    Control Add Label, hDlg&, %TDcbgLbl, "" , 14, 14, 183, 265
    Control Add Frame, hDlg&, %TDccnFrm, dnm$, 18, 18, 175, n&+38
    Control Send hDlg&, %TDccnFrm, %WM_SETFONT, shFont&, 1
    Control Add ListBox, hDlg&, %TDccnLbx,, 30, 32, 152, n&, Style&
    Control Send hDlg&, %TDccnLbx, %WM_SETFONT, mnFont&, 1
    Control Handle hDlg&, %TDccnLbx To hCtl&
    CmnWndProc& = SetWindowLong(hCtl&, %GWL_WNDPROC, CodePtr(CcnMnuSubProc))

    Control Add Button, hDlg&, %TDccxBtn, "Exit", 78, n&+34, 50, 18
    Control Send hDlg&, %TDccxBtn, %WM_SETFONT, mnFont&, 1

    For i&=1 To jbs&
    Replace Chr$(179) With " " In t$
    If Val(Left$(t$,4))<10 Then t$=" "+t$
    ListBox Add hDlg&, %TDccnLbx, t$

    Control Disable hDlg&, %TDtdkBtn
    Control Disable hDlg&, %TDextBtn[code]

    This works very well the first time through, but when all
    colummns have been completed, and I go back to start a new line
    below the first, the listbox no longer responds to either the
    ENTER key nor double click as for the first time through.

    I find nothing in Petzold to explain this. Any help would be
    greatly appreciated!


  • #2
    Not sure I follow you here, but it seems like you use the same
    variable for all three? CmnWndProc will only keep the address to
    the window procedure for the last one created. Also seems like you
    don't un-sublass the previous ones properly? You need to do:

    IF CmnWndProc& THEN CALL SetWindowLong(hCtl&, %GWL_WNDPROC, CmnWndProc&) unsubclass it and set it back to use original window procedure,
    before using same variable for next control.

    When you subclass a control, return value of SetWindowLong is
    address of original window procedure. This value must be returned
    before exit, or whenever you feel it's time to do it. Otherwise the
    control will be lost in space. This value must be stored in Static
    or Global variable, if you ever leave function before using it for
    next control.



    • #3
      Borje, thanks for your input. The only control which is subclassed is
      the listbox, and that only to trap the ENTER key.

      I use CmnWndProc& each time to sub-class, and then un-subclass the
      listbox immediately the user has responded. As I mentioned, this works
      right across the first line of entry, which represents three create/
      subclass/select item/un-subclass occurrences. That completes the line
      of entry, and now I repeat this on the next line -- but now the listbox
      responds to everything EXCEPT double click and ENTER.



      • #4
        Generally you un-subclass during %WM_DESTROY, so it seems that maybe your existing code is triggering unexpectedly if you un-subclass for some other event.

        To be sure, either write to a log file from that event handler; or test that CmdWndProc& is non-zero before you un-subclass, and then immediately set CmnWndProc& to zero. This should ensure that you don't try to un-subclass with an invalid address.

        Other than this, we are only going to be guessing.

        Can you please post the rest of your code and we can give it the once over? Thanks!

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


        • #5
          Lance --
          Can you explain reasons to un-subclass during %WM_DESTROY ?

          E-MAIL: [email protected]


          • #6
            I've extracted the routines related to the problem, and pruned
            out irrelevant bit, but it still comes to 994 lines! Isn't that
            a bit big to post, Lance?



            • #7

              The "generally" accepted place to restore the windows procedure
              of a particular window is in the %WM_DESTROY handler of the
              subclass procedure. That's not set in concrete but it's the
              BEST place to do it because that's where you have access to
              the window handle. This is particularly useful when using MDI.
              Also, it's BEST place to do house cleaning of any objects that
              windows does not take care of.


              One suggestion, MODULAR PROGRAMMING!!! It's a necessity when
              you code to this size. Should be for all your programming




              • #8
                Thanks, everybody!

                Finally, changed the method to create the items only once and
                thereafter using SHOW and HIDE instead of KILL and recreate, and
                this works. That means the un-subclassing is under DESTROY now.

                Cecil, those 990 lines are part of one DLL module out of
                seveteen, plus one EXE, all of which add up to some 175,000
                lines. Before, when we were trying to use Delphi, and calling
                PbDLL modules from there, these were over 200,000 lines.

                How modular can it get? There is immense sub/function sharing and
                code reuse. The size of the thing does not necessarily imply
                sloppy code: If there is to be a lot of facilities at user end,
                a lot of programming is necessary to cater for it. Yet the whole
                app zips up on one and a half 1.4Mb diskettes.



                • #9
                  Cecil --
                  The "generally" accepted place To restore the windows procedure
                  of a particular window is In the %WM_DESTROY handler of the
                  subclass procedure.
                  Don't know like you, but I am too lazy.
                  "Play" with this. In my understaning, it's not necessary to "clean".

                     #Compile Exe
                     #Register None
                     #Dim All
                     #Include "WIN32API.INC"
                     %ID_TEXT = 201
                     %ID_RECREATE = 101
                     %ID_SUBCLASS = 102
                     Global gOldSubClassProc As Long
                     CallBack Function SubClassProc
                         SetWindowText GetParent(CbHndl), "CbMsg = &H" + Hex$(CbMsg) + " at " + Time$
                         Function = CallWindowProc(gOldSubClassProc, CbHndl, CbMsg, CbWparam, CbLparam)
                     End Function
                     CallBack Function DlgProc
                        Select Case CbMsg
                           Case %WM_INITDIALOG
                              Control Add Button,  CbHndl, %ID_RECREATE, "Re-create", 190, 10, 40, 14
                              Control Add Button,  CbHndl, %ID_SUBCLASS, "SubClass", 190, 25, 40, 14
                           Case %WM_COMMAND
                              If CbCtl = %ID_RECREATE Then
                                 If GetDlgItem (CbHndl, %ID_TEXT) Then Control Kill CbHndl, %ID_TEXT
                                    Control Add TextBox,  CbHndl, %ID_TEXT, "", 10, 30, 170, 130, _
                                    SetWindowText CbHndl, "hEdit = " + Hex$(GetDlgItem(CbHndl, %ID_TEXT))
                              ElseIf CbCtl = %ID_SUBCLASS Then
                                 If GetWindowLong(GetDlgItem(CbHndl, %ID_TEXT), %GWL_WNDPROC) = _
                                    CodePtr(SubClassProc) Then MsgBox "Already done" Else _
                                 gOldSubClassProc = SetWindowLong(GetDlgItem (CbHndl, %ID_TEXT), %GWL_WNDPROC, CodePtr(SubClassProc))
                             End If
                        End Select
                     End Function
                     Function PbMain
                        Local hDlg As Long
                        Dialog New 0, "Subclassed EDIT control with DDT Example",,, 240, 180, %DS_CENTER Or %WS_SYSMENU _
                        Dialog Show Modal hDlg Call DlgProc
                     End Function

                  E-MAIL: [email protected]


                  • #10

                    I wasn't implying that your code was sloppy. That was all I
                    had to work with at the time. Apparently you were talking about
                    total lines including subs/fns. My thinking at the time was the
                    opposite. True, a DLL is a module, but reference to modular
                    programming is containing reusable code in managable files
                    and just including them in the main module. Is that what you
                    mean by modular programming. It's not the DLL proper, but how
                    the DLL itself is modularized. Do you get the picture now? Glad
                    you got the code going!!


                    True, the code you present does not need house cleaning. My
                    reference above was to objects created that do not get destroyed
                    like fonts, pens, menus not tied to a window reference, etc. The
                    best place to take care of this is in %WM_DESTROY handler.
                    Again, this is not set in concrete, just the "generally"
                    accepted place to do it.




                    • #11
                      While your small example gets away without unsubclassing, you are deviating from intended methods of Windows app design.

                      MS and various 3rd-party authors (API guru's) all carefully point out that you should always unsubclass any subclassing you set up. Because this is usually done when a control or Window is closing, the %WM_DESTROY handler is ideal. Obviously other handlers could do this task too, but there is a greater chance of problems since %WM_DESTROY is unlikely to get called "accidentally", whereas, a handler like %WM_COMMAND could be triggered multiple times during a control or dialog's life.

                      One such example of dangerous behavior comes to mind: Say you try to explicitly unload a DLL and that DLL includes the subclassing procedure for a given control or window (that is still running). What do you think is likely to happen when the now-defunct subclass procedure is called? Allocate yourself one GPF point.

                      There are certain to be other situations that make unsubclassing essential too, and doing it correctly helps keep your code in a state that is easier to maintain in the future, and by other people.

                      Remember the golden API rule of Windows programming: "always clean up or destroy everything you create".

                      Finally, there has to be a better word than "unsubclassing" (it sounds terrible!)... anyone got any suggestions? Maybe we should call it "reclassing".

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


                      • #12
                        In keeping with the current crop of spy and intelligence/counter-intelligence movies and TV shows, how about DeClassify?

                        [email protected]
                        :) IRC :)


                        • #13
                          Cecil, (btw, no offence meant) the concept of modularised programming
                          or structured programming has been around for a while, and, indeed,
                          "comes with experience", almost. Chopping large routines into logical
                          sub-sections simplifies both development and maintenance, and this was
                          as true in the early days (IBM assembler, NCR's NEAT, and others) in
                          the early sixties of last century, as it is now. Long before the
                          advent of PC's, and when thermionic tubes/valves were used, before transistors and chips brought it all closer to nature.

                          Unfortunately I ended up with those 990 lines of code, in order to
                          include all the bits that interrelate, and this code, which is the main
                          part of a time docket entry task, has a multi-tier cost structure
                          to reference all along the line: this is what necessitates the several
                          list boxes - they represent part of a user-customized cost structure.
                          And my mysterious promblem could have been anywhere.

                          Lance, until there is a proper dictionary or at least a reasonably
                          authoritative glossary of terms, I reckon home-made words will persist.
                          "Un-subclassing" is an ugly thing, but is explicit; can't be mistaken.
                          "Declassing" or "reclassing" sounds much better, but does everyone
                          know what is intended? One might well wonder at how much agony would be
                          eliminated if everyone used - and knew - a common recognized