Winapi 禁用列表框时允许在列表框中垂直滚动(VB6)
我需要在multiselect列表框(VB6)中允许垂直滚动条,但是,当控件被禁用时,我无法滚动 我认为有一个API可以实现这一点,但我最喜欢的API没有办法 我假装它被禁用了,并忽略了点击。。。但是用VB6代码来做这件事真的很难看。。。因此,如果这是一个解决方案,我需要一个API来忽略点击Winapi 禁用列表框时允许在列表框中垂直滚动(VB6),winapi,vb6,listbox,Winapi,Vb6,Listbox,我需要在multiselect列表框(VB6)中允许垂直滚动条,但是,当控件被禁用时,我无法滚动 我认为有一个API可以实现这一点,但我最喜欢的API没有办法 我假装它被禁用了,并忽略了点击。。。但是用VB6代码来做这件事真的很难看。。。因此,如果这是一个解决方案,我需要一个API来忽略点击 感谢您的帮助。与其寻找API来忽略点击,不如直接忽略事件?(即,当用户单击/选择某个内容时,不要这样做) 而且,我认为有一个SelectionMode属性可以禁用multiselect并使其成为单选 如果您
感谢您的帮助。与其寻找API来忽略点击,不如直接忽略事件?(即,当用户单击/选择某个内容时,不要这样做) 而且,我认为有一个
SelectionMode
属性可以禁用multiselect并使其成为单选
如果您不希望用户能够选择任何内容,您可以尝试挂接到SelectionIndexChanged
事件,并将SelectionIndex
设置为-1
我的VB6有点生锈,如果事件/属性名称不完全匹配,那么很抱歉。这是一个完全的VB攻击,但我认为您可以保持列表框处于启用状态,然后在除滚动条之外的所有列表框上拖动一个透明标签(带有空白文本)。标签将拦截任何鼠标点击(尽管这不会影响击键)
这只在标签像我记得的那样透明的情况下才起作用(可能是图像控件,没有加载的图像,在VB中就是这样工作的)。说到黑客,如果当鼠标在滚动条上移动时启用滚动条会怎么样
或者也许。。。在列表框的SB上放置另一个滚动条,并使用API来查找禁用的LB。在禁用的列表框上只启用滚动条是可能的(我认为),但您必须深入Windows API并执行SendMessage和其他一些可怕的操作
我刚刚签出它,当一个列表框被禁用时,您仍然可以通过更改控件的ListIndex属性,通过编程方式上下滚动它。因此,您可以按照greg的建议,在列表框上的垂直滚动条上“浮动”一个已启用的垂直滚动条,并使用此滚动条的Value_Changed事件(我想这就是它的名称)来更改列表框的ListIndex属性。我提出了以下代码,它隐藏了类后面的所有粗糙细节。基本上,我实现了greg的想法,即在禁用列表框的滚动条上覆盖另一个滚动条。在我的代码中,我动态地创建了另一个ListBox控件(调整大小以便只有它的滚动条可见),并使用它的滚动条滚动实际的ListBox。我还特别避免使用Windows API(除了调用
GetSystemMetrics
,我用来计算滚动条在系统上的宽度)。使用另一个ListBox的滚动条的好处是,它将正确地设置主题(ListBox在显示其滚动条时使用操作系统的主题,但是VB.scrollbar不使用,因此看起来不合适)。使用第二个列表框滚动第一个列表框的另一个优点是,实现滚动逻辑非常容易(只要在滚动第二个列表框时将第一个列表框的TopIndex属性设置为第二个列表框的TopIndex属性即可)
我还将其设置为尽可能低的影响(您只需在表单\u Load
事件中调用单个函数即可使其工作)
用法
CustomScrollingSupport.cls
和ListBoxExtras.bas
添加到项目中form\u Load
事件中,添加以下行:
addCustomListBoxScrollingMe
这将使表单上的每个VB.ListBox都支持滚动,即使它们被禁用。如果只想将此功能添加到选定数量的列表框中,则可以调用AddCustomScrollingSupport
,传入特定的列表框控件ZOrder
方法来确保它显示在第一个列表框的顶部。这意味着第二个列表框实际上在第一个列表框后面;有趣的是,当第一个列表框被禁用时,第二个列表框上的滚动仍然有效!显然,当第一个列表框被禁用时,任何可能进入该列表框的鼠标和键盘事件都会“溢出”到第二个列表框,因此滚动支持仍然有效。我不确定这是一个bug还是设计(我的意思是,你可能会说禁用控件后的控件能够接收事件是有意义的…)。但是,我发现滚动有时有点不稳定,所以我决定添加.ZOrder 0
,使第二个列表框呈现在第一个列表框的顶部。这有一个缺点,即您可以看到第二个列表框的框架边框(在滚动条的左侧),您无法看到它是否隐藏在第一个列表框后面,但滚动更平滑
自定义滚动支持.cls 这个类总结了向
VB.ListBox
控件添加“自定义滚动支持”(因为没有更好的名称)所需的逻辑。它不应该直接使用,而是使用ListBoxExtras.bas
模块中的Add*
方法之一(稍后我将提供该模块的代码)
是的,我担心“什么都不做”不是答案,因为VB在点击事件后更改了选择本身。你需要做额外的工作才能什么都不做。。。我只是还不知道是什么。任何有帮助的API都将不胜感激。我不知道该用哪一个我真的很喜欢这个想法,但它不会与标签或图像控制工作。它们都不能放在列表框的前面以捕获点击事件。对不起,我发誓这是可能的。
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
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