65.9K
CodeProject 正在变化。 阅读更多。
Home

多个 RichTextBox 的同步滚动

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.60/5 (6投票s)

2005年1月4日

2分钟阅读

viewsIcon

84322

使用 EM_GETSCROLLPOS 和 EM_SETSCROLLPOS 消息同步多个富文本框的滚动。

引言

我注意到很多帖子都在描述如何同步多个 RichTextBox 的滚动。大多数示例使用 GetScrollPos() 调用和/或响应 VScroll/HScroll 事件。我遇到的问题是,客户端区域并不总是与滚动条位置匹配,并且很难区分是使用 GetScrollInfo() 返回的 trackPos 还是 Pos 值,尤其是在拖动滚动条时。(这可能是因为我对如何使用这些方法/API 不了解,所以请告诉我...)

RichTextBox 提供了一种确定和设置客户端区域位置的方法,而不是滚动条的位置,即使用 EM_GETSCROLLPOSEM_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 对本文的贡献。

© . All rights reserved.