none
テキストボックス(MultiLine)の改行について RRS feed

  • 質問

  • テキストボックスをMultiLineに設定して文字入力を行っています。

    テキストボックスの右端まで入力し、改行(Enter)を入力せずに文字を入力し続けると、改行コード無しの状態で自動折り返しがされます。

    私の認識では折り返した2行目の先頭で改行(Enterキー)を行うと、2行目の先頭位置は変わらずに2行目の先頭?(1行目の末尾?)に改行コードが挿入されるイメージでしたが、実際は2行目の先頭が3行目に移動してしまします。(2行目は空行の様に見える)

    しかし、その状態でデータ更新を行い、再度取得し、テキストボックスに表示を行うと想定した通り2行目の先頭に改行が入っています。

    入力時に2行目が3行目に移動しない改行制御は可能でしょうか。

    以下、例

    ----------------------------実際の動き----------------------------

     あああああああああああああああああああああああああああああ  ←1行目

    あああ                                        ←2行目

    2行目の先頭で改行(Enter)

     あああああああああああああああああああああああああああああ  ←1行目

                                                ←2行目

    あああ                                        ←3行目

    ここで、イメージとしては2行目の先頭の位置は変わらずに改行を挿入したいです。

    2015年2月17日 15:58

回答

  • こんにちは。
    悩ましいですねこれは。

    まず、WinFormsで良いですかね。
    Resizeか、Textの再設定をするとTextBox内で自動計算してくれますね。
    ※Textの再設定はフォーカスがはずれてしまうので、Resizeしました。

    とりあえず以下が実験コードで。

        Public Sub New()
            InitializeComponent()
            txtHoge.WordWrap = True
            txtHoge.Multiline = True
            txtHoge.ScrollBars = ScrollBars.None
            txtHoge.Text = "あああああああああああああああああああああああああ"
            AddHandler txtHoge.KeyDown,
                Sub(sender As Object, e As KeyEventArgs)
                    If e.KeyCode = Keys.Enter Then
                        txtHoge.Width += 1
                        txtHoge.Width -= 1
                    End If
                End Sub
        End Sub

    お求めなのはその再計算処理を呼び出す方法は無いのか、ということですよね。

    見当たりませんでした。私も知りたいです…。

    • 回答としてマーク T.Akiba0905 2015年2月18日 4:48
    2015年2月17日 17:35
    モデレータ
  • こんな?

    Private Sub TextBox1_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles TextBox1.PreviewKeyDown
        Dim txb As TextBox = DirectCast(sender, TextBox)
        If (e.KeyCode = Keys.Enter AndAlso Control.ModifierKeys = Keys.None) Then
            Dim len = txb.SelectionLength
            Dim startIndex = txb.SelectionStart
            Dim startLine = txb.GetLineFromCharIndex(startIndex)
            Dim startLineStartIndex = txb.GetFirstCharIndexFromLine(startLine)
    
            If (len = 0 AndAlso startIndex = startLineStartIndex AndAlso startLine <> 0) Then
                '2行目以降の先頭にキャレットがある
                Dim nl = Environment.NewLine
                If (startIndex > nl.Length) Then
                    Dim prev As String = txb.Text.Substring(startIndex - nl.Length, nl.Length)
                    If (prev <> nl) Then'直前が改行ではない
                        'ひとつ前の文字にカーソル移動
                        'ENDキーを押したことにしてひとつ前の行の最後の位置にキャレット移動
                        txb.SelectionStart = startIndex - 1
                        Const WM_KEYDOWN As UInteger = &H100
                        Const WM_KEYUP As UInteger = &H101
                        SendMessage(txb.Handle, WM_KEYDOWN, Keys.End, &H14F0001)
                        SendMessage(txb.Handle, WM_KEYUP, Keys.End, &HC4F0001)
                        txb.SelectionLength = len
                    End If
                End If
            End If
        End If
    End Sub
    <system.runtime.interopservices.dllimport("user32.dll")>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    End Function</system.runtime.interopservices.dllimport("user32.dll")>

    #メモ帳なども同じ挙動なのでこういうものだという気もしますが。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク T.Akiba0905 2015年2月18日 5:33
    • 編集済み gekkaMVP 2015年2月18日 9:48 SendMessageの定義の貼り忘れ
    2015年2月18日 3:55

すべての返信

  • こんにちは。
    悩ましいですねこれは。

    まず、WinFormsで良いですかね。
    Resizeか、Textの再設定をするとTextBox内で自動計算してくれますね。
    ※Textの再設定はフォーカスがはずれてしまうので、Resizeしました。

    とりあえず以下が実験コードで。

        Public Sub New()
            InitializeComponent()
            txtHoge.WordWrap = True
            txtHoge.Multiline = True
            txtHoge.ScrollBars = ScrollBars.None
            txtHoge.Text = "あああああああああああああああああああああああああ"
            AddHandler txtHoge.KeyDown,
                Sub(sender As Object, e As KeyEventArgs)
                    If e.KeyCode = Keys.Enter Then
                        txtHoge.Width += 1
                        txtHoge.Width -= 1
                    End If
                End Sub
        End Sub

    お求めなのはその再計算処理を呼び出す方法は無いのか、ということですよね。

    見当たりませんでした。私も知りたいです…。

    • 回答としてマーク T.Akiba0905 2015年2月18日 4:48
    2015年2月17日 17:35
    モデレータ
  • こんな?

    Private Sub TextBox1_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles TextBox1.PreviewKeyDown
        Dim txb As TextBox = DirectCast(sender, TextBox)
        If (e.KeyCode = Keys.Enter AndAlso Control.ModifierKeys = Keys.None) Then
            Dim len = txb.SelectionLength
            Dim startIndex = txb.SelectionStart
            Dim startLine = txb.GetLineFromCharIndex(startIndex)
            Dim startLineStartIndex = txb.GetFirstCharIndexFromLine(startLine)
    
            If (len = 0 AndAlso startIndex = startLineStartIndex AndAlso startLine <> 0) Then
                '2行目以降の先頭にキャレットがある
                Dim nl = Environment.NewLine
                If (startIndex > nl.Length) Then
                    Dim prev As String = txb.Text.Substring(startIndex - nl.Length, nl.Length)
                    If (prev <> nl) Then'直前が改行ではない
                        'ひとつ前の文字にカーソル移動
                        'ENDキーを押したことにしてひとつ前の行の最後の位置にキャレット移動
                        txb.SelectionStart = startIndex - 1
                        Const WM_KEYDOWN As UInteger = &H100
                        Const WM_KEYUP As UInteger = &H101
                        SendMessage(txb.Handle, WM_KEYDOWN, Keys.End, &H14F0001)
                        SendMessage(txb.Handle, WM_KEYUP, Keys.End, &HC4F0001)
                        txb.SelectionLength = len
                    End If
                End If
            End If
        End If
    End Sub
    <system.runtime.interopservices.dllimport("user32.dll")>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    End Function</system.runtime.interopservices.dllimport("user32.dll")>

    #メモ帳なども同じ挙動なのでこういうものだという気もしますが。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク T.Akiba0905 2015年2月18日 5:33
    • 編集済み gekkaMVP 2015年2月18日 9:48 SendMessageの定義の貼り忘れ
    2015年2月18日 3:55
  • さっそくご回答ありがとうございます。

    やはり既出の事象なのですね・・・。ネットで検索しても同様の現象の回避策が見当たらなく困っていました。

    ご提示いただいた実験コードを実装したところ解決しました!!!

     ありがとうございました。

    2015年2月18日 4:48