2014年8月25日月曜日

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



前回において、コンテキストメニューのウインドウとプロパティと動作は終了となります。
基本的にユーザーコントロールのプロパティに依存する形でしょう。

しかし、次のプロパティの設定の前に、重大な問題が残っています。これを今回の課題としたいと。


問題となるのが「コピー」と「貼り付け」です。
このように、貼り付け時にコピー元がフォント情報を持っていると、それごと張り付けられてしまいます。
どこが問題なのか?というと、リッチテキストボックスとして使用する分にも問題ありませんが、通常のテキストボックスとして使用する場合、この動作は誠によろしくないのが実態としてあります。
これは貼り付けだけでなく、縦型テキストボックスを他のウインドウに張り付ける時も同様です。

問題はどのように操作するか?なんですよね。
クリップボードのデータを直接操作するか、クリップボードのデータはそのままで、COPY又は貼り付け時の処理自体に手を加えるか。
今回は後者で考えていきたいと思います。

まずは、Ctrl+V又はCtrl+Cのイベントの取得から。
Select Case uMsg
    Case WM_COPY
If Copy() Then
End If
 End Select
''' <summary>
''' テキストボックスのモードを設定及び取得します。
''' </summary>
''' <value>リッチテキストボックスモードの場合はTrue。テキストボックスモードの場合はFalse</value>
''' <returns></returns>
''' <remarks></remarks>
Property RichMode As Boolean = False
''' <summary>
''' コントロールが押されているかどうかの判別用。Trueで押されている。Falseで押されていない。
''' </summary>
''' <remarks></remarks>
Dim _ControlKey As Boolean = False
''' <summary>
''' Ctrl+C or Vの取得のための関数
''' </summary>
''' <param name="uMsg">メッセージ</param>
''' <param name="wParam">パラメータ</param>
''' <returns>True:方向キーの変更を実施 False:方向キーの変更を未実施</returns>
''' <remarks></remarks>
Private Function KeyCheckCopyPaste(ByVal uMsg As IntPtr, ByRef wParam As IntPtr) As Boolean
    'リッチテキストモードの場合は何もしない。
    If RichMode Then Return True

    Select Case wParam
        Case Windows.Forms.Keys.ControlKey
            Select Case uMsg
                Case WM_KEYUP
                    _ControlKey = False
                Case WM_KEYDOWN
                    _ControlKey = True
            End Select
    End Select
    Return True
End Function

こういう感じのコード書いたのですが、いろいろよくわからない。
次に、動作の順序を確認する必要があるんだろうと判断し、デバックの為

Private Function KeyCheckCopyPaste(ByVal uMsg As IntPtr, ByRef wParam As IntPtr) As Boolean
    'リッチテキストモードの場合は何もしない。
    If RichMode Then Return True

    Select Case wParam
        Case Windows.Forms.Keys.ControlKey
            Select Case uMsg
                Case WM_KEYUP
                    _ControlKey = False
                Case WM_KEYDOWN
                    _ControlKey = True
            End Select
        Case Windows.Forms.Keys.V, Windows.Forms.Keys.C
            If _ControlKey AndAlso uMsg = WM_KEYDOWN Then
                Return False
            End If
    End Select
    Return True
End Function
TateControlProc」内を下のように記載。

If KeyCheck(uMsg, wParam) = False Then
    If KeyCheckCopyPaste(uMsg, wParam) = False Then
        'テキストモードであり、コピーまたは貼り付けが選択されています
        If wParam = Windows.Forms.Keys.V Then
            Debug.Print(count & ":Ctrl+V")
            Dim Te As String = Text
            Text &= count & ":Ctrl+V" & vbCrLf
        Else
            Debug.Print(count & ":Ctrl+c")
        End If
        count += 1
    End If
End If
Select Case uMsg
    Case WM_COPY
        If _ControlKey Then
            Debug.Print(count & ":WM_COPY")
            count += 1
        End If
End Select
すると下記のように、貼り付けられた後にメッセージを拾っています。

なので考え方を変えます。
ユーザーコントロール内にて、Ctrl+V及びCtrl+Cを無効すると同時に、それぞれの処理プロシージャを実行する形に変えます。
''' <summary>
''' ショートカット制御用 Ctrl+C及びctrl+Vの無効及びコピー、貼り付け処理の実施
''' </summary>
''' <param name="msg"></param>
''' <param name="keyData"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
    If (keyData And Keys.Control) = Keys.Control AndAlso c IsNot Nothing AndAlso RichMode = False Then
        Select Case (keyData And Keys.KeyCode)
            Case Keys.V
                Call c.Paste()
                Return True
            Case Keys.C
                Call c.Copy()
                Return True
        End Select
    End If
    Return MyBase.ProcessCmdKey(msg, keyData)
End Function

