【问题标题】:How to change RichTextBox line endings from Lf to CrLf如何将 RichTextBox 行尾从 Lf 更改为 CrLf
【发布时间】:2014-07-27 18:39:52
【问题描述】:

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

我在反射器中打开控件,发现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_GETEDITSTYLEEM_SETEDITSTYLE 消息的一部分。但正如您所见,该标志已过时。

SES_USECRLF 已过时。不要使用。

所以我想我又回到了第一个覆盖文本属性的地方。

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

【问题讨论】:

  • 与其修补未记录的内部结构,为什么不将输出字符串中的 lf 替换为 crlf?
  • 我没有发现问题。不同的是,不,它没有。事实上,我可以开箱即用地复制+粘贴到记事本中,然后它们被转换为 CRLF,当将文本保存到文件时,它们也最终成为 CRLF (0D+0A)。但我可以确认它们仅在 RichTextBox 中是 LF。哦,好吧,如果比较文本,我可以看到这将是一个问题;o 或者只是剥离所有 0D。此外,richtextbox 本身在开头附加了大约 20-30 个 ascii 字符,这些字符不会出现在输出中。
  • 哦,这 30 个字符是:System.Windows.Forms.RichTextBox, Text: lmao ;p 当您执行 .ToString 时 :) WTH? ;p for each line in RTB.lines :\
  • 啊,如果你使用RichTextBox.Rtf 属性,它会返回一个带有 CRLF (0D & 0A) 的字符串来替换 LF (0A)。所以那里有内部代码。您还可以设置该属性以从我想的文件中加载文本。但它也返回一些用于字体等的 RTF 格式代码:\
  • C#相关问题:RichTextBox Newline Conversion?最近才遇到这个,很奇怪的行为。

标签: vb.net winforms winapi richtextbox


【解决方案1】:

所以我设法使用反射创建了一个可行的解决方案。我确信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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多