如何改变RichTextBox行结束从Lf到CrLf



与使用CrLf行结尾的TextBox控件相反,RichTextBox控件使用Lf行结尾。我不想那样。我需要一致性。我需要一个RichTextBox控件谁使用CrLf行结尾。

我打开了反射器中的控件,注意到Text属性的getter调用以下函数:

Private Function StreamOut(ByVal flags As Integer) As String
    '...
    Me.StreamOut(data, flags, False)
    '...
End Function

最终调用:

Private Sub StreamOut(ByVal data As Stream, ByVal flags As Integer, ByVal includeCrLfs As Boolean)
    '...
    Dim es As New EDITSTREAM
    '...
    ElseIf includeCrLfs Then
        num = (num Or &H20)
    Else
    '...
    es.dwCookie = DirectCast(num, IntPtr)
    '...
End Sub

您可以看到,includeCrLfs参数将始终是False

所以我子类化了控件并截获了EM_STREAMOUT消息。此消息的LParam包含指向EDITSTREAM结构体的指针。我在上面的函数中添加了&H20标志,但这不起作用。Text属性开始返回空字符串。我相信我可能不得不删除/附加其他旗帜,但我不知道是哪些旗帜。此外,MSDN不提供任何提示,除了应用程序定义的值

dwCookie
富编辑控件传递给pfnCallback成员指定的EditStreamCallback回调函数的应用程序定义的值

这是我的子类控件:

Public Class UIRichTextBox
    Inherits System.Windows.Forms.RichTextBox
    Private Sub EmStreamOut(ByRef m As Message)
        Dim es As New EDITSTREAM
        es = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(EDITSTREAM)), EDITSTREAM)
        If (IntPtr.Size = 4) Then
            Dim cookie As Int32 = es.dwCookie.ToInt32()
            'cookie = (cookie Or &H20I) '<- Didn't work
            es.dwCookie = New IntPtr(cookie)
        Else
            Dim cookie As Int64 = es.dwCookie.ToInt64()
            'cookie = (cookie Or &H20L) '<- Didn't work
            es.dwCookie = New IntPtr(cookie)
        End If
        Marshal.StructureToPtr(es, m.LParam, True)
        MyBase.WndProc(m)
    End Sub
    Protected Overrides Sub WndProc(ByRef m As Message)
        Select Case m.Msg
            Case EM_STREAMOUT
                Me.EmStreamOut(m)
                Exit Select
            Case Else
                MyBase.WndProc(m)
                Exit Select
        End Select
    End Sub
    Private Const EM_STREAMOUT As Integer = &H44A
    Private Delegate Function EDITSTREAMCALLBACK(ByVal dwCookie As IntPtr, ByVal buf As IntPtr, ByVal cb As Integer, <Out()> ByRef transferred As Integer) As Integer
    <StructLayout(LayoutKind.Sequential)> _
    Private Class EDITSTREAM
        Public dwCookie As IntPtr = IntPtr.Zero
        Public dwError As Integer
        Public pfnCallback As EDITSTREAMCALLBACK
    End Class
End Class

所以这些标志并不是没有记录的。它们是EM_GETEDITSTYLE和EM_SETEDITSTYLE消息的一部分。但是正如你所看到的,这个标志已经过时了。

SES_USECRLF 过时了。不要使用。

我想我又回到了起点重写text属性

Public Overrides Property Text() As String
    Get
        Dim value As String = MyBase.Text
        If (Not value Is Nothing) Then
            value = value.Replace(ChrW(13), "")
            value = value.Replace(ChrW(10), Environment.NewLine)
        End If
        Return value
    End Get
    Set(value As String)
        MyBase.Text = value
    End Set
End Property

所以我设法使用反射创建了一个工作解决方案。我相信SES_USECRLF被淘汰肯定有一个很好的理由,所以请谨慎行事。

