我需要在多选列表框(VB6)中允许垂直滚动条,但是,当禁用该控件时,我无法滚动。
我认为有一个API允许这样做,但我最喜欢的VB6 site (MVPS VB.NET)没有办法。
我玩弄假装它被禁用了,忽略了点击......但是用VB6代码做这件事真是太丑了......所以如果这是一个解决方案,我需要一个API来忽略点击。
感谢您的帮助。
答案 0 :(得分:4)
我提出了以下代码,它隐藏了类背后的所有细节。基本上,我实现了greg的想法,即使用覆盖禁用列表框滚动条顶部的另一个滚动条。在我的代码中,我动态创建另一个ListBox控件(调整大小以便只显示其滚动条),并使用其滚动条滚动实际的ListBox。我还特别避免使用Windows API(除了调用GetSystemMetrics
之外,我曾经用它来计算滚动条在系统上的宽度)。使用另一个ListBox的滚动条的好处是它将正确主题(ListBox在显示它的滚动条时使用OS的主题,但是VB.Scrollbar没有,因此它看起来不合适)。使用第二个ListBox滚动第一个列表框的另一个好处是,实现滚动逻辑非常容易(只要滚动第二个ListBox的TopIndex属性,只需将第一个ListBox的TopIndex属性设置为第二个ListBox的TopIndex属性)。
我还将其设置为尽可能低影响(您只需在Form_Load
事件中调用单个函数即可使其正常工作。)
将CustomScrollingSupport.cls
和ListBoxExtras.bas
添加到您的项目中。
在表单的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
答案 1 :(得分:1)
不是要寻找忽略点击的API,你不能忽略这些事件吗? (即当用户点击/选择某些内容时不要这样做。)
并且,我认为有一个SelectionMode
属性可以禁用多选,并使其单选。
如果您不希望用户能够选择任何内容,您可以尝试挂钩SelectionIndexChanged
事件并将SelectionIndex
设置为-1。
我的VB6有点生锈,很抱歉,如果事件/属性名称不完全匹配。
答案 2 :(得分:1)
说到黑客攻击,当鼠标在滚动条上移动时启用滚动条怎么办?
或者可能......在ListBox的SB上放置另一个滚动条,并使用API来屏蔽已禁用的LB.
答案 3 :(得分:1)
只允许启用禁用列表框上的滚动条(我认为),但您必须深入了解Windows API并执行SendMessage和其他一些可怕的内容。
我刚检查过,当列表框被禁用时,您仍然可以通过更改控件的ListIndex属性以编程方式上下滚动它。所以你可以做一些像greg建议的东西,并在列表框上的那个“浮动”一个启用的垂直滚动条,并使用这个滚动条的Value_Changed事件(我认为这就是所谓的)来改变列表框的ListIndex属性。
答案 4 :(得分:0)
这是一个完整的VB hack,但我认为你可以启用列表框,然后在除滚动条之外的所有列表框上拖动透明标签(带有空白文本)。标签将拦截任何鼠标点击(虽然这不会影响击键)。
只有当标签像我记得的那样透明时才会有效(它可能是图像控件 - 没有加载的图像 - 在VB中就像这样)。