2014年8月5日火曜日

縦書きテキストボックスコントロールの作成(その7)



方向キーでフォーカスが移動する問題は、まぁ見た目上は前回の処置で終了という形にします。
次はタブキーでフォーカスを移動する件について、なんとかしたいなと。



前回の終わりに考察したやり方をした場合、問題点としてコントロールを使用するユーザーが、タブコントロールのイベントを処理しようとした場合、こちらでのタブ移動処理と干渉してしまう可能性があります。
たとえば、グループ内に配置したコントロールがフォーカスを持つとき、タブキーを押すとあくまでグループ内でフォーカスがループするような形で処理するように調整している場合です。
この時、こちらで干渉する可能として、ループ処理を無視してしまう懸念があります。

まぁ、何がどうなるかわからないので、その方法ではなく、別の方法を模索する必要があるのかなぁと。

そこでまず、Tabが押された場合、フォーカス移動のメッセージ自体はユーザーコントロールに送信され、コントロール側で何らかの処理をした後に次のコントロールにメッセージを送っているのではないか?
ユーザーコントロールに送信されたメッセージを次に回さずにきちんと処理してやればいけるんじゃないか?と思って下記のコードを作成しました。
''' <summary>
''' ユーザーコントロールがフォーカスを受け取った場合、フォーカスをテキストボックスに移す
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _GotFocus(sender As Object, e As System.EventArgs) Handles Me.GotFocus
    Class_API.SetFocus(c.Handle)
End Sub
こちらも同様に忘れずに宣言します。
''' <summary>
''' コントロールが選択されたら、フォーカスをテキストボックスに移す
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _Enter(sender As Object, e As System.EventArgs) Handles Me.Enter
    Class_API.SetFocus(c.Handle)
End Sub
また、忘れずユーザーコントロールの「Enabled」は「True」とします。

これで試したところ、Tabキーによってフォーカスを受け取ることに成功しました。
まずは第一段階成功です。

しかしこのままでは、今度はTabキーによるコントロールの移動はできないままです。
ですので、「MainFormProc」内で実施していた
If hWnd = _NowParent AndAlso uMsg = WM_GETDLGCODE Then
   Dim nF As IntPtr = Class_API.GetFocus
   If nF = _Handle Then hWnd = _Handle
End If
この処理について、手を加えようと考えました。
まず、TabStopプロパティはユーザーコントロールに依存する形にしたいと考えましたので、縦書き用クラス内にユーザーコントロールの情報を参照渡ししてやるべく、
Friend UC As UserControl = Nothing
このように宣言し、ユーザーコントロール内でクラスを作成したときに
c.UC = Me
このように渡してやります。
次に上記「MainFormProc」における
「If hWnd = _NowParent AndAlso uMsg = WM_GETDLGCODE Then」
を
「If hWnd = _NowParent AndAlso uMsg = WM_GETDLGCODE AndAlso UC.TabStop = False Then」
に書き換えてやりました。
「SHIFT+Tab」で前のタブにも移動しましたので、これでタブの問題は解決かなぁ。

と思ったのですが、そうは問屋が卸さない。

パネルなどに張り付けた場合とフォームに直接張り付けた場合とで動作で差が出ます。
パネルに張り付けた方の縦書きボックスにフォーカスがあるとき、十字キーでフォーカスが移動する現象が発生。
直接張り付けた方は問題ないことより、おそらくユーザーコントロールの
''' <summary>
''' 入力されたキーのチェック処理
''' </summary>
''' <param name="KeyData">キー情報</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function KeyChack(KeyData As System.Windows.Forms.Keys) As Boolean
    If c Is Nothing Then
        Return MyBase.ProcessDialogKey(KeyData)
    End If
    Dim n As IntPtr = Class_API.GetFocus()
    Select Case n
        Case c.Handle
            Select Case KeyData
                Case Keys.Up, Keys.Down, Keys.Left, Keys.Right
                    Return MyBase.IsInputKey(KeyData)
                Case Else
                    Return MyBase.ProcessDialogKey(KeyData)
            End Select
        Case Else
            Return MyBase.ProcessDialogKey(KeyData)
    End Select
    Return True