Public Class UIRichTextBox
    Inherits System.Windows.Forms.RichTextBox
    Shared Sub New()
        UIRichTextBox.InternalEditStream = GetType(System.Windows.Forms.RichTextBox).GetField("editStream", (BindingFlags.NonPublic Or BindingFlags.Instance))
        UIRichTextBox.InternalStreamIn = GetType(System.Windows.Forms.RichTextBox).GetMethod("StreamIn", (BindingFlags.NonPublic Or BindingFlags.Instance), Nothing, New Type() {GetType(System.IO.Stream), GetType(System.Int32)}, Nothing)
        UIRichTextBox.InternalStreamOut = GetType(System.Windows.Forms.RichTextBox).GetMethod("StreamOut", (BindingFlags.NonPublic Or BindingFlags.Instance), Nothing, New Type() {GetType(System.IO.Stream), GetType(System.Int32), GetType(System.Boolean)}, Nothing)
    End Sub
    Public Sub New()
        Me.m_includeCrLfs = True
    End Sub
    <DefaultValue(True), Category("Behavior")> _
    Public Property IncludeCrLfs() As Boolean
        Get
            Return Me.m_includeCrLfs
        End Get
        Set(value As Boolean)
            If (value <> Me.m_includeCrLfs) Then
                Me.m_includeCrLfs = value
                Me.RecreateHandle()
            End If
        End Set
    End Property
    Public Overrides Property [Text]() As String
        Get
            Dim value As String = Nothing
            If (Me.StreamOut(&H11, value)) Then
                Return value
            End If
            Return MyBase.[Text]
        End Get
        Set(ByVal value As String)
            If (Not Me.StreamIn(value, &H11)) Then
                MyBase.[Text] = value
            End If
        End Set
    End Property
    Private Function StreamIn(ByVal str As String, ByVal flags As Integer) As Boolean
        If (((Me.IsHandleCreated AndAlso ((Not Me.IsDisposed) AndAlso (Not Me.Disposing))) AndAlso ((Not str Is Nothing) AndAlso (str.Length > 0))) AndAlso ((Not UIRichTextBox.InternalEditStream Is Nothing) AndAlso (Not UIRichTextBox.InternalStreamIn Is Nothing))) Then
            Dim bytes As Byte()
            Dim index As Integer = str.IndexOf(ChrW(0))
            If (index <> -1) Then
                str = str.Substring(0, index)
            End If
            If ((flags And &H10) <> 0) Then
                bytes = Encoding.Unicode.GetBytes(str)
            Else
                bytes = Encoding.Default.GetBytes(str)
            End If
            Dim data As New System.IO.MemoryStream()
            UIRichTextBox.InternalEditStream.SetValue(Me, data)
            data.Write(bytes, 0, bytes.Length)
            data.Position = 0
            UIRichTextBox.InternalStreamIn.Invoke(Me, New Object() {data, flags})
            Return True
        End If
        Return False
    End Function
    Private Function StreamOut(ByVal flags As Integer, ByRef result As String) As Boolean
        If ((Me.IsHandleCreated AndAlso ((Not Me.IsDisposed) AndAlso (Not Me.Disposing))) AndAlso (Not UIRichTextBox.InternalStreamOut Is Nothing)) Then
            Dim data As New System.IO.MemoryStream()
            UIRichTextBox.InternalStreamOut.Invoke(Me, New Object() {data, flags, Me.m_includeCrLfs})
            data.Position = 0
            Dim length As Integer = CInt(data.Length)
            Dim str As String = String.Empty
            If (length > 0) Then
                Dim buffer As Byte() = New Byte(length - 1) {}
                data.Read(buffer, 0, length)
                If ((flags And &H10) <> 0) Then
                    str = Encoding.Unicode.GetString(buffer, 0, buffer.Length)
                Else
                    str = Encoding.Default.GetString(buffer, 0, buffer.Length)
                End If
                If ((Not String.IsNullOrEmpty(str)) AndAlso (str.Chars((str.Length - 1)) = ChrW(0))) Then
                    str = str.Substring(0, (str.Length - 1))
                End If
            End If
            result = str
            Return True
        End If
        Return False
    End Function
    Private Shared ReadOnly InternalEditStream As FieldInfo
    Private Shared ReadOnly InternalStreamIn As MethodInfo
    Private Shared ReadOnly InternalStreamOut As MethodInfo
    Private m_includeCrLfs As Boolean
End Class

最新更新