''' <summary>
''' 選択されている範囲をコピーする。(テキストボックスモードのみに使用)コピー処理に成功した場合はTrueを、必要ない場合はFalseを返す。
''' </summary>
''' <remarks></remarks>
Public Function Copy() As Boolean
    '選択されているテキストを取得し、クリップボードに張り付ける。
    'コピーする文字列の有無を確認。
    Dim c As AutoVerbMenu.CHARRANGE
    Class_API.SendMessageA(_Handle, AutoVerbMenu.EM_GETSEL, c.cpMin, c.cpMax)
    'コピーする文字列がない場合は処理を終了する。
    If c.cpMax = c.cpMin Then Return False
    Dim buf As New System.Text.StringBuilder
    buf.Length = 32767
    Class_API.SendMessage(_Handle, AutoVerbMenu.EM_GETSELTEXT, 0, buf)
    If buf.ToString.Length > 0 Then
        Debug.Print(buf.ToString)
        Clipboard.SetText(buf.ToString)
        Return True
    Else
        Return False
    End If
End Function
''' <summary>
''' クリップボードのデータをボックスに張り付ける(テキストボックスモードのみに使用)
''' </summary>
''' <remarks></remarks>
Public Sub Paste()
    Dim temp As IDataObject = Clipboard.GetDataObject
    If temp.GetDataPresent(DataFormats.Text) = False Then Exit Sub
    Dim str As String = temp.GetData(DataFormats.Text, True)
    If str Is Nothing OrElse str.Length = 0 Then Exit Sub
    Class_API.SendMessage(_Handle, AutoVerbMenu.EM_REPLACESEL, 0, New System.Text.StringBuilder(str))
End Sub

これで、コピーアンドペースト時に含まれるフォント情報を除外した形で貼り付けとコピーを行うことができるようになりました。
後はコンテキストメニューのボタンをクリックしたときも、テキストボックスモードの時に上記のプロシージャを呼び出すようにしてやれば完了となります。

Ctrl+Paste
コンテキストメニューからの貼り付け時

最後に、このコンテキストメニューについては、作成時に下記の感じで自動追加する形にしていますが、プロパティで設定できるようにします。
''' <summary>
''' コントロールに追加する
''' </summary>
''' <param name="sender"></param>
''' <remarks></remarks>
Public Sub ADD(ByRef sender As Control)
    If IntInstance(sender) = False Then Exit Sub
    lpord = Class_API.SetWindowLong(_Handle, Class_API.GWL_WNDPROC, AddressOf TateControlProc)
    'mlpord = Class_API.SetWindowLong(_NowParent, Class_API.GWL_WNDPROC, AddressOf MainFormProc)
    AddHandler sender.SizeChanged, AddressOf ReSetWindow
    AddHandler sender.Move, AddressOf ReSetWindow
'コンテキストメニューの設定
    _AVM = New AutoVerbMenu(_Handle)
    sender.ContextMenuStrip = _AVM.AVM
    _AVM.TateClass = Me
End Sub
これを下記の通りに直す。
''' <summary>
''' コントロールに追加する
''' </summary>
''' <param name="sender"></param>
''' <remarks></remarks>
Public Sub ADD(ByRef sender As Control)
    If IntInstance(sender) = False Then Exit Sub
    lpord = Class_API.SetWindowLong(_Handle, Class_API.GWL_WNDPROC, AddressOf TateControlProc)
    'mlpord = Class_API.SetWindowLong(_NowParent, Class_API.GWL_WNDPROC, AddressOf MainFormProc)
    AddHandler sender.SizeChanged, AddressOf ReSetWindow
    AddHandler sender.Move, AddressOf ReSetWindow
    If _AutoVerbMenu Then AutoVerbMenu = True
End Sub

''' <summary>
''' 標準コンテキストメニュー設定用。
''' </summary>
''' <remarks></remarks>
Dim _AutoVerbMenu As Boolean = False
''' <summary>
''' 右クリック時にコンテキストメニューが自動で表示されるかどうを設定します。表示されるコンテキストメニューは標準のものです。
''' </summary>
''' <value>独自に設定する場合はFalse。標準コンテキストメニューを使用する場合はTrue</value>
''' <returns></returns>
''' <remarks></remarks>
Property AutoVerbMenu As Boolean
    Get
        Return _AutoVerbMenu
    End Get
    Set(value As Boolean)
        _AutoVerbMenu = value
        If UC Is Nothing Then Exit Property
        If UC.ContextMenuStrip IsNot Nothing Then
            UC.ContextMenuStrip.Close()
        End If
        If value = True Then
            'コンテキストメニューの設定
            Dim _AVM As new AutoVerbMenuSplit(_Handle)
            UC.ContextMenuStrip = _AVM.AVM
            _AVM.TateClass = Me
        End If
    End Set
End Property

これでAutoVerbMenuを新たに実装した形となりました。
プロパティ名
説明
AutoVerbMenu
ユーザーがコントロールを右クリックしたときに表示されるショートカットメニューです。

ContextMenuStripプロパティについては、ユーザーコントロールに依存する形となります。
プロパティ名
説明
ContextMenuStrip
ユーザーがコントロールを右クリックしたときに表示されるショートカットメニューです。
とはいえ、テキストボックス内の文字列などを操作するわけですから、それに合わせた形でプロシージャを一通り用意してやる必要はあるだろうなぁと。

本日はここまで。
参考までに、今までのテストでやってたプロジェクトのデータを添付します。まぁ、こんなものの需要なんてないでしょうがw

テストファイルリンク(テスト1)

0 件のコメント:

コメントを投稿