前回で方向キーを縦型テキストボックスに対応させることはできましたが、フォーカスを失うという問題が残っています。
他にも問題があり、メッセージを拾うのはよいのですが、アプリ終了時に御呪いは必要なので、下記の通り記載しました。
Private Sub _Disposed(sender As Object, e As System.EventArgs) Handles Me.Disposed
lpord = Class_API.SetWindowLong(hwndEdit, Class_API.GWL_WNDPROC, lpord)
End Sub
方向キーについても、専用の関数を作成し、それを呼び出す形に修正をかけることで整理。
''' <summary>
''' 方向キーを縦型に対応させるための関数
''' </summary>
''' <param name="uMsg">メッセージ</param>
''' <param name="wParam">パラメータ</param>
''' <returns>True:方向キーの変更を実施 False:方向キーの変更を未実施</returns>
''' <remarks></remarks>
Private Function KeyCheck(ByVal uMsg As IntPtr, ByRef wParam As IntPtr) As Boolean
If uMsg = API.Msg.WM.WM_KEYUP Or uMsg = API.Msg.WM.WM_KEYDOWN Then
Select Case wParam
Case Windows.Forms.Keys.Up
wParam = Windows.Forms.Keys.Left
Case Windows.Forms.Keys.Down
wParam = Windows.Forms.Keys.Right
Case Windows.Forms.Keys.Left
wParam = Windows.Forms.Keys.Down
Case Windows.Forms.Keys.Right
wParam = Windows.Forms.Keys.Up
Case Else
Return False
End Select
Return True
Else
Return False
End If
End Function
さて、ここからが長い戦いがはじまりです。
実のところ、いろいろ調査をしてみましたが、どうにも方向キーのイベントメッセージだけ受け取れない状況でした。ほかのコントロールが受け取っているのかと思い、ハンドルを変えていろいろチェックをかけたのですが、見つからない……
コントロール独自の問題で、内部で変なことをしているのかと思って、他のバージョンやテキストボックスに変更してテストしてみましたが、同様の現象が発生しました。
さらに、ユーザーコントロール上で実施しているのが悪く、直接フォーム上で宣言しても見ましたが、ダメでした。
なお、CreateWindowEx関数で二つ以上の縦書きコントロールを配置してみましたが、こちらは問題なく動作する……
よーわからん………
試しに、通常のテキストボックスに宣言しなおしてもペケ。
さらに別の問題が。
「Me.ParentForm.Handle」では、ベースとなる大元の親フォームのハンドルを返す為、仮にパネルなどに張り付けている場合は動作が正常にならない問題が発生。
(後から気が付きましたが、「Me.Parent.Handle」でやれば下記の処理を省略できた(涙))
縦書コントロールが格納されているコントロールのハンドルを取得する方向で調整する必要があるようです。
しかし「Control.FromChildHandle(Me.Handle).Handle」では、貼り付けているハンドルをなぜか取得できない。
仕方がないので、自分が格納されているコントロールを調べる関数を作成することに。
''' <summary>
''' 自分が格納されているコントロールのハンドルを取得する
''' </summary>
''' <param name="Value">調べるコントロール</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function FindHandle(ByVal Value As Windows.Forms.Control) As Integer
For Each i As Object In Value.Controls
If i.handle = Me.Handle Then
Return Value.Handle
Else
If i.Controls.count > 0 Then
Dim a As Integer = FindHandle(i)
If a <> 0 Then
Return a
End If
End If
End If
Next
Return 0
End Function
次に「SetPararenthWnd」プロシを下記に修正
''' <summary>
''' オーナーウインドウを変更する
''' </summary>
''' <remarks></remarks>
Public Sub SetPararenthWnd()
If Me.ParentForm IsNot Nothing AndAlso _SetParant = False Then
Me.Enabled = False
Dim MePanel As Integer = FindHandle(Me.ParentForm)
Class_API.SetParent(hwndEdit, MePanel)
lpord = Class_API.SetWindowLong(hwndEdit, Class_API.GWL_WNDPROC, AddressOf TateControlProc)
_SetParant = True
End If
End Sub
次に前回ではユーザーコントロールのLoadイベント時にオーナーウインドウを変更する「SetPararenthWnd」プロシージャを呼び出していました。
なんとかユーザーコントロール内だけでクローズしたいので、ユーザーコントロールのLoadイベント時で宣言してみました。
デザイン時の問題が発生する件については、「ParentForm」変数を調べることで実行の有無を検証させることで、問題の解決を図ります。
Private Sub _Load(sender As Object, e As System.EventArgs) Handles Me.Load
Call SetPararenthWnd()
End Sub
残念ながら、このコードでは正常に動作しませんでした。
どうやら親フォームのLoad前にこちらのLoadが呼び出されているようです。タイミング的に逆なら解決なんですが……
また、複数の縦書きを配置する場合、それぞれパネルを事前に配置して呼び出すのであればOKなのですが、直接配置した場合、縦書コントロールが重なってしまうという問題も発生。
「SetWindowPos(hwndEdit, 0, Me.Left, Me.Top, Me.Width, Me.Height, Class_API.SWP_NOMOVE Or Class_API.SWP_NOZORDER)」
こちらを宣言していると、デザイン時にエディットソフトが強制終了する問題すら発生。
「lpord = Class_API.SetWindowLong(hwndEdit, Class_API.GWL_WNDPROC, AddressOf TateControlProc)」
結果として、デザイン時に不要だと思うコードについては識別することにしました。また、LoadについてもonLoad時に変更。
デザインモードかそうでないかは「Me.DesignMode」で判別できます。
(なお、このプロパティですが、.NetFrameworksのバージョンで変わってくるので要注意)
コードの整理も含めて、縦書きコントロールを作成し制御するコードをユーザーコントロール内ではなく、クラスを別途用意してそこに宣言することに。
あまり好きじゃないのですが、デザイン時はCreateWindow関数で縦書きコントロールは宣言せず、ユーザーコントロールが縦書きコントロールのように擬態することにしました。
<ユーザーコントロール内>
Dim c As Class_CreateTateRichText = Nothing
''' <summary>
''' 読み込み時の初期処理
''' </summary>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Overrides Sub OnLoad(e As System.EventArgs)
MyBase.OnLoad(e)
If Me.DesignMode = False AndAlso Me.ParentForm IsNot Nothing AndAlso Me.ParentForm.Handle <> Me.Handle Then
c = New Class_CreateTateRichText
c.ADD(Me)
Call c.ChangeParent(Me.Parent)
Me.BackColor = Color.Transparent
Me.BorderStyle = Windows.Forms.BorderStyle.None
Else
Me.BackColor = Color.White
Me.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
End If
End Sub
Protected Overrides Sub Finalize()
c.lpord = Class_API.SetWindowLong(c.Handle, Class_API.GWL_WNDPROC, c.lpord)
Class_API.DestroyWindow(c.Handle)
MyBase.Finalize()
End Sub
ユーザーコントロールを二つ宣言した場合では、方向キーを押しても問題ない状況まで行きました。ただし、直接テキストボックスなど、フォーカスを受け取ることができるコントロールを配置してやると相変わらずそっちにフォーカスが移動してしまいます。
調べていくと、たぶんですが、モードレスダイアログという扱いになっているっぽい。
なので、ダイアログ同士であればフォーカスの移動は発生しないのですが、親ウインドウが存在する状況でフォーカスを受け取れる環境では、ダイアログ内にコントロールが存在するという判定を行い、コントロール間の移動が発生するんだろうなと、予測を立てました。
実際、WindowProcではなくCallBackProcの方を覗いてみると、受け取っているメッセージの種類は
264:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
265:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
266:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
267:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
268:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
269:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
270:msg=0x87 (WM_GETDLGCODE) hwnd=0x60818 wparam=0x0 lparam=0x0 result=0x0
こんな感じでありましたし(「Debug.Print」でログを吐き出したもののコピペ。)
つまるところ、Windowsは次のフォーカスがある場合は、一部のキーを特殊キーとして処理しているという……これをどうにかするには、特殊キー云々を判定しているプリプロセスをどうにかしてやる必要があるっポイ。
通常のデザイナー等で配置した場合は下記のコードやらプロセスメッセージをオーバーライドしてやればいけそうですが、、APIで宣言した関係で、どうにもこういう風にはいかないっポイ。
Private Sub _PreviewKeyDown(sender As System.Object, e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles MyBase.PreviewKeyDown
Select Case e.KeyCode
Case Keys.Up, Keys.Down, Keys.Left, Keys.Right
e.IsInputKey = True
End Select
End Sub
一応、ためしに
Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean
Return MyBase.ProcessDialogKey(keyData)
End Function
このようにユーザーコントロール内で宣言して、イベントメッセージを拾っているのか確認してみました。
当然といえば当然なんでしょうねぇ。受けっていませんでした。
もし拾えていればよかったんですが、、、
以下、ユーザーコントロール内で試してみたけどダメだった一覧
#Region "ダメコード(失敗)"
Public Overrides Function PreProcessMessage(ByRef msg As System.Windows.Forms.Message) As Boolean
If c IsNot Nothing Then msg.HWnd = c.Handle
Return MyBase.PreProcessMessage(msg)
End Function
Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean
Debug.Print(keyData.ToString & vbCrLf)
Select Case keyData
Case Keys.Up, Keys.Down, Keys.Left, Keys.Right
Return True
Case Else
Return MyBase.ProcessDialogKey(keyData)
End Select
End Function
Private Sub _PreviewKeyDown(sender As System.Object, e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles MyBase.PreviewKeyDown
Select Case e.KeyCode
Case Keys.Up, Keys.Down, Keys.Left, Keys.Right
e.IsInputKey = True
End Select
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If c IsNot Nothing Then m.HWnd = c.Handle
MyBase.WndProc(m)
End Sub
Protected Overrides Function IsInputKey(keyData As System.Windows.Forms.Keys) As Boolean
Select Case keyData
Case Keys.Up, Keys.Down, Keys.Left, Keys.Right
Return True
Case Else
Return MyBase.IsInputKey(keyData)
End Select
End Function
#End Region
に、煮詰まってきた(汗)
0 件のコメント:
コメントを投稿