End Function
''' <summary>
''' モーダルダイアログ状況におけるキー処理イベント
''' </summary>
''' <param name="keyData"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean
    Return KeyChack(keyData)
End Function
こいつが働いているのが後者で、働いていないのが前者なのだと思われます。
そして再び変な動作を発見。
SHIFTを押しながら十字キーを押すと、文字列が選択できるというテキストボックスの特性が働かず、フォーカスの移動が発生。

方向キーよ、またお前か!!

「MainFormProc」の
「If hWnd = _NowParent AndAlso uMsg = WM_GETDLGCODE AndAlso UC.TabStop = False Then」
タブ移動については、これが悪さしているのはわかります。
「AndAlso UC.TabStop = False」この部分を削除したら問題なく動作しました。
つまり、タブ入力に関しては、別のところで処理してやらねばならないということです。

順当に行き過ぎたからおかしいと思ってたんだよ(涙)

「TateControlProc」から呼び出している「KeyCheck」関数に下記を追加。
If UC.TabStop = False Then
   wParam = Windows.Forms.Keys.Tab
End If
タブ文字が有効な場合は関数はTrueを返し、Falseを返した場合「TateControlProc」で処理を記載することにしました。
If KeyCheck(uMsg, wParam) = False Then
(ここにコードを書く)
End If

パネルに張り付けた場合の方の縦書きコントロールに問題が発生するのですが、この時もう一つ処置すべきことを発見しました。
それは、オーナーが移動した場合、移動先のコントロールの「GotFocus」「Enter」にて「SetFocus」が宣言されていないこと。
しかし移動した先のパネルに、複数のコントロールが配置されている場合、単純に「SetFocus」してやればいいというわけにはいかないわけです。
となると、親ウインドウに張り付けられている場合と、子ウインドウに張り付けらている場合とで処理を切り替えてやる必要も出てきます。
う~ん、いろいろ出てくるなぁ(涙)

てか、オーナーを変更しているから問題が出るなら、オーナーを変更しなきゃいいじゃん?と思い、
lpord = Class_API.SetWindowLong(_Handle, Class_API.GWL_WNDPROC, AddressOf TateControlProc)
mlpord = Class_API.SetWindowLong(_NowParent, Class_API.GWL_WNDPROC, AddressOf MainFormProc)
を「ADD」に移動させ、オーナー変更処理自体をやめました。

するとどうでしょう?
十字キーは問題なし、サイズ変更問題なし、Tabでフォーカス移動問題なし。

「MainFormProc」でやってた、モードレスダイアログの問題対策も、なんかやらなくても解決してる。
オーナー変更が問題をややこしくしてた感じですね。なんて遠回りな(号泣)
ま、まぁ問題なく動くならいいや。

後はTabStop時の処理をどうするか?です。
って、TabStopって、あれ?
動きが変、、、てか、タブで次のコントロールに移動する。でも、コントロールからタブ移動によるフォーカスを受け取らない、、、
でも縦書きコントロールだけでなく、通常のテキストボックスも同じ、、、

MSDSの方を確認してみると、
書いてあることが違うし。てか、こっちの方が正確だし、、、ふざけるなだし、、、

続けます。
タブ文字の為のプロパティを宣言してやります。    ''' <summary>
''' タブ文字の入力を許可するかどうかを示します。
''' </summary>
''' <value>有効にする場合はTrue。無効にする場合はFalse。</value>
''' <returns></returns>
''' <remarks></remarks>
Public Property AcceptsTab As Boolean = False
次に「ProcessDialogKey」を弄ります。
Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean
   Return KeyChack(keyData)
End Function
↓
Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean
   Select Case keyData
       Case Keys.Tab
           If AcceptsTab Then 'タブ文字が有効の場合
              Return False
           End If
   End Select
   Return KeyChack(keyData)
End Function
これで終了。試してみましたが、問題なく動きました。
次に「SHIFT+Tab」をどうするか?なんですが、まぁ放置することにします。

とりあえず、気が付いたバグやらおかしな動作やらはこれで解決した感じなので、次のプロパティ関連の作成等に行きたいなぁと。

0 件のコメント:

コメントを投稿