DOBON.NET DOBON.NETプログラミング掲示板過去ログ

【VB.NET+FW3.0〜】入力情報を維持したままフォーカスを移動させたいのですが……

環境/言語:[VB.NET(Express2008) .NETFramework2.0~]
分類:[.NET]

いつもこちらのサイト及び掲示板の皆様には大変お世話になっております。
今回どうしても自力では解決できなかったので、こちらで質問させてください。

当方はVisualBasicのExpressEditon2008で開発を行っています。
.NET Frameworkも基本は2.0ですが、必要な機能があれば3.0も使用しています。

今回、いわゆるチャットソフトのようなものを作成しており、ログの表示に
ReadOnlyプロパティをTrueにしたRichTextBoxを使用し、入力にはMultiLine
アリのTextBoxを利用しています。

いわゆるLimeChatなどに実装されているように、ログであるRichTextBoxなどに
フォーカスがあった場合、入力が行われたらそれをTextBoxに移動させたいのです。

まず、RichTextBoxのKeyPressやPreviewKeyDownなどで入力があった場合
TextBox.Focusでフォーカスを移動してみましたが、これでは入力された
キーがなくなってしまい、ダメでした。

次に

'Private Sub RichTextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles RichTextBox1.KeyPress

' e.Handled = True
' TextBox1.Focus()
' TextBox1.Text = TextBox1.Text & e.KeyChar
' TextBox1.SelectionStart = TextBox1.Text.Length

'End Sub

このようにしてみたところ、キー情報は送信できましたが、これで日本語入力など
は維持できないため、「aああ……」となってしまいます。


何か方法はないものでしょうか、お教えください。
■No26241に返信(罠師さんの記事)
> いつもこちらのサイト及び掲示板の皆様には大変お世話になっております。
> 今回どうしても自力では解決できなかったので、こちらで質問させてください。
>
> 当方はVisualBasicのExpressEditon2008で開発を行っています。
> .NET Frameworkも基本は2.0ですが、必要な機能があれば3.0も使用しています。
>
> 今回、いわゆるチャットソフトのようなものを作成しており、ログの表示に
> ReadOnlyプロパティをTrueにしたRichTextBoxを使用し、入力にはMultiLine
> アリのTextBoxを利用しています。
>
> いわゆるLimeChatなどに実装されているように、ログであるRichTextBoxなどに
> フォーカスがあった場合、入力が行われたらそれをTextBoxに移動させたいのです。
>
> まず、RichTextBoxのKeyPressやPreviewKeyDownなどで入力があった場合
> TextBox.Focusでフォーカスを移動してみましたが、これでは入力された
> キーがなくなってしまい、ダメでした。
>
> 次に
>
> 'Private Sub RichTextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles RichTextBox1.KeyPress
>
> ' e.Handled = True
> ' TextBox1.Focus()
> ' TextBox1.Text = TextBox1.Text & e.KeyChar
> ' TextBox1.SelectionStart = TextBox1.Text.Length
>
> 'End Sub
>
> このようにしてみたところ、キー情報は送信できましたが、これで日本語入力など
> は維持できないため、「aああ……」となってしまいます。
>
>
> 何か方法はないものでしょうか、お教えください。

はじめまして。

たぶん、これで回避できると思います。間違っていたらすみません。

'Private Sub RichTextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles RichTextBox1.KeyPress

   if e.Handled = True then Exit Sub '←追加

e.Handled = True
TextBox1.Focus()
TextBox1.Text = TextBox1.Text & e.KeyChar
TextBox1.SelectionStart = TextBox1.Text.Length

'End Sub
> たぶん、これで回避できると思います。間違っていたらすみません。
>
> 'Private Sub RichTextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles RichTextBox1.KeyPress
>
>    if e.Handled = True then Exit Sub '←追加
>
> e.Handled = True
> TextBox1.Focus()
> TextBox1.Text = TextBox1.Text & e.KeyChar
> TextBox1.SelectionStart = TextBox1.Text.Length
>
> 'End Sub
>

GYROさんありがとうございます。
試してみましたが、変化はありませんでした。
やはり日本語入力オンの状態で「あああああ」と入力しても
最初の一文字だけ「aああああ」となってしまいます。
上のソースでは確かに、キャラクターをテキストボックスに挿入している
だけなので、こうなるのはわかっているのです……orz

