結果、継承コントロールでオブジェクトを指定してやり、作成したコントロールを参照渡しする方法も探しましたができなかったので、再びプロパティをちまちま作成することに。
まずは、現在状況の調整から。
現状のままでは、実行時に→キーを押すとうまくいかないなどの問題があり、このまま使用することは厳しい状況です。
プロパティを作成する前に、この辺の調整から始めます。
十時キーの問題はどうやら、「CreateWindowEx」関数を宣言したタイミングでのパラメーターの一つ、
<param
name="hWndParent">親ウインドウまたはオーナーウインドウのハンドル</param>
こいつが、宣言時はユーザーコントロールのハンドルを渡しているために、発生した問題のようです。
これを「Me.Handle」から「Me.ParentForm.Handle」に変更した場合、Newのタイミングでは「Me.ParentForm」は「Nothing」のため、エラーを返します。
なので「CreateWindowEx」関数の宣言のタイミングを「New」から「Load」に変更すれば問題は解決します。
しかし、「Load」のタイミングでやると、デザイン段階でコントロールのサイズや移動を行うとうまくいかないという不具合が発生しました。
なので、デザイン時のオーナーはユーザーコントロールが、実行時のオーナーは配置先のウインドウという切り替えをしてやる必要があります。
まず、オーナーの変更には「SetParent」関数を使用します。
''' <summary>
''' 指定された子ウインドウの親ウインドウを変更します
''' </summary>
''' <param
name="hWndChild">ウインドウハンドル</param>
''' <param
name="hWndNewParent">新しい親ウインドウ</param>
''' <returns></returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll",
SetLastError:=True)> _
Public Shared Function
SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As
IntPtr) As IntPtr
End Function
次に、ユーザーコントロール側に以下の宣言を行います。
''' <summary>
''' オーナーウインドウの変更の実施の有無 True:変更済み False:変更前
''' </summary>
''' <remarks></remarks>
Private _SetParant As Boolean
= False
''' <summary>
''' オーナーウインドウを変更する
''' </summary>
''' <remarks></remarks>
Public Sub SetPararenthWnd()
If Me.ParentForm IsNot Nothing Then
Me.Enabled
= False
Class_API.SetParent(hwndEdit, Me.ParentForm.Handle)
_SetParant
= True
End If
End Sub
最後に、作成したユーザーコントロール(縦書きテキストボックス)を配置したウインドウに、以下のコードを記載します。
Private Sub test4_Load(sender As System.Object,
e As System.EventArgs)
Handles MyBase.Load
Call TateTextBox_Test41.SetPararenthWnd()
End Sub
これでとりあえずは、デザイン時のレイアウト調整及び実行時のカーソルキーの問題は解決します。
次にサイズ変更について、以下の通りおまじないをかけます。
''' <summary>
''' 縦書きコントロールの位置及びサイズ調整
''' </summary>
''' <remarks></remarks>
Private Sub ReSetWindow(sender As Object, e As System.EventArgs)
Class_API.SetWindowPos(hwndEdit,
0, Me.Left, Me.Top,
Me.Width, Me.Height,
Class_API.SWP_NOMOVE Or Class_API.SWP_NOZORDER)
End Sub
次に「CreateWindow」関数を宣言した後に、以下のコードを追加し、サイズ変更およびコントロール移動時に上記プロシージャを呼び出すようにします。
AddHandler Me.SizeChanged, AddressOf
ReSetWindow
AddHandler Me.Move, AddressOf
ReSetWindow
これで終了なのですが、再び問題が発生。
縦書き用テキストボックスがこれ一つであれば問題はないのですが、これ以外の入力フォーカスのあるコントロールが配置されている場合、そちらにフォーカスが移動してしまうという事態が発生してしまいました。
こうなると、イベントを拾ってやるしかないなぁって思って、下記のようにどういうイベントか見てみましたが、どうにも拾えない。
Protected Overrides Sub
WndProc(ByRef m As
System.Windows.Forms.Message)
MyBase.WndProc(m)
End Select
Debug.Print(m.ToString)
End Sub
CreateWindow関数はAPIなので、もしかしてAPI関数で作成したコントロールのイベントは拾えない??
こうなると、イベント自体を取得するためのコードが必要そうです。なんてめんどくさい……
この問題を放置すると、正直コントロールとして使えないので、何とかイベントを拾ってやろうと「SetWindowLong」関数を使うことに。
''' <summary>
''' 指定されたウインドウの属性を変更します。拡張ウインドウメモリ内に指定されたオフセット位置にあるlongデータも設定できます
''' </summary>
''' <param
name="hWnd">ウインドウのハンドル</param>
''' <param
name="nIndex">設定する値のオフセット</param>
''' <param
name="dwNewLong">新しい値</param>
''' <returns>成功すると、変更前の値が帰ります。失敗すると0が返ります。</returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll",
SetLastError:=True)> _
Public Shared Function
SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As IntPtr, ByVal
dwNewLong As IntPtr)
As IntPtr
End Function
ユーザーコントロール内
Dim lpord As Integer = 0
''' <summary>
''' 縦書きコントロール用コールバック関数
''' </summary>
''' <param
name="m"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function TateControlProc(ByRef m As Message) As IntPtr
Static calling As Boolean
If calling = False Then
calling = True
Debug.Print(Hex(m.Msg), Hex(m.LParam), Hex(lpord))
calling = False
End If
TateControlProc
= Class_API.CallWindowsProc(lpord, m.HWnd,
m.Msg, m.WParam, m.LParam)
End Function
Private Sub TateTextBox_Test4_Load(sender As Object, e As System.EventArgs)
Handles Me.Load
lpord = Class_API.SetWindowLong(hwndEdit, Class_API.GWL_WNDPROC, AddressOf
WindowProc)
End Sub
とすると
このようなエラーが(涙)
仕方がないのでDelegateを宣言してやることに。
''' <summary>
''' 指定されたウインドウの属性を変更します。拡張ウインドウメモリ内に指定されたオフセット位置にあるlongデータも設定できます
''' </summary>
''' <param
name="hWnd">ウインドウのハンドル</param>
''' <param
name="nIndex">設定する値のオフセット</param>
''' <param
name="dwNewLong">新しい値</param>
''' <returns>成功すると、変更前の値が帰ります。失敗すると0が返ります。</returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll",
SetLastError:=True)> _
Public Shared Function
SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As IntPtr, ByVal
dwNewLong As WndProcDelegate)
As IntPtr
End Function
''' <summary>
''' SetWindowLong関数のAddressof用デリゲート型
''' </summary>
''' <param
name="hWnd">ウインドウハンドル</param>
''' <param
name="uMsg">メッセージ</param>
''' <param
name="wParam">最初のパラメータ</param>
''' <param
name="lParam">二番目のパラメータ</param>
''' <returns></returns>
''' <remarks></remarks>
Public Delegate Function
WndProcDelegate(ByVal
hWnd As IntPtr,
ByVal uMsg As IntPtr, wParam As IntPtr, ByVal
lParam As IntPtr)
As IntPtr
''' <summary>
''' 指定されたウインドウプロシージャに、メッセージ情報を渡します
''' </summary>
''' <param
name="lpPrevWndFunc">元のウインドウプロシージャ</param>
''' <param
name="hWnd">ウインドウのハンドル</param>
''' <param
name="msg">メッセージ</param>
''' <param
name="wParam">メッセージの最初のパラメータ</param>
''' <param
name="lParam">メッセージの2番目のパラメータ</param>
''' <returns></returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll",
SetLastError:=True)> _
Public Shared Function
CallWindowProc(ByVal lpPrevWndFunc As IntPtr, ByVal hWnd As IntPtr, ByVal msg As IntPtr, ByVal wParam As IntPtr, ByVal
lParam As IntPtr)
As IntPtr
End Function
ユーザーコントロール内も変更
''' <summary>
''' 縦書きコントロール用コールバック関数
''' </summary>
''' <param
name="hWnd">ウインドウハンドル</param>
''' <param
name="uMsg">メッセージ</param>
''' <param
name="wParam">最初のパラメータ</param>
''' <param
name="lParam">二番目のパラメータ</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function TateControlProc(ByVal hWnd As IntPtr, ByVal uMsg
As IntPtr,
wParam As IntPtr,
ByVal lParam As
IntPtr) As IntPtr
Static calling As Boolean
If calling = False Then
calling = True
(ここに処理を書く)
calling = False
End If
TateControlProc
= Class_API.CallWindowProc(lpord, hWnd,
uMsg, wParam, lParam)
End Function
で、上のコードをユーザーコントロール内に記載するとエラーが発生しました。(ぉぃぉぃ)
縦書きコントロールを配置したフォームに変更すると問題なく動きましたので、「SetPararenthWnd」関数の宣言に合わせる形の方がよいかもしれません。
わたしは、縦型テキストコントロール内で処理したかったので、そのようにしました。
次にキーイベントごとにふるい分けして処理してやればいいのですが、いろいろ関数とか宣言してそれに合わせて定数を作ったりしてきたので、整理が必要かも(汗
とりあえず、キーイベントについては別のクラス内で宣言しようと思います。(定数なら列挙体で宣言してもいけそうではあるんですけどね)
今回必要なイベント定数は、キーが押された、離されたの二つですから
''' <summary>キーが押された</summary>
Friend Const WM_KEYDOWN As
Integer = &H100
''' <summary>キーが離された</summary>
Friend Const WM_KEYUP As
Integer = &H101
このように宣言し、「(ここに処理を書く)」にて識別します。
今回のように、縦書ボックスにした場合の方向キーの動作が変になる件については
Select Case uMsg
Case API.Msg.WM.WM_KEYUP, API.Msg.WM.WM_KEYDOWN
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
End Select
Case Else
End Select
このように記載してやればOKです。前後左右のボタンの問題はようやく解決を見せました。
次に方向キーを押すとコントロールのフォーカスが変更する件。(てか、最初にこっちだろうとw)
ログを調べたところ、キーダウンもアップのイベントも拾えませんでした。その代り「8」と「281」の番号を拾えたので、おそらくコレをキャンセルしてやればいけそうな気もします。
まずは意味から。
8はフォーカスを失ったときに発生する「WM_KILLFOCUS」定数であることがわかりました。
281はウインドウがアクティブになった時にアプリケーションに送信される「WM_IME_SETCONTEXT」定数のようです。
どちらにしても、フォーカスを失った後に発生しては意味がないわけで、、、さて、どうしたものか?
0 件のコメント:
コメントを投稿