我用这个简单的代码同时设置了不同 RichTextBox 控件的两个 Scrollbars 的位置。
当 RichTextBox 的文本比另一个更长时,麻烦就来了。
有什么建议吗?如何计算差异的百分比,以同步两个控件的滚动位置,例如,在开始/中间/结束处,同时?
Const WM_USER As Integer = &H400
Const EM_GETSCROLLPOS As Integer = WM_USER + 221
Const EM_SETSCROLLPOS As Integer = WM_USER + 222
Declare Function SendMessage Lib "user32.dll" Alias "SendMessageW" (ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByRef lParam As Point) As Integer
Private Sub RichTextBox1_VScroll(sender As Object, e As EventArgs) Handles RichTextBox1.VScroll
Dim pt As Point
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, pt)
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, pt)
End Sub
Private Sub RichTextBox2_VScroll(sender As Object, e As EventArgs) Handles RichTextBox2.VScroll
Dim pt As Point
SendMessage(RichTextBox2.Handle, EM_GETSCROLLPOS, 0, pt)
SendMessage(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, pt)
End Sub
答案 0 :(得分:2)
此处描述了该过程:
How to scroll a RichTextBox control to a given point regardless of caret position
您需要计算控件的最大滚动值
考虑 ClientSize.Height
和 Font.Height
:当我们定义最大滚动位置时,它们都起作用。最大垂直滚动值定义为:
MaxVerticalScroll = Viewport.Height - ClientSize.Height + Font.Height - BorderSize
其中 Viewport 是包含其所有内容的控件的整体内部表面。
它通常由 PreferredSize 属性(属于 Control
类)返回,但是,例如 RichTextBox,在文本换行之前设置 PreferredSize
,因此它只是相对于未换行的文本,在这里用处不大。
您可以手动确定基本距离(如上面的链接所述),或使用 GetScrollInfo() 函数。它返回一个 SCROLLINFO 结构,其中包含绝对的最小和最大滚动值以及当前滚动位置。
计算两个最大滚动位置的相对差:这是用于缩放两个滚动位置的乘数因子,以生成一个共同的相对值。
重要:使用VScroll
事件,必须引入一个变量,防止两个Control反复触发对方的Scroll动作,导致StackOverflow异常。
请参阅 VScroll 事件处理程序和 synchScroll
布尔字段的使用。
▶ SyncScrollPosition()
方法调用计算相对值的 GetAbsoluteMaxVScroll()
和 GetRelativeScrollDiff()
方法滚动值,然后调用 SendMessage 设置要同步的控件的滚动位置。
两者都接受 TextBoxBase
参数,因为 RichTextBox 派生自此基类,作为 TextBox 类,因此您可以对 RichTextBox 和 TextBox 控件使用相同的方法而无需任何更改。
▶ 使用您在此处找到的 SendMessage
声明等。
Private synchScroll As Boolean = False
Private Sub richTextBox1_VScroll(sender As Object, e As EventArgs) Handles RichTextBox1.VScroll
If synchScroll Then Return
synchScroll = True
SyncScrollPosition(RichTextBox1, RichTextBox2)
synchScroll = False
End Sub
Private Sub richTextBox2_VScroll(sender As Object, e As EventArgs) Handles RichTextBox2.VScroll
If synchScroll Then Return
synchScroll = True
SyncScrollPosition(RichTextBox2, RichTextBox1)
synchScroll = False
End Sub
Private Sub SyncScrollPosition(ctrlSource As TextBoxBase, ctrlDest As TextBoxBase)
Dim infoSource = GetAbsoluteMaxVScroll(ctrlSource)
Dim infoDest = GetAbsoluteMaxVScroll(ctrlDest)
Dim relScrollDiff As Single = GetRelativeScrollDiff(infoSource.nMax, infoDest.nMax, ctrlSource, ctrlDest)
Dim pt = New Point(0, CType((infoSource.nPos + 0.5F) * relScrollDiff, Integer))
SendMessage(ctrlDest.Handle, EM_SETSCROLLPOS, 0, pt)
End Sub
Private Function GetAbsoluteMaxVScroll(ctrl As TextBoxBase) As SCROLLINFO
Dim si = New SCROLLINFO(SBInfoMask.SIF_POSRANGE)
GetScrollInfo(ctrl.Handle, SBParam.SB_VERT, si)
Return si
End Function
Private Function GetRelativeScrollDiff(sourceScrollMax As Integer, destScrollMax As Integer, source As TextBoxBase, dest As TextBoxBase) As Single
Dim border As Single = If(source.BorderStyle = BorderStyle.None, 0F, 1.0F)
Return (CSng(destScrollMax) - dest.ClientSize.Height) / (sourceScrollMax - source.ClientSize.Height - border)
End Function
Win32 方法声明:
Imports System.Runtime.InteropServices
Private Const WM_USER As Integer = &H400
Private Const EM_GETSCROLLPOS As Integer = WM_USER + 221
Private Const EM_SETSCROLLPOS As Integer = WM_USER + 222
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Friend Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As Integer, <[In], Out> ByRef lParam As Point) As Integer
End Function
<DllImport("user32.dll")>
Friend Shared Function GetScrollInfo(hwnd As IntPtr, fnBar As SBParam, ByRef lpsi As SCROLLINFO) As Boolean
End Function
<StructLayout(LayoutKind.Sequential)>
Friend Structure SCROLLINFO
Public cbSize As UInteger
Public fMask As SBInfoMask
Public nMin As Integer
Public nMax As Integer
Public nPage As UInteger
Public nPos As Integer
Public nTrackPos As Integer
Public Sub New(mask As SBInfoMask)
cbSize = CType(Marshal.SizeOf(Of SCROLLINFO)(), UInteger)
fMask = mask : nMin = 0 : nMax = 0 : nPage = 0 : nPos = 0 : nTrackPos = 0
End Sub
End Structure
Friend Enum SBInfoMask As UInteger
SIF_RANGE = &H1
SIF_PAGE = &H2
SIF_POS = &H4
SIF_DISABLENOSCROLL = &H8
SIF_TRACKPOS = &H10
SIF_ALL = SIF_RANGE Or SIF_PAGE Or SIF_POS Or SIF_TRACKPOS
SIF_POSRANGE = SIF_RANGE Or SIF_POS Or SIF_PAGE
End Enum
Friend Enum SBParam As Integer
SB_HORZ = &H0
SB_VERT = &H1
SB_CTL = &H2
SB_BOTH = &H3
End Enum
工作原理:
请注意,两个控件使用不同的字体:
Segoe UI, 9.75pt
上面的控件Microsoft Sans Serif, 9pt
另一个