どうにか日本語入力維持したまま(変換候補とかの出る状態で)TextBoxに移動するか
先にフォーカスをTextBoxに移動させる方法はないものでしょうか……
(難しいのは、完全に先にフォーカスを移動させてしまうと、矢印キーや
 PageDownなども移動してしまうことです)
お疲れ様です。

とりあえず以下のコードで試してみてください。
(私のPCではうまくいきましたが、私はSendMessageの使い方を詳しく知りません。
完全にはテストしていないので、入念な確認をお願いいたします)

Imports System.Runtime.InteropServices

Public Class Form_RichTextBoxTest
    Inherits Form

    Dim WithEvents TextBox1 As New TextBox
    Dim WithEvents RichTextBox1 As New RichTextBoxEx(Me.TextBox1)

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        With Me.RichTextBox1
            .Multiline = True
            .Bounds = New Rectangle(30, 30, 220, 100)
            ' ※ ReadOnlyにせず、ImeModeをTextBox1と同期させる
            .BackColor = Control.DefaultBackColor
            .Text = "かきくけこ123" + vbCrLf + "さしすせそABCDEF"
        End With

        With Me.TextBox1
            .Multiline = True
            .Bounds = New Rectangle(50, 160, 220, 100)
            .Text = "あいうえおABC567"
        End With

        Me.Controls.Add(Me.RichTextBox1)
        Me.Controls.Add(Me.TextBox1)

        MyBase.OnLoad(e)
    End Sub

    ' カスタムRichTextBox
    Private Class RichTextBoxEx
        Inherits RichTextBox

        Dim _tbx As TextBox

        Public Sub New(ByVal tbx As TextBox)
            Me._tbx = tbx
            Me._tbx.DataBindings.Add("ImeMode", Me, "ImeMode")
        End Sub

        <DllImport("user32")> _
        Private Shared Sub SendMessage(ByVal hwnd As IntPtr, _
            ByVal msg As Integer, ByVal wparam As IntPtr, ByVal lparam As IntPtr)
            ' コード不要
        End Sub

        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            Const WM_KEYDOWN As Integer = &H100
            Const WM_SYSDEADCHAR As Integer = &H107
            Const WM_IME_STARTCOMPOSITION As Integer = &H10D
            Const WM_IME_KEYDOWN As Integer = &H290
            Const WM_IME_CHAR As Integer = &H286

            If (WM_KEYDOWN <= m.Msg AndAlso m.Msg <= WM_SYSDEADCHAR) OrElse _
                m.Msg = WM_IME_STARTCOMPOSITION OrElse m.Msg = WM_IME_KEYDOWN OrElse _
                m.Msg = WM_IME_CHAR Then

                Select Case m.WParam.ToInt32
                    Case Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.PageDown, Keys.PageUp, Keys.Home, Keys.End
                        MyBase.WndProc(m)
                    Case Else
                        Me._tbx.SelectionStart = Me._tbx.TextLength
                        If Me._tbx.ImeMode = Windows.Forms.ImeMode.On Then
                            ' この辺は苦し紛れ
                            SendMessage(Me._tbx.Handle, WM_IME_CHAR, m.WParam, m.LParam)
                        End If
                        SendMessage(Me._tbx.Handle, m.Msg, m.WParam, m.LParam)
                        Me._tbx.Focus()
                        Return
                End Select

            Else
                MyBase.WndProc(m)
            End If
        End Sub
    End Class

End Class
>>H.K.R.さま

ちょっと今週は時間がないのですが、週末にでもさっそく試してみます。
取り急ぎ、御礼を申し上げます。
ありがとうございました〜!
コード自体は問題なさそうなのですが、クラスに貼付けても
新規アプリに貼りつけても動作しないようです……

OnLoadがオーバーライドされてるけど、呼ばれていないようです。
もう少し調べてみます。 ありがとうございました。
>>H.K.R.さま
できました!ありがとうございます!
あとはこれをクラス化できるように……頑張ります。
ひとまず解決とさせて頂きます。

本当にありがとうございました
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板