views:

4404

answers:

5

I need to allow the vertical scrollbar in a multiselect listbox (VB6) however, when the control is disabled, I can't scroll.

I would think there is an API to allow this, but my favorite VB6 site (MVPS VB.NET) does not have a way.

I toyed with pretending it was disabled, and ignore the clicks... but to do that with VB6 code is really ugly... so if this is a solution, I need an API to ignore the clicks.

Thanks for your help.

+1  A: 

Rather than looking for the API to ignore clicks, can't you just ignore the events? (i.e. just don't do when the user clicks/selects something).

And, I think there is a SelectionMode property to disable multiselect and make it single select.

If you don't want the user to be able to select anything at all, you can try hooking into the SelectionIndexChanged event and set the SelectionIndex to -1.

My VB6 is a bit rustly, so sorry if the event/property name don't match exactly.

Chetan Sastry
Yeah, I'm afraid 'not doing anything' isn't the answer, because VB changes the selection itself after the click event. You need to do extra work to not do anything... I just don't know what, yet.
Jason
A: 

This is a total VB hack, but I think you can leave the listbox enabled, and then drag a transparent label (with blank text) over all of the listbox except the scrollbar. The label will intercept any mouse clicks (although this won't affect keystrokes).

This will only work if the label is transparent like I remember (it may be the image control - without a loaded image - that works like this in VB).

MusiGenesis
I was really loving this idea, but it won't work with the label or image control. Neither of them can be put in front of the listbox in order to trap the click events.
Jason
Sorry, I could have sworn that was possible.
MusiGenesis
see my other answer
MusiGenesis
+1  A: 

Speaking of hacks, what if you enable the scrollbar when the mouse is moving over the scroll bar?

Or maybe ... place another scroll bar over the ListBox's SB, and use APIs to scoll the disabled LB.

GregUzelac
Any API that can help, would be appreciated. I don't know which ones to use. ;)
Jason
+1  A: 

Enabling just the scrollbar on a disabled listbox is possible (I think), but you'd have to dig into the Windows API and do SendMessage and some other grisly stuff.

I just checked it out, and when a listbox is disabled you can still scroll it up and down programatically by changing the control's ListIndex property. So you could do something like what greg suggests, and "float" an enabled vertical scroll bar over the one on the list box, and use this scrollbar's Value_Changed event (I think that's what it's called) to change the listbox's ListIndex property.

MusiGenesis
Thanks, I'll try that.
Jason
+3  A: 

I came up with the following code, which hides all of the gnarly details behind a class. Basically, I implemented greg's idea of using overlaying another scrollbar on top of the disabled list box's scrollbar. In my code, I dynamically create another ListBox control (resized so that only its scrollbar is visible), and use its scrollbar to scroll the actual ListBox. I also specifically avoided using the Windows API (except for the call to GetSystemMetrics that I used to figure how how wide a scroll bar is on the system). The nice thing about using another ListBox's scrollbar is that it will be themed properly (a ListBox uses the OS's theme when it displays it's scrollbar, but a VB.Scrollbar doesn't, so it would look out-of-place). Another advantage of using a second ListBox to scroll the first list box is that it's really easy to implement the scrolling logic (just set the first ListBox's TopIndex property to the second ListBox's TopIndex property whenever the second one is scrolled).

I also set it up to be as low-impact as possible (you only have to call a single function in your Form_Load event to make it work).

Usage

  1. Add CustomScrollingSupport.cls and ListBoxExtras.bas to your project.

  2. In your form's Form_Load event, add the following line:

    AddCustomListBoxScrolling Me

    This will make every VB.ListBox on the form support scrolling even while they are disabled. If you only want to add this functionality to a select number of ListBox's, you can call AddCustomScrollingSupport instead, passing in a specific ListBox control.

Interesting Note

In an older version of this code, I wasn't calling the ZOrder method on the second listbox (the one that provides the scrollbar) to make sure it would appear on top of the first listbox. This meant the second listbox was actually behind the first listbox; the interesting thing is that the scrolling on the second ListBox still worked when the first ListBox was disabled! Apparently, when the first ListBox is disabled, any mouse and keyboard events that would have gone to that ListBox "bleed through" to the second ListBox, so scrolling support still does work. I'm not sure if this is a bug or by design (I mean, you could argue that it makes sense that controls behind a disabled control would be able to receive events...). However, I found the scrolling to be slightly jerky at times, so I decided to add .ZOrder 0 to make the second listbox render on top of the first one. This has the drawback that you see the frame border for the second listbox (to the left of the scroll bar), which you wouldn't see if it was hidden behind the first listbox, but the scrolling is smoother.


