Kin*_*ico 3 vb.net winapi scrollbar richtextbox winforms
我使用这个简单的代码同时设置不同 RichTextBox 控件的两个滚动条的位置。
当 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
Run Code Online (Sandbox Code Playgroud)
此处描述了该过程:
\n如何将 RichTextBox 控件滚动到给定点,而不管插入符位置如何
您需要计算控件的最大滚动值
\n考虑ClientSize.Height和Font.Height:当我们定义最大滚动位置时,两者都会发挥作用。最大垂直滚动值定义为:
MaxVerticalScroll = Viewport.Height - ClientSize.Height + Font.Height - BorderSize \nRun Code Online (Sandbox Code Playgroud)\n其中视口是包含其所有内容的控件的整体内表面。
\n它通常由PreferredSize属性(属于该类Control)返回,但是,例如 RichTextBox,设置PreferredSize之前的文本换行,因此它只是相对于未换行的文本,在这里并不是很有用。\n您可以手动
确定基本距离(如上面的链接所述),或使用GetScrollInfo()函数。它返回一个SCROLLINFO结构,其中包含绝对最小和最大滚动值以及当前滚动位置。
计算两个最大滚动位置的相对差:这是用于缩放两个滚动位置的乘数,以生成共同的相对值。
\n重要:使用该VScroll事件,你必须引入一个变量,防止两个Control反复触发对方的Scroll动作,导致StackOverflow异常。
\n请参阅 VScroll 事件处理程序和布尔字段的使用synchScroll。
\xe2\x96\xb6SyncScrollPosition()方法调用GetAbsoluteMaxVScroll()和GetRelativeScrollDiff()方法计算相对滚动值,然后调用 SendMessage 设置要同步的 Control 的 Scroll 位置。
\n两者都接受TextBoxBase参数,因为 RichTextBox 作为 TextBox 类从该基类派生,因此您可以对 RichTextBox 和 TextBox 控件使用相同的方法,而无需进行任何更改。
\xe2\x96\xb6 使用SendMessage您在此处找到的声明以及其他声明。
Private synchScroll As Boolean = False\n\nPrivate Sub richTextBox1_VScroll(sender As Object, e As EventArgs) Handles RichTextBox1.VScroll\n SyncScrollPosition(RichTextBox1, RichTextBox2)\nEnd Sub\n\nPrivate Sub richTextBox2_VScroll(sender As Object, e As EventArgs) Handles RichTextBox2.VScroll\n SyncScrollPosition(RichTextBox2, RichTextBox1)\nEnd Sub\n\nPrivate Sub SyncScrollPosition(ctrlSource As TextBoxBase, ctrlDest As TextBoxBase)\n If synchScroll Then Return\n synchScroll = True\n\n Dim infoSource = GetAbsoluteMaxVScroll(ctrlSource)\n Dim infoDest = GetAbsoluteMaxVScroll(ctrlDest)\n Dim relScrollDiff As Single = GetRelativeScrollDiff(infoSource.nMax, infoDest.nMax, ctrlSource, ctrlDest)\n\n Dim nPos = If(infoSource.nTrackPos > 0, infoSource.nTrackPos, infoSource.nPos)\n Dim pt = New Point(0, CType((nPos + 0.5F) * relScrollDiff, Integer))\n SendMessage(ctrlDest.Handle, EM_SETSCROLLPOS, 0, pt)\n synchScroll = False\nEnd Sub\n\nPrivate Function GetAbsoluteMaxVScroll(ctrl As TextBoxBase) As SCROLLINFO\n Dim si = New SCROLLINFO(SBInfoMask.SIF_ALL)\n GetScrollInfo(ctrl.Handle, SBParam.SB_VERT, si)\n Return si\nEnd Function\n\nPrivate Function GetRelativeScrollDiff(sourceScrollMax As Integer, destScrollMax As Integer, source As TextBoxBase, dest As TextBoxBase) As Single\n Dim border As Single = If(source.BorderStyle = BorderStyle.None, 0F, 1.0F)\n Return (CSng(destScrollMax) - dest.ClientSize.Height) / (sourceScrollMax - source.ClientSize.Height - border)\nEnd Function\nRun Code Online (Sandbox Code Playgroud)\nWin32 方法声明:
\nImports System.Runtime.InteropServices\n\nPrivate Const WM_USER As Integer = &H400\nPrivate Const EM_GETSCROLLPOS As Integer = WM_USER + 221\nPrivate Const EM_SETSCROLLPOS As Integer = WM_USER + 222\n\n<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>\nFriend Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As Integer, <[In], Out> ByRef lParam As Point) As Integer\nEnd Function\n\n<DllImport("user32.dll")>\nFriend Shared Function GetScrollInfo(hwnd As IntPtr, fnBar As SBParam, ByRef lpsi As SCROLLINFO) As Boolean\nEnd Function\n\n<StructLayout(LayoutKind.Sequential)>\nFriend Structure SCROLLINFO\n Public cbSize As UInteger\n Public fMask As SBInfoMask\n Public nMin As Integer\n Public nMax As Integer\n Public nPage As UInteger\n Public nPos As Integer\n Public nTrackPos As Integer\n\n Public Sub New(mask As SBInfoMask)\n cbSize = CType(Marshal.SizeOf(Of SCROLLINFO)(), UInteger)\n fMask = mask : nMin = 0 : nMax = 0 : nPage = 0 : nPos = 0 : nTrackPos = 0\n End Sub\nEnd Structure\n\nFriend Enum SBInfoMask As UInteger\n SIF_RANGE = &H1\n SIF_PAGE = &H2\n SIF_POS = &H4\n SIF_DISABLENOSCROLL = &H8\n SIF_TRACKPOS = &H10\n SIF_ALL = SIF_RANGE Or SIF_PAGE Or SIF_POS Or SIF_TRACKPOS\n SIF_POSRANGE = SIF_RANGE Or SIF_POS Or SIF_PAGE\nEnd Enum\n\nFriend Enum SBParam As Integer\n SB_HORZ = &H0\n SB_VERT = &H1\n SB_CTL = &H2\n SB_BOTH = &H3\nEnd Enum\nRun Code Online (Sandbox Code Playgroud)\n它的工作原理如下:
\n请注意,两个控件包含不同的文本,并且还使用不同的字体:
Segoe UI, 9.75pt上面的控制Microsoft Sans Serif, 9pt另一个C# 版本:
\nMaxVerticalScroll = Viewport.Height - ClientSize.Height + Font.Height - BorderSize \nRun Code Online (Sandbox Code Playgroud)\n声明:
\nusing System.Runtime.InteropServices;\n\nprivate const int WM_USER = 0x400;\nprivate const int EM_GETSCROLLPOS = WM_USER + 221;\nprivate const int EM_SETSCROLLPOS = WM_USER + 222;\n\n[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]\ninternal static extern int SendMessage(IntPtr hWnd, int msg, int wParam, [In, Out] ref Point lParam);\n\n\n[DllImport("user32.dll")]\ninternal static extern bool GetScrollInfo(IntPtr hwnd, SBParam fnBar, ref SCROLLINFO lpsi);\n\n\n[StructLayout(LayoutKind.Sequential)]\ninternal struct SCROLLINFO {\n public uint cbSize;\n public SBInfoMask fMask;\n public int nMin;\n public int nMax;\n public uint nPage;\n public int nPos;\n public int nTrackPos;\n\n public SCROLLINFO(SBInfoMask mask)\n {\n cbSize = (uint)Marshal.SizeOf<SCROLLINFO>();\n fMask = mask; nMin = 0; nMax = 0; nPage = 0; nPos = 0; nTrackPos = 0;\n }\n}\n\ninternal enum SBInfoMask : uint {\n SIF_RANGE = 0x1,\n SIF_PAGE = 0x2,\n SIF_POS = 0x4,\n SIF_DISABLENOSCROLL = 0x8,\n SIF_TRACKPOS = 0x10,\n SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS,\n SIF_POSRANGE = SIF_RANGE | SIF_POS | SIF_PAGE\n}\n\ninternal enum SBParam : int {\n SB_HORZ = 0x0,\n SB_VERT = 0x1,\n SB_CTL = 0x2,\n SB_BOTH = 0x3\n}\nRun Code Online (Sandbox Code Playgroud)\n