2

我需要在多选列表框(VB6)中允许垂直滚动条但是,当控件被禁用时,我无法滚动。

我认为有一个 API 可以实现这一点,但我最喜欢的VB6 站点(MVPS VB.NET)没有办法。

我玩弄假装它被禁用,并忽略点击......但是用VB6代码做到这一点真的很丑......所以如果这是一个解决方案,我需要一个API来忽略点击。

谢谢你的帮助。

4

5 回答 5

4

我想出了下面的代码,它隐藏了一个类后面的所有细节。基本上,我实现了 greg 的想法,即在禁用列表框的滚动条之上使用覆盖另一个滚动条。在我的代码中,我动态地创建了另一个 ListBox 控件(调整大小以便只有它的滚动条可见),并使用它的滚动条来滚动实际的 ListBox。我还特别避免使用 Windows API(除了调用GetSystemMetrics我曾经计算过滚动条在系统上的宽度)。使用另一个 ListBox 的滚动条的好处是它可以正确设置主题(ListBox 在显示它的滚动条时使用操作系统的主题,但 VB.Scrollbar 没有,所以它看起来不合适)。使用第二个 ListBox 滚动第一个列表框的另一个优点是实现滚动逻辑非常容易(只要滚动第二个 ListBox,只需将第一个 ListBox 的 TopIndex 属性设置为第二个 ListBox 的 TopIndex 属性)。

我还将它设置为尽可能低影响(您只需在Form_Load事件中调用一个函数即可使其工作)。

用法

  1. 添加CustomScrollingSupport.clsListBoxExtras.bas到您的项目。

  2. 在表单的Form_Load事件中,添加以下行:

    AddCustomListBoxScrolling Me

    这将使表单上的每个 VB.ListBox 都支持滚动,即使它们被禁用。如果您只想将此功能添加到选定数量的 ListBox 中,则可以AddCustomScrollingSupport改为调用,传入特定的 ListBox 控件。

有趣的笔记

在此代码的旧版本中,我没有调用ZOrder第二个列表框(提供滚动条的那个)上的方法来确保它出现在第一个列表框的顶部。这意味着第二个列表框实际上在第一个列表框之后;有趣的是,当第一个 ListBox 被禁用时,第二个 ListBox 上的滚动仍然有效!显然,当第一个 ListBox 被禁用时,任何会进入该 ListBox 的鼠标和键盘事件都会“渗透”到第二个 ListBox,因此滚动支持仍然有效。我不确定这是错误还是设计使然(我的意思是,您可能会争辩说禁用控件后面的控件能够接收事件是有道理的……)。但是,我发现滚动有时有点生涩,所以我决定添加.ZOrder 0使第二个列表框呈现在第一个列表框之上。这样做的缺点是您会看到第二个列表框(滚动条左侧)的框架边框,如果它隐藏在第一个列表框后面,您不会看到它,但滚动更顺畅。


CustomScrollingSupport.cls

此类封装了向VB.ListBox控件添加“自定义滚动支持”(因为没有更好的名称)所需的逻辑。它不应该直接使用,而是使用模块Add*中的一种方法ListBoxExtras.bas(我将在后面的帖子中提供该模块的代码)。

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

该模块包含两个实用方法:

AddCustomScrollingSupport向单个VB.ListBox 控件

AddCustomListBoxScrolling添加自定义滚动功能向VB.ListBox 给定的每个控件添加自定义滚动功能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
于 2008-11-17T00:06:12.570 回答
1

与其寻找忽略点击的 API,不如就忽略事件?(即当用户单击/选择某些东西时不要这样做)。

而且,我认为有一个SelectionMode属性可以禁用多选并使其成为单选。

如果您根本不希望用户能够选择任何内容,您可以尝试挂钩SelectionIndexChanged事件并将其设置SelectionIndex为 -1。

我的 VB6 有点生疏,如果事件/属性名称不完全匹配,请见谅。

于 2008-11-15T21:25:55.280 回答
1

说到黑客,如果当鼠标在滚动条上移动时启用滚动条会怎样?

或者也许......在 ListBox 的 SB 上放置另一个滚动条,并使用 API 来滚动禁用的 LB。

于 2008-11-15T23:01:32.123 回答
1

仅在禁用的列表框上启用滚动条是可能的(我认为),但是您必须深入研究 Windows API 并执行 SendMessage 和其他一些可怕的事情。

我刚刚检查了它,当列表框被禁用时,您仍然可以通过更改控件的 ListIndex 属性以编程方式上下滚动它。因此,您可以执行 greg 建议的操作,并将启用的垂直滚动条“浮动”在列表框上的垂直滚动条上,并使用此滚动条的 Value_Changed 事件(我认为这就是它的名称)来更改列表框的 ListIndex 属性。

于 2008-11-16T16:23:53.860 回答
0

这是一个完整的 VB hack,但我认为您可以启用列表框,然后在除滚动条之外的所有列表框上拖动一个透明标签(带有空白文本)。标签将拦截任何鼠标点击(尽管这不会影响击键)。

这只有在标签像我记得的那样透明时才有效(它可能是图像控件 - 没有加载的图像 - 在 VB 中像这样工作)。

于 2008-11-15T22:42:46.937 回答