CustomScrollingSupport.cls

This class wraps up the logic necessary to add "custom scrolling support" (for lack of a better name) to a VB.ListBox control. It should not be used directly, instead use the one of the Add* methods in the ListBoxExtras.bas module (I'll provide the code for that module later in the post).

Option Explicit

Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Const SM_CXVSCROLL = 2
Private Const SM_CXFRAME = 32

Private m_runningScrollers As Collection
Private WithEvents m_list As VB.listbox
Private WithEvents m_listScroller As VB.listbox

'--------------------------------------------------------------'
' Bind                                                         '
'                                                              '
'   Adds custom scrolling support to a ListBox control.        '
'   Specifically, it allows the ListBox to be                  '
'   scrolled even when it is disabled.                         '
'                                                              '
'   Parameters:                                                '
'                                                              '
'   + list                                                     '
'       the ListBox control to add custom scrolling support to '
'                                                              '
'   + runningScrollers                                         '
'       a Collection of CustomScrollingSupport objects. Passed '
'       in so that this object can remove itself from the list '
'       when it is terminated.                                 '
'                                                              '
'--------------------------------------------------------------'

Public Sub Bind(ByVal list As VB.listbox, runningScrollers As Collection)

    Set m_list = list
    Set m_runningScrollers = runningScrollers

    'Create another ListBox loaded with the same number of entries as the real listbox'
    Set m_listScroller = m_list.Container.Controls.Add("VB.ListBox", list.Name & "_scroller")
    LoadScrollerList

    Dim nScrollbarWidth As Long
    nScrollbarWidth = GetSystemMetricScaled(SM_CXVSCROLL, m_list) + _
                      GetSystemMetricScaled(SM_CXFRAME, m_list)

    'Display the other listbox (the "scroller"), just wide enough so that only its scrollbar is visible'
    'and place it over the real listboxs scroll bar'
    With m_listScroller
        .Left = m_list.Left + m_list.Width - nScrollbarWidth
        .Top = m_list.Top
        .Height = m_list.Height
        .Width = nScrollbarWidth
        .Enabled = True
        .Visible = True
        .ZOrder 0
    End With

End Sub

Private Sub m_listScroller_Scroll()
    'If the master list has changed, need to reload scrollers list'
    '(not ideal, but there is no ItemAdded event that we could use to keep the lists in sync)'
    If m_list.ListCount <> m_listScroller.ListCount Then
        LoadScrollerList
    End If

    'Make any scrolling done on the scroller listbox occur in the real listbox'
    m_list.TopIndex = m_listScroller.TopIndex

End Sub

Private Sub Class_Terminate()

    Dim scroller As CustomScrollingSupport
    Dim nCurrIndex As Long

    If m_runningScrollers Is Nothing Then
        Exit Sub
    End If

    'Remove ourselves from the list of running scrollers'

    For Each scroller In m_runningScrollers
        nCurrIndex = nCurrIndex + 1
        If scroller Is Me Then
            m_runningScrollers.Remove nCurrIndex
            Debug.Print m_runningScrollers.Count & " scrollers are running"
            Exit Sub
        End If
    Next

End Sub

Private Sub LoadScrollerList()

    Dim i As Long

    m_listScroller.Clear
    For i = 1 To m_list.ListCount
        m_listScroller.AddItem ""
    Next

End Sub

Private Function GetSystemMetricScaled(ByVal nIndex As Long, ByVal ctrl As Control)
    GetSystemMetricScaled = ctrl.Container.ScaleX(GetSystemMetrics(nIndex), vbPixels, ctrl.Container.ScaleMode)
End Function


ListBoxExtras.bas

This module contains two utility methods:

AddCustomScrollingSupport adds custom scrolling functionality to an individual VB.ListBox control

AddCustomListBoxScrolling adds custom scrolling functionality to every VB.ListBox control on a given Form

Option Explicit

Public Sub AddCustomScrollingSupport(ByVal list As VB.listbox)

    Static runningScrollers As New Collection

    Dim newScroller As CustomScrollingSupport
    Set newScroller = New CustomScrollingSupport

    runningScrollers.Add newScroller
    newScroller.Bind list, runningScrollers

End Sub

Public Sub AddCustomListBoxScrolling(ByVal frm As Form)

    Dim ctrl As Control
    For Each ctrl In frm.Controls

        If TypeOf ctrl Is VB.listbox Then
            AddCustomScrollingSupport ctrl
        End If

    Next

End Sub
Mike Spross
Worked perfectly, thank you.
Jason