How to Dynamically set the Combo Drop Down List Width

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • ADezii
    Recognized Expert Expert
    • Apr 2006
    • 8834

    How to Dynamically set the Combo Drop Down List Width

    This week’s Tip of the Week will clearly demonstrate how you can dynamically set the Drop Down List Width of a Combo Box to the length of the longest item in its Row Source. The inspiration for this Tip came from one of our own resident Experts, mshmyob. In response to a Thread relating to this very Topic, mshmyob came up with a rather ingenious method to accomplish this task. He computed the Average Character Width of a String consisting of Upper and Lower case characters as well as the numbers 0 to 9, then applied this logic to determine the List Width of the Combo Box. This he did by calculating the length of the longest entry in the Combo Box, determined the current Font Name and Size, utilizing the TextWidth Method of a Report opened in Hidden Mode, computing the Average Character Width, then transferring this value back to the Form (to a Text Box), where the ListWidth of the Combo Box can now be calculated to accommodate the longest entry. What makes this approach unique is that:
    • This is all accomplished internally within Access with no external References or API Calls.
    • Regardless of the Font Size and Name, the code will adjust and always produce the desired results.
    • Since Forms do not have a TextWidth Method, an alternative method had to be realized in order to solve this dilemma. mshmyob’s use of the TextWidth Method within a Hidden Report, was not at all intuitive, and in fact was very clever.

    I’ll post the code below for your preview. It is amply commented, even bordering on overkill, but it should give you a good indication of exactly what is happening. These code modifications have been made with mshmyob’s approval, but keep in mind that the original concept was his, not mind. Even with the varied comments, and what I feel is a sound and logical code based approach, I have made this Tip available as an Attachment. I strongly suggest that you download it and get a visual indication of how this functionality is actually implemented. Should you have any questions whatsoever, please feel free to ask them. I’ll be monitoring this Thread as I’m sure mshmyob will be also.
    Code:
    Private Sub Form_Open(Cancel As Integer)
    Dim lngNumberOfEntries As Long
    Dim lngLongestRowlength As Long
    Dim dblLargestCboEntryWidth As Double
    
    Call IntroDialog
    
    'gcboCtl is a Global Object Variable of Type Combo Box
    'Initialize it now
    Set gcboCtl = Me![cboAdjust]
    
    lngNumberOfEntries = gcboCtl.ListCount
    
    If lngNumberOfEntries = 0 Then Exit Sub     'No entries, Bye-Bye!
    
    'Retrieve the longest Row of data from the Combo Box via the Function, pass
    'to the Function the number of entries in the Combo Box
    lngLongestRowlength = fGetLongestRowLength(lngNumberOfEntries)
    
    'Determine Font Size and Font Name in use and Save these values in Global
    'Variables instead of passing Arguments to the Report, then retrieving them
    gintFontSize = gcboCtl.FontSize
    gstrFontName = gcboCtl.FontName
    
    'Open the Report, but in Hidden Mode so you can apply Parameters to it such as
    'FontSize and FontName. We need to use a Report because Access's TextWidth
    'Method only works in a Report, and not a Form
    DoCmd.OpenReport "rptAvgString", acViewPreview, , , acHidden
    
    'txtAvgValue holds the value passed back from the Report and is the Average
    'Character Width based on the pre-determined String (strMessage) as defined
    'in the Print() Event of the Report. You can hide the txtAvgValue Text Box,
    'I just used this to show you how it works. Multiply the Average Character
    'Width by the longest value in the Combo Box to arrive at the Length of the
    'longest entry in Twips.
    dblLargestCboEntryWidth = Me.txtAvgValue * lngLongestRowlength
    
    'Adjust the Drop Down List Width of the Combo Box
    gcboCtl.ListWidth = dblLargestCboEntryWidth
      
    'Close the 'Hidden' Report
    DoCmd.Close acReport, "rptAvgString", acSaveNo
    
    Call FillDescription    'Program Description in Form Label
    End Sub
    Code:
    Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
    Dim rpt As Report
    Dim strMessage As String
    Dim dblAvgSize As Double
    Const conTWIPS As Byte = 1
        
    Set rpt = Me
        
    'Create a string with all upper and lowercase characters, This process will allow
    'the creation of an'Average Characer Width in Twips given the Font's Name and Size.
    strMessage = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    
    With rpt
      'Set ScaleMode to Twips, and set FontName and FontSize properties.
      'Character size is based on Font Size and Font Name - set as Twips not Pixels
      .ScaleMode = conTWIPS
      .FontName = gstrFontName
      .FontSize = gintFontSize
    End With
    
    'Use the TextWidth method to determine the Horizontal Width - Vertical is unimportant.
    'The value returned by the TextWidth method is expressed in terms of the coordinate system
    'in effect for the Report, as defined by the Scale Method
    dblAvgSize = rpt.TextWidth(strMessage) / 62
    
    'Pass this number back to the calling form to calculate length needed
    Forms!frmListWidth.txtAvgValue = dblAvgSize
    End Sub
  • FishVal
    Recognized Expert Specialist
    • Jun 2007
    • 2656

    #2
    Nice work.

    Though I'm very unsure it is an optimal approach at all.
    IMHO Win API functions (particularly those from GDI32 and User32) having an exclusive responsibility on rendering graphics might have a "last word" in determining of expecting results.

    Here is what simple googling give.

    Kind regards,
    Fish

    P.S. Cheers to the developers anyway.

    Comment

    • ADezii
      Recognized Expert Expert
      • Apr 2006
      • 8834

      #3
      Originally posted by FishVal
      Nice work.

      Though I'm very unsure it is an optimal approach at all.
      IMHO Win API functions (particularly those from GDI32 and User32) having an exclusive responsibility on rendering graphics might have a "last word" in determining of expecting results.

      Here is what simple googling give.

      Kind regards,
      Fish

      P.S. Cheers to the developers anyway.
      Great point FishVal, but I think the beauty of mshmyob's approach is that everything is handled internally within Access, no API Declarations and implementations , no Type...End Type Declarations, no Multiple Constant Declarations, etc. I also agree that the API has the 'last word' in rendering Graphics, but I am a KISS Principal Advocate, and although the API offers limitless functionality, it can just as easily cause Fatal Errors. I'm sure I will cause a stir with these Comments, let's just wait and see what other, varied, opinions may be.

      Comment

      • FishVal
        Recognized Expert Specialist
        • Jun 2007
        • 2656

        #4
        And you are right ADezii. ;)

        Though I still stay on my own in the following:
        • The best way to prevent Fatal Errors is to keep computer switched off.
        • The most frequent reason for Fatal Errors is users noobness.
        • Access native objects could easily crash too.
        • Lack of Combobox.TextWi dth or even Combobox.AutoFi t properties is a shame of M$ developers.
        • Report.TextWidt h property is sure to use API, otherwise it will not work at all or will work in a way making happy nobody.
        • Using API certainly requires more code. More code - more bugs. Some DLL errors could not be trapped with VBA code - really it is still considered to be a macrolaunguage.
        • But as soon as the code has been debugged properly it is expected too be more efficient that may be crucial when processing multiple strings in batch.


        And the last but not the least - mshmyob's algorithm has a flaw. I've added string consisting of dots and the code failes to fit list width. Assuming a string with maximal character count to be the longest string when rendered is not a proof way as well as calculating symbol average width on alphabet - it should be calculated at least on more representative collection reflecting character frequency.

        Regards,
        Fish

        P.S. BTW what is being calculated when dividing twips count by 62?

        Comment

        • mshmyob
          Recognized Expert Contributor
          • Jan 2008
          • 903

          #5
          Hello Fish,

          Your point about the algorithm failing when a field is populated with say all periods - I agree will fail. I thought of that but under normal conditions nobody will fill the field with all non alphanumeric data. If that is the case then a slight change to the algorithim would be needed.

          The divide by 62 is to divide the string that consists of the upper and lower case letters and numbers 0-9. There are 62 characters if you count them (26 upper, 26 lower, 10 numbers) Just getting a rough average character width based on those characters going into the table. You could add non alpahnumeric characters if you wish.

          I think this method works pretty close for almost all situations and just shows how with some slight changes you could accomodate someone entering all periods.

          Switching off computer now to avoid fatal errors :).

          cheers,

          Originally posted by FishVal
          And you are right ADezii. ;)

          Though I still stay on my own in the following:
          • The best way to prevent Fatal Errors is to keep computer switched off.
          • The most frequent reason for Fatal Errors is users noobness.
          • Access native objects could easily crash too.
          • Lack of Combobox.TextWi dth or even Combobox.AutoFi t properties is a shame of M$ developers.
          • Report.TextWidt h property is sure to use API, otherwise it will not work at all or will work in a way making happy nobody.
          • Using API certainly requires more code. More code - more bugs. Some DLL errors could not be trapped with VBA code - really it is still considered to be a macrolaunguage.
          • But as soon as the code has been debugged properly it is expected too be more efficient that may be crucial when processing multiple strings in batch.


          And the last but not the least - mshmyob's algorithm has a flaw. I've added string consisting of dots and the code failes to fit list width. Assuming a string with maximal character count to be the longest string when rendered is not a proof way as well as calculating symbol average width on alphabet - it should be calculated at least on more representative collection reflecting character frequency.

          Regards,
          Fish

          P.S. BTW what is being calculated when dividing twips count by 62?

          Comment

          • ADezii
            Recognized Expert Expert
            • Apr 2006
            • 8834

            #6
            Originally posted by FishVal
            And you are right ADezii. ;)

            Though I still stay on my own in the following:
            • The best way to prevent Fatal Errors is to keep computer switched off.
            • The most frequent reason for Fatal Errors is users noobness.
            • Access native objects could easily crash too.
            • Lack of Combobox.TextWi dth or even Combobox.AutoFi t properties is a shame of M$ developers.
            • Report.TextWidt h property is sure to use API, otherwise it will not work at all or will work in a way making happy nobody.
            • Using API certainly requires more code. More code - more bugs. Some DLL errors could not be trapped with VBA code - really it is still considered to be a macrolaunguage.
            • But as soon as the code has been debugged properly it is expected too be more efficient that may be crucial when processing multiple strings in batch.


            And the last but not the least - mshmyob's algorithm has a flaw. I've added string consisting of dots and the code failes to fit list width. Assuming a string with maximal character count to be the longest string when rendered is not a proof way as well as calculating symbol average width on alphabet - it should be calculated at least on more representative collection reflecting character frequency.

            Regards,
            Fish

            P.S. BTW what is being calculated when dividing twips count by 62?
            Hello again, FishVal. As always, your points are well taken and always respected. It is true that the TextWidth() Method must, of course, access the API in some manner, but the hugh difference is that the functionality is encapsulated within the Method which has thoroughly been debugged and ultra efficient. It is, in my mind, a major step to mimic this functionality and access the API directly. Would you agree?

            You are also correct about the flaw but as stated by mshmyob, logic can easily be incorporated into the code that will offset this, namely the occurrences of periods, or other similar characters in the String. A really nice pick up, I would never have thought of this!

            P.S. - If I ever get the time, I will run Benchmark Tests comparing msymyob's approach, to that which you have demonstrated, on very long Combo Box RowSources. Care to bet who the winner will be?
            Code:
            msymyob vs. The API       'seems like an old Godzilla Movie! (LOL).

            Comment

            • FishVal
              Recognized Expert Specialist
              • Jun 2007
              • 2656

              #7
              Hello.

              I hope you don't take my critisism personally. ;)
              I intend to help you make the code better.

              So I'd like to add a few more points:
              • A mistic 62 divisor is now clear for me. ;)
              • Flat alphabetic list IMHO does not give statistically relevant results as for symbol average width. Letter frequency should be taken into account to enhance result relevancy. Though all statistical tricks work better the larger collection is taken. ;)
              • Though Report.TextWidt h takes into account CR/LF in text, your code still calculates full string width.
              • Again, a text with maximal symbol count is not the longest text when rendered.


              Single bricks are quite good, but the whole building is not enough strong, IMHO.
              I would recommend you to rethink the whole logic and issue the next version ;).

              Regards,
              Fish

              Comment

              • FishVal
                Recognized Expert Specialist
                • Jun 2007
                • 2656

                #8
                Originally posted by ADezii
                Hello again, FishVal. As always, your points are well taken and always respected. It is true that the TextWidth() Method must, of course, access the API in some manner, but the hugh difference is that the functionality is encapsulated within the Method which has thoroughly been debugged and ultra efficient. It is, in my mind, a major step to mimic this functionality and access the API directly. Would you agree?
                He-he.

                4 years ago I've bought Kodak Z780 soapbox and was quite happy.
                Now I could hardly agree with all its "friendly automatics" and dream about camera with at least hand-focus available. :D

                I was one from that kids with daemonic fire in eyes coding in asm.

                But these happy days were years ago when I was student with no money, no home, no wife and no children. Now I prefer to do less and to get more to afford myself at least a half of that fun I had. :D

                Regards,
                Fish

                Comment

                • FishVal
                  Recognized Expert Specialist
                  • Jun 2007
                  • 2656

                  #9
                  Ok, gentlemen.

                  Some more points:
                  • I think these stuff could be nicely implemented as class built over Access.ComboBox class.
                  • User may be allower to adjust list width manually with an option to store user changes - some kind of design mode emulation.
                  • Too long entries may be displayed separately in some kind of "status line" or popup.


                  Hope these point are not drastically off topic.

                  Regards,
                  Fish

                  Comment

                  • Stewart Ross
                    Recognized Expert Moderator Specialist
                    • Feb 2008
                    • 2545

                    #10
                    Hi All. This has been a very interesting discussion, but perhaps at this stage we should simply recognise that mshmyobs' method is a very good starter for achieving something not done at all using native Access facilities.

                    As ADezii pointed out, mshmyob's solution is entirely self contained. Fish is right about windows API calls being better/more efficient/more general. But, the wrappers for API code are hardly easy for users to read and understand when trying to work out what the code does - unless time and trouble has been taken to translate low-level numeric values into human-readable constants and so on. And misplaced or missing dlls can break the code anyway. At least in native VBA it is easy enough to work out what is going on...

                    Thank you to mshmyob for the work that has gone into this, and to ADezii for posting the thread for all to read. Thanks also to Fishval for reviewing the code and suggesting potential improvements.

                    Ahh, the lure of writing perfect code - I might one day get there, but I haven't been able to achieve this yet despite more than 25 years of trying...

                    Cheers

                    Stewart
                    Last edited by Stewart Ross; Aug 18 '08, 04:27 PM. Reason: correcting how long I've been writing code... LOL!

                    Comment

                    • mshmyob
                      Recognized Expert Contributor
                      • Jan 2008
                      • 903

                      #11
                      LOL @ corrected years of perfect code. Combined we have 50 years and M$ must have a billion and they aren't perfect yet either.

                      cheers,

                      Originally posted by Stewart Ross Inverness
                      .... Ahh, the lure of writing perfect code - I might one day get there, but I haven't been able to achieve this yet despite more than 25 years of trying...

                      .....
                      Stewart

                      Comment

                      • nico5038
                        Recognized Expert Specialist
                        • Nov 2006
                        • 3080

                        #12
                        Just my $0.02 "contributi on", why would I need this?
                        Problem with this solution is the fact that there can be another control on the right of the combo box. . . . .

                        I prefer to control my form layout in the design phase, the option to widen the expanded combobox automatically will be sufficient for me.

                        For solving this problem I would probably would have tried to launch a datasheet sub form with the longest field first and, after selecting the column, apply the resize method to be able to read the field width Access calculated for me...

                        Nic;o)

                        Comment

                        • ADezii
                          Recognized Expert Expert
                          • Apr 2006
                          • 8834

                          #13
                          Originally posted by nico5038
                          Just my $0.02 "contributi on", why would I need this?
                          Problem with this solution is the fact that there can be another control on the right of the combo box. . . . .

                          I prefer to control my form layout in the design phase, the option to widen the expanded combobox automatically will be sufficient for me.

                          For solving this problem I would probably would have tried to launch a datasheet sub form with the longest field first and, after selecting the column, apply the resize method to be able to read the field width Access calculated for me...

                          Nic;o)
                          Problem with this solution is the fact that there can be another control on the right of the combo box. . . . .
                          That's what I thought was one of the benefits of this code: only the Drop Down List Width becomes dynamically re-sized, not the Text Box Component. No Controls to the right of the Combo would be affected. Controls may be overshadowed by the Drop Down, but to me that is irrelevant. Done rambling, thanks for joining in, Nico.

                          Comment

                          • youmike
                            New Member
                            • Mar 2008
                            • 69

                            #14
                            I've followed this thread with huge interest and congratulate those who have produced really elegant code BUT I'm still in the KISS camp when it comes to the usefulness of variable length drop-down lists. I think we have to recognise that the drop-down list isn't a permanent hog of screen real estate and use this feature with caution.

                            How many of you have (like me) pored over code written months ago and wondered what on earth you originally meant it to do? And I'll bet you've had to do this at 3am! I know I have, and I curse myself for dreaming up cute little twirls which really were never necessary.

                            Enough said. I've downloaded the zip file and I know I'll use it!!

                            Comment

                            • FishVal
                              Recognized Expert Specialist
                              • Jun 2007
                              • 2656

                              #15
                              Hello, gentlemen.

                              As soon as nobody comes with this obvious question I will ask it. :)
                              What for is that average symbol width calculation at all?
                              Exactly the same with higher precision and for less money may be done by calculation screen width of the longest list item directly.

                              IMHO it is not perfect too, but at least more reliable.

                              Regards,
                              Fish

                              Comment

                              Working...