Enabled Scrollbar in a Disabled Textbox in VB.NET

2019-07-20 05:52发布

问题:

I'm trying to allow scrolling in a Multiline-TextBox even if the TextBox is set to

textbox.Enabled = False

This is not possible, as the Scrollbar is disabled with the Enabled-Command, too.

The default solution here is to set

textbox.ReadOnly = True
textbox.Enabled = True

but this doesn't really do the trick for me. With ReadOnly I can still select the text of a TextBox as well as place the cursor inside of it. But as I have normal (non Multiline-TextBoxes) and other controls on the same form I don't want that to happen. I want to have exactly the same behavior as all the other disabled TextBoxes.

For everything else, like mimic the color of a disabled textbox and so on, there is a legitimate workaround with the ReadOnly-Property, but I coudn't find any for selecting text and placing the cursor.

UPDATE:

With some hints from here I tried to use WIN32 API but it didn't work as expected:

Imports System.Windows.Forms
Imports System.Runtime.InteropServices

Public Class TestTextBox
    Inherits TextBox

    Private Class Native
        <DllImport("user32.dll")> _
        Friend Shared Function EnableScrollBar(ByVal hWnd As IntPtr, ByVal wSBflags As UInteger, ByVal wArrows As UInteger) As Boolean
        End Function
        <DllImport("User32.dll")> _
        Friend Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal wparam As Integer, ByVal lparam As Integer) As Integer
        End Function

        Public Const WM_SETREDRAW As Long = &HB
        Public Const ESB_ENABLE_BOTH As UInteger = 0
        Public Const SB_VERT As UInteger = 1
    End Class

    Public Sub Change() 'Gets called by a Button in my example.
        'Native.SendMessage(Me.Handle, Native.WM_SETREDRAW, New IntPtr(0), IntPtr.Zero)
        Native.EnableScrollBar(Me.Handle, Native.SB_VERT, Native.ESB_ENABLE_BOTH)
        'Native.SendMessage(Me.Handle, Native.WM_SETREDRAW, New IntPtr(1), IntPtr.Zero)
        Me.PerformLayout()
    End Sub

End Class

回答1:

I think this should work. HideCaret makes sure that the caret is, well, hidden in the case that the textbox is ReadOnly. The other WM intercepts prevent the user from selecting anything with mouse or keyboard.

Imports System.Runtime.InteropServices

Public Class CustomTextbox
    Inherits System.Windows.Forms.TextBox

    Private Const WM_KEYDOWN = &H100
    Private Const WM_SYSKEYDOWN = &H104
    Private Const WM_MOUSEMOVE = &H200

    <DllImport("user32.dll")> _
    Private Shared Function HideCaret(ByVal hwnd As IntPtr) As Integer
    End Function

    Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
        If Me.ReadOnly Then HideCaret(Me.Handle)
        MyBase.OnGotFocus(e)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If Me.ReadOnly And (m.Msg = WM_MOUSEMOVE) Or _
                           (m.Msg = WM_KEYDOWN) Or _
                           (m.Msg = WM_SYSKEYDOWN) Then Exit Sub
        MyBase.WndProc(m)
    End Sub
End Class