多个 RichTextBox 的同步滚动






3.60/5 (6投票s)
2005年1月4日
2分钟阅读

84322
使用 EM_GETSCROLLPOS 和 EM_SETSCROLLPOS 消息同步多个富文本框的滚动。
引言
我注意到很多帖子都在描述如何同步多个 RichTextBox
的滚动。大多数示例使用 GetScrollPos()
调用和/或响应 VScroll
/HScroll
事件。我遇到的问题是,客户端区域并不总是与滚动条位置匹配,并且很难区分是使用 GetScrollInfo()
返回的 trackPos
还是 Pos
值,尤其是在拖动滚动条时。(这可能是因为我对如何使用这些方法/API 不了解,所以请告诉我...)
RichTextBox
提供了一种确定和设置客户端区域位置的方法,而不是滚动条的位置,即使用 EM_GETSCROLLPOS
和 EM_SETSCROLLPOS
消息。使用这种方法可以准确地同步客户端区域,而不是滚动条位置,这正是我们想要的。
代码用法
首先声明 SendMessage
API,它将实际将消息发送到 RTF 控件,并声明 API 常量。我们将修改 SendMessage
声明中的 lParam
参数,以通过引用接受 POINT
结构
Private Declare Auto Function SendScrollPosMessage _
Lib "user32.dll" Alias "SendMessage" ( _
ByVal hWnd As IntPtr, _
ByVal Msg As Integer, _
ByVal wParam As IntPtr, _
ByRef lParam As POINT) As Integer
Private Const WM_USER = &H400
Private Const EM_GETSCROLLPOS = WM_USER + 221
Private Const EM_SETSCROLLPOS = WM_USER + 222
Private Structure POINT
Public x As Integer
Public y As Integer
End Structure
要检索客户端区域的位置,需要向 RichTextbox
发送 EM_GETSCROLLPOS
消息。您需要指定目标 RichTextBox
的句柄,并传递 POINT
结构,该结构将在消息发送后包含位置
Dim stcScrollPoint As New Point
SendMessage(rtfText.Handle(), EM_GETSCROLLPOS, _
New IntPtr(0), stcScrollPoint)
Debug.WriteLine(stcScrollPoint.y)
Debug.WriteLine(stcScrollPoint.x)
要设置客户端区域的位置,需要向 RichTextBox
发送 EM_SETSCROLLPOS
消息。如上所述,您需要指定目标 RichTextBox
的句柄,并传递 POINT
结构,该结构将在消息发送后设置位置
Dim stcScrollPoint As New Point
stcScrollPoint.x = 20
stcScrollPoint.y = 40
SendMessage(rtfText.Handle(), EM_SETSCROLLPOS, _
New IntPtr(0), ptrScrollPoint)
实现
我已经将此实现到一个类中,该类允许您简单地添加 RichTextBox
并设置同步,以包含水平、垂直或两个滚动条,并负责其余的操作。该类的用法如下
Private objScrollSync As New clsRTFScrollSync
Public Sub FormLoad(...)...
objScrollSync.ScrollBarToSync = ScrollBars.Vertical
objScrollSync.AddControl(rtfML)
objScrollSync.AddControl(rtfGutter)
End Sub
完整的源代码
Imports System.Runtime.InteropServices
Public Class clsRTFScrollSync
Private Declare Auto Function SendScrollPosMessage _
Lib "user32.dll" Alias "SendMessage"( _
ByVal hWnd As IntPtr, _
ByVal Msg As Integer, _
ByVal wParam As IntPtr, _
ByRef lParam As POINT) As Integer
Private Const WM_USER = &H400
Private Const EM_GETSCROLLPOS = WM_USER + 221
Private Const EM_SETSCROLLPOS = WM_USER + 222
Private Structure POINT
Public x As Integer
Public y As Integer
End Structure
Private aControls As New ArrayList
Private sbScrollBarType As Windows.Forms.ScrollBars
Public Property ScrollBarToSync() As Windows.Forms.ScrollBars
Get
Return sbScrollBarType
End Get
Set(ByVal Value As Windows.Forms.ScrollBars)
sbScrollBarType = Value
End Set
End Property
Public Sub AddControl(ByVal RTFControl As Object)
Dim objControlSubClass As New clsWindowSubClass(RTFControl.Handle, _
RTFControl, Me)
aControls.Add(objControlSubClass)
End Sub
Public Sub SyncScrollBars(ByVal Handle As IntPtr, _
ByVal SubClass As clsWindowSubClass, _
ByVal Window As Object, _
ByRef WindowsMessage As Message)
Static blnIgnoreMessages As Boolean
If blnIgnoreMessages = True Then Exit Sub
blnIgnoreMessages = True
Dim blnChangeVertPos As Boolean = False
Dim blnChangeHorizPos As Boolean = False
Dim lngVertPos, lngHorizPos As Long
Dim stcScrollPoint As New Point
Dim ptrScrollPoint As IntPtr
SendScrollPosMessage(Handle, EM_GETSCROLLPOS, _
New IntPtr(0), stcScrollPoint)
lngVertPos = stcScrollPoint.y
lngHorizPos = stcScrollPoint.x
If (sbScrollBarType = RichTextBoxScrollBars.Both Or _
sbScrollBarType = RichTextBoxScrollBars.Vertical) Then
blnChangeVertPos = True
If (sbScrollBarType = RichTextBoxScrollBars.Both Or _
sbScrollBarType = RichTextBoxScrollBars.Horizontal) Then
blnChangeHorizPos = True
If blnChangeVertPos = True Or blnChangeHorizPos = True Then
Dim objSubClass As clsWindowSubClass
Dim objWindowMessage As New Windows.Forms.Message
For Each objSubClass In aControls
If objSubClass.Handle.ToInt32 <> Handle.ToInt32 Then
SendScrollPosMessage(objSubClass.Handle, EM_GETSCROLLPOS, _
New IntPtr(0), stcScrollPoint)
If blnChangeHorizPos = True Then
stcScrollPoint.x = lngHorizPos
If blnChangeVertPos = True Then
stcScrollPoint.y = lngVertPos
SendScrollPosMessage(objSubClass.Handle, EM_SETSCROLLPOS, _
New IntPtr(0), stcScrollPoint)
End If
Next
End If
blnIgnoreMessages = False
End Sub
End Class
Public Class clsWindowSubClass
Inherits System.Windows.Forms.NativeWindow
Private objWindow As Object
Private objParent As clsRTFScrollSync
Public Sub New(ByVal Handle As IntPtr, ByVal Window As Object,
ByVal Parent As clsRTFScrollSync)
objWindow = Window
objParent = Parent
MyBase.AssignHandle(Handle)
End Sub
Protected Overrides Sub WndProc(ByRef WindowMessage As _
system.Windows.Forms.Message)
MyBase.WndProc(WindowMessage)
objParent.SyncScrollBars(MyBase.Handle, Me, _
objWindow, WindowMessage)
End Sub
End Class
致谢
特别感谢 Georgi Atanasov 对本文的贡献。