すべてのプロパティを作成するのはめんどくさいので、必要そうなプロパティを列挙してみます。
今回は元々テキストボックスでやりたかったので、リッチテキストではなくテキストボックスでできる範囲で使うであろうプロパティで実装が必要そうなものを挙げてみます。
プロパティ名
|
説明
|
BackColor
|
コンポーネントの背景色です。
|
BorderStyle
|
エディットコントロールに境界線を付けるかどうかを示します。Fixed3D,FixedSingle,None
|
CharacterCasing
|
すべての文字をそのまま(Normal)にするか、大文字(Upper)または小文字(Lower)にするかを示します。
|
ContextMenuStrip
|
ユーザーがコントロールを右クリックしたときに表示されるショートカットメニューです。
|
Dock
|
コンテナーに固定されるコントロールの境界線を定義します。
|
Enabled
|
コントロールが有効かどうかを示します。
|
Font
|
コントロールでテキストを表示するフォントです。
|
ForeColor
|
テキストを表示するのに使用される、このコンポーネントの前景色です。
|
HideSelection
|
エディットコントロールがフォーカスを失ったときに選択を非表示にすることを示します。
|
ImeMode
|
選択された時のオブジェクトのIMEの状態を決定します。
|
Lines
|
文字列配列として複数行エディット内に表示されるテキストの行です。
|
Location
|
コンテナーの上部左端に相対する、コントロール上部左端の座標です。
|
Multiline
|
エディットコントロールのテキストが複数行にわたることができるかどうかを設定します。
|
ScrollBars
|
複数行エディットコントロールで、このコントロールに対してどのスクロールバーを表示するかを示します。
|
Text
|
コントロールに関連付けられたテキストです。
|
TextAlign
|
エディットコントロールに対してどのようにテキストを配置するかを示します。
|
Visible
|
コントロールの表示、非表示を示します。
|
WordWrap
|
複数行エディットコントロールで、行が自動的に折り返されるかどうかを示します。
|
上の項目のうち、ユーザーコントロールの方で勝手にやってくれそうなのが
プロパティ名
|
説明
|
Dock
|
コンテナーに固定されるコントロールの境界線を定義します。
|
Enabled
|
コントロールが有効かどうかを示します。
|
Location
|
コンテナーの上部左端に相対する、コントロール上部左端の座標です。
|
Visible
|
コントロールの表示、非表示を示します。
|
AcceptsTabは前回に実装したので今回からは除外します。
プロパティ名
|
説明
|
Text
|
コントロールに関連付けられたテキストです。
|
Textはすでに作成している通りなので、こちらも合わせて除外してもよいと考えます。
プロパティ名
|
説明
|
ContextMenuStrip
|
ユーザーがコントロールを右クリックしたときに表示されるショートカットメニューです。
|
旧来のリッチテキストボックスには、AutoVerbMenuという項目があったのですが、.netになってから標準のコンテキストメニューがなくなったようです。
MSDSを見ても、自作するコードが乗っているだけで、プロパティの変更でどうにかなるようなものではないようです。
メニュー項目としては、元に戻す、切り取り、コピー、貼り付け、削除、すべて選択の6点。
さて、メニューに含めるかどうか悩むのが、元に戻すは「UNDO」に似た操作の「REDO」、つまり「操作をやり直す」というものです。
UNDOは変更履歴をさかのぼるだけなのに対して、REDOは変更履歴ではなく操作履歴を遡る形となり、微妙に違います。
(つまり、状況によっては変更履歴を進める、ブラウザでいうところの右矢印の進むみたいなものです)
リッチテキストボックスのバージョン2以降は実装されている機能のようなので、作りたいなぁと思うこのごろです。
なので、まずはこれら標準に関して一般的なコード(調整前)を記載します。
まずはコンテキストメニューのウインドウ及びその動作において作成します。
''' <summary>リッチテキストコントロールのハンドルを格納した変数</summary>
Dim _RT As IntPtr
''' <summary>
''' コンテキストメニューコントロール
''' </summary>
''' <remarks></remarks>
Dim WithEvents AVM As ContextMenuStrip
''' <summary>
''' 作成時初期処理(メニューの作成)
''' </summary>
''' <param name="TextHandle">縦書ボックスのハンドル</param>
''' <remarks></remarks>
Sub New(TextHandle As IntPtr)
If AVM Is Nothing Then AVM = New ContextMenuStrip
With AVM
.ShowImageMargin = False
.Items.Add(Add_Menu("元に戻す(&U)", Keys.U, True)) '0
.Items.Add(Add_Menu("やり直す(&R)", Keys.R, True)) '1
.Items.Add(New ToolStripSeparator) '2
.Items.Add(Add_Menu("切り取り(&T)", Keys.T, True)) '3
.Items.Add(Add_Menu("コピー(&C)", Keys.C, True)) '4
.Items.Add(Add_Menu("貼り付け(&P)", Keys.P, True)) '5
.Items.Add(Add_Menu("削除(&D)", Keys.Delete, False)) '6
.Items.Add(New ToolStripSeparator) '7
.Items.Add(Add_Menu("すべて選択(&A)", Keys.A, True)) '8
.Items.Add(New ToolStripSeparator) '9
AddHandler CType(.Items(0), ToolStripMenuItem).Click, AddressOf _undo
AddHandler CType(.Items(1), ToolStripMenuItem).Click, AddressOf _Redo
AddHandler CType(.Items(3), ToolStripMenuItem).Click, AddressOf _Cut
AddHandler CType(.Items(4), ToolStripMenuItem).Click, AddressOf _Copy
AddHandler CType(.Items(5), ToolStripMenuItem).Click, AddressOf _Paste
AddHandler CType(.Items(6), ToolStripMenuItem).Click, AddressOf _Delete
AddHandler CType(.Items(8), ToolStripMenuItem).Click, AddressOf _SelectALL
End With
_RT = TextHandle
End Sub
''' <summary>
''' メニューアイコン作成用関数
''' </summary>
''' <param name="ViewText">表示するテキスト</param>
''' <param name="ShoutCutKey">ショートカットキー</param>
''' <param name="ControlKey">ショートカットキーにCtrlキーを含める場合はTrue、含めない場合はFalse</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function Add_Menu(ByVal ViewText As String, ByVal ShoutCutKey As Keys, ByVal ControlKey As Boolean) As ToolStripMenuItem
Dim C As New ToolStripMenuItem(ViewText)
C.ShowShortcutKeys = False
If ControlKey Then
C.ShortcutKeys = CType((System.Windows.Forms.Keys.Control Or ShoutCutKey), System.Windows.Forms.Keys)
Else
C.ShortcutKeys = ShoutCutKey
End If
Return C
End Function
''' <summary>
''' ドロップダウンが開いたときに発生
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub AVM_Opened(sender As Object, e As System.EventArgs) Handles AVM.Opened
For Each i As ToolStripItem In AVM.Items
Select Case True
Case TypeOf i Is ToolStripMenuItem
Select Case i.Text
Case "元に戻す(&U)"
If Class_API.SendMessage(_RT, EM_CANUNDO, 0, 0) <> 0 Then
i.Enabled = True
Else
i.Enabled = False
End If
Case "やり直す(&R)"
If Class_API.SendMessage(_RT, EM_CANREDO, 0, 0) <> 0 Then
i.Enabled = True
Else
i.Enabled = False
End If
Case "切り取り(&T)", "コピー(&C)", "コピー(&C)"
'文字列が選択されているかどうか、選択状況を調べます。
Dim c As CHARRANGE
Class_API.SendMessage(_RT, EM_GETSEL, c.cpMin, c.cpMax)
If c.cpMan = c.cpMin Then
i.Enabled = False
Else
i.Enabled = True
End If
Case "貼り付け(&P)"
If Class_API.SendMessage(_RT, EM_CANPASTE, 0, 0) <> 0 Then
i.Enabled = True
Else
i.Enabled = False
End If
Case "すべて選択(&A)"
Dim _LENGTH As Integer = Class_API.SendMessage(_RT, WM_GETTEXTLENGTH, 0, 0)
If _LENGTH > 0 Then
i.Enabled = True
Else
i.Enabled = False
End If
End Select
End Select
Next
End Sub
''' <summary>
''' 元に戻す
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _undo(sender As System.Object, e As System.EventArgs)
If Class_API.SendMessage(_RT, EM_CANUNDO, 0, 0) <> 0 Then
Class_API.SendMessage(_RT, EM_UNDO, 0, 0)
End If
End Sub
''' <summary>
''' やり直す
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _Redo(sender As System.Object, e As System.EventArgs)
If Class_API.SendMessage(_RT, EM_CANREDO, 0, 0) <> 0 Then
Class_API.SendMessage(_RT, EM_REDO, 0, 0)
End If
End Sub
''' <summary>
''' 切り取り
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _Cut(sender As System.Object, e As System.EventArgs)
Class_API.SendMessage(_RT, WM_CUT, 0, 0)
End Sub
''' <summary>
''' コピー
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _Copy(sender As System.Object, e As System.EventArgs)
Class_API.SendMessage(_RT, WM_COPY, 0, 0)
End Sub
''' <summary>
''' 貼り付け
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _Paste(sender As System.Object, e As System.EventArgs)
If Class_API.SendMessage(_RT, EM_CANPASTE, 0, 0) <> 0 Then
Class_API.SendMessage(_RT, WM_PASTE, 0, 0)
End If
End Sub
''' <summary>
''' 削除
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _Delete(sender As System.Object, e As System.EventArgs)
Class_API.SendMessage(_RT, WM_CLEAR, 0, 0)
End Sub
''' <summary>
''' すべて選択
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _SelectALL(sender As System.Object, e As System.EventArgs)
Dim _LENGTH As Integer = Class_API.SendMessage(_RT, WM_GETTEXTLENGTH, 0, 0)
Dim c As CHARRANGE
c.cpMin = 0
c.cpMax = _LENGTH
Class_API.SendMessage(_RT, EM_SETSEL, c.cpMin, c.cpMax)
End Sub
次に、
''' <summary>
''' 指定されたウインドウに、新しいメニューを割り当てます。
''' </summary>
''' <param name="hWnd">ウインドウのハンドル</param>
''' <param name="hMenu">メニューのハンドル</param>
''' <returns></returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function SetMenu(ByVal hWnd As IntPtr, ByVal hMenu As IntPtr) As IntPtr
End Function
このようにメニューをぶっこむSetMenu関数を宣言して、宣言してやります。
次に縦書きコントロール作成時に下記ノードを追加してやります。
_AVM = New AutoVerbMenu(_Handle)
Dim err_ As IntPtr = Class_API.SetMenu(_Handle, _AVM.Handle)
If err_ = 0 Then
MsgBox("メニュー作成に失敗しました", , "エラーダイアログ")
End If
これで十分かな? と思ってテストすると
Oh….
この方法では、コンテキストメニュープロパティに格納してやることはできないようです。
次に試した方法は、調べた結果、コンテキストメニューは「TrackPopupMenu関数」を使用して表示するようです。
宣言から
''' <summary>
''' 指定された位置にショートカットメニューを表示し、そのメニュー内のメニュー項目の選択状況を追跡します。ショートカットメニューは、画面内のどこにでも表示できます。
''' </summary>
''' <param name="hMenu">ショートカットメニューのハンドル</param>
''' <param name="uFlags">オプション</param>
''' <param name="x">水平位置</param>
''' <param name="y">垂直位置</param>
''' <param name="nReserved">予約済み、0を指定する</param>
''' <param name="hWnd">所有側ウインドウのハンドル</param>
''' <param name="preRect">無視される</param>
''' <returns></returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function TrackPopupMenu(ByVal hMenu As IntPtr, ByVal uFlags As UInteger, ByVal x As Integer, ByVal y As Integer, ByVal nReserved As Integer, ByVal hWnd As IntPtr, ByVal preRect As IntPtr) As IntPtr
End Function
次に、マウスの右クリックのイベントを取得してやる必要があります。
「TateControlProc」ないで、マウスイベントを取得します。
lParamから、X座標とY座標の値を取得する必要もあるので、取得するためのLOWORD関数及びHIWORD関数も合わせて作成します。
lParam
Select Case uMsg
Case API.Msg.WM.WM_RBUTTONUP
Dim x As Integer = Class_API.LOWORD(lParam)
Dim Y As Integer = Class_API.HIWORD(lParam)
Call _AVM.PopUp(x, Y)
End Select
Sub PopUp(ByVal x As Integer, ByVal y As Integer)
Dim err_ As IntPtr = Class_API.TrackPopupMenu(AVM.Handle, 0, x, y, 0, _RT, Nothing)
If err_ = 0 Then
MsgBox("コンテキストメニュー表示に失敗しました。", , "エラーダイアログ")
End If
End Sub
''' <summary>
''' 指定した値の下位の値を取得します。
''' </summary>
''' <param name="value">取得元の値</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function LOWORD(ByVal value As IntPtr) As Integer
Return CInt((value.ToInt32 And &HFFFF&))
End Function
''' <summary>
''' 指定した値の上位の値を取得します。
''' </summary>
''' <param name="value">取得元の値</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function HIWORD(ByVal value As IntPtr) As Integer
Return CInt((value.ToInt32 And &HFFFF000%)) \ &H10000%
End Function
さて、これでテストしてみると、
Oh…> orz
どうやら、.Netが提供しているContextMenuSplitは使えないようです。こうなると、API関数の「CreatePopupMenu関数」で作成してやらないといけないかもしれません。
ですが、どうにもそれをやりたくない、、、
そこで、はっと気が付きました。なら、UserControlの方のContextMenuSplitに割り当てればいいんじゃね? 重ねて表示しているわけだし。
さっそく「AVM」の宣言を「DIM」から「FRIEND」に変更し
_AVM = New AutoVerbMenu(_Handle)
Dim err_ As IntPtr = Class_API.SetMenu(_Handle, _AVM.Handle)
If err_ = 0 Then
MsgBox("メニュー作成に失敗しました", , "エラーダイアログ")
End If
上を下に書き換えてみましたところ
_AVM = New AutoVerbMenu(_Handle)
sender.ContextMenuStrip = _AVM.AVM
このように、カーソル位置のそばに表示されました。
ただ、範囲選択の件だけうまく動作していないので修正してやる必要があります。
原因は簡単で、情報を格納する戻り値である「wParam」と「lParam」の宣言が「ByVal」になっているためです。これを「ByRef」に直してやる必要があります。
「SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr)」
しかし、単純に直せばいいというわけでもないので、あたらなメッセージ送信関数を準備することにしました。
''' <summary>
''' メッセージの送信
''' </summary>
''' <param name="hWnd">送信先ウインドウハンドル</param>
''' <param name="Msg">送信するメッセージ</param>
''' <param name="wParam">最初のパラメータ</param>
''' <param name="lParam">二番目のパラメータ</param>
''' <returns></returns>
''' <remarks></remarks>
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True, CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
Public Shared Function SendMessageA(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByRef wParam As IntPtr, ByRef lParam As IntPtr) As IntPtr
End Function
つぎに、先ほどのメッセージ送信時の宣言を
「Class_API.SendMessageA(_RT, EM_GETSEL, c.cpMin, c.cpMax)」に修正。
すべて選択も、
この通り、問題なく選択することができました。
なお、上記のコードではポップアップ時における「削除」の制御を忘れていますので、付け加えてやる必要があります。あしからず。
一般的なコード(調整前)はここまでで、これでは「コピー」と「貼り付け」において問題が発生しています。
それは次回で。
0 件のコメント:
コメントを投稿