C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[9]: オリジナルTextBoxにカスタムコントロール


(過去ログ 128 を表示中)

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■75841 / inTopicNo.1)  オリジナルTextBoxにカスタムコントロールをあてる方法
  
□投稿者/ ni (4回)-(2015/05/08(Fri) 09:25:53)

分類:[.NET 全般] 

2015/05/08(Fri) 10:01:46 編集(投稿者)
2015/05/08(Fri) 09:53:49 編集(投稿者)

Windows7Pro(64bit)、VSE2013を使用、NET Framework 4 です。

お世話になっております。
別で質問していたのですが、
http://bbs.wankuma.com/index.cgi?mode=al2&namber=75745
手段が変わり、他の問題が出てきましたので、こちらで質問致します。
WEB上で皆さんの知識を頼りにフォーム作成している初心者です、どうぞお手柔らかに。

フォームにテキストボックスがいくつかあり、
そのうちのいくつかに入力文字制限があるため
カスタムコントロール(以下、カスタムTextBox)をURL先を参照して作成しました。
http://jeanne.wankuma.com/tips/vb.net/textbox/permitchars.html
http://www.vbstation.net/tips/inherits_control.htm
この
・カスタムTextBox

・オリジナルのTextBox
の右クリックコンテキストメニュー(等々)のイベントを
一括したいと思っています。

そこで調べたところ、
http://dobon.net/vb/dotnet/beginner/explicit.html
●自作クラス(カスタムTextBox)を別の型(オリジナルTextBox)にキャストする、
この手法で正しいとは思い、
また複数の手法のうち
【暗黙の型変換と明示的な型変換、拡大変換と縮小変換】
が一番適当に思うのですが、オリジナルTextBoxクラスへの
アクセス方法がわかりません。

ご教授いただけますよう、よろしくお願い致します。
引用返信 編集キー/
■75843 / inTopicNo.2)  Re[1]: オリジナルTextBoxにカスタムコントロールをあてる方法
□投稿者/ 魔界の仮面弁士 (311回)-(2015/05/08(Fri) 10:48:50)
No75841 (ni さん) に返信
> カスタムコントロール(以下、カスタムTextBox)をURL先を参照して作成しました。
> http://jeanne.wankuma.com/tips/vb.net/textbox/permitchars.html
> http://www.vbstation.net/tips/inherits_control.htm
TextBox を継承したクラスを自作されたのですね。


> そのうちのいくつかに入力文字制限があるため
どこまで対応するのかにもよりますが、入力手段には
 ・キーボードからの直接入力
 ・クリップボードからの貼り付け(WM_PASTE)
   ・Ctrl+V
   ・Shift+Insert
   ・コンテキストメニュー
   ・多機能マウスやスタイラスボタンなどによる「貼り付け」機能
 ・手書文字認識入力
 ・WM_SETTEXT による直代入
などがありますね。あとは音声入力とか?


> の右クリックコンテキストメニュー(等々)のイベントを
> 一括したいと思っています。
すみません、質問の意図が良く分かりませんでした。

自作クラスで新たなイベントを定義したわけではないのですよね。

イベント発生時の応答処理を実装するのは、呼び出し側(Form)の役目だと思いますが、
カスタムコントロールであっても、ベースクラスのイベントはそのまま利用可能なので、
利用側としてみれば
> ・カスタムTextBox
> ・オリジナルのTextBox
を区別する必要は無いように思うのですが…。

いざとなれば Reflection という力技もあったりもしますが、
やりたいことがイメージできていないので、具体的なアドバイスはできかねます。


> ●自作クラス(カスタムTextBox)を別の型(オリジナルTextBox)にキャストする、
やりたい事が分かっていないので回答しにくいですが、
それは今回のケースで使うものでは無いと思います。

そもそも、今回のように 継承関係にあるものに対して
自分で型変換演算子を実装する必要はありません。
わざわざ Operator CType を用意したりせずとも型変換できますから。


> この手法で正しいとは思うのですが、オリジナルTextBoxクラスへの
> アクセス方法がわかりません。
もしかして、イベント処理を行いたいのは Form 上ではなく、
カスタムコントロール上で、ベースとなる TextBox のイベントを
利用したいという意味でしょうか。

だとしたら、カスタムコントロールからは、元のコントロールのイベントを
利用するのではなく、On何某 のメソッドをオーバーライドした方が良いでしょう。
引用返信 編集キー/
■75844 / inTopicNo.3)  Re[2]: オリジナルTextBoxにカスタムコントロール
□投稿者/ ni (5回)-(2015/05/08(Fri) 12:15:29)
2015/05/08(Fri) 13:42:56 編集(投稿者)
2015/05/08(Fri) 13:39:48 編集(投稿者)

No75843 (魔界の仮面弁士 さん) に返信
魔界の仮面弁士 さん、早速のご連絡ありがとうございます。

_____________________________
_____________________________
フォームに
●オリジナルテキストボックス
●カスタムテキストボックス(入力文字制限)
(入力手段には
・キーボードからの直接入力
・クリップボードからの貼り付け(WM_PASTE)
の2種)
があります。
_____________________________
_____________________________


> そもそも、今回のように 継承関係にあるものに対して
> 自分で型変換演算子を実装する必要はありません。
> わざわざ Operator CType を用意したりせずとも型変換できますから。
そうも思います、オリジナルのTextBoxとして一括処理したいだけですので。
なぜ継承しているのに、イベントが正しく発生しないのか今現在わかりません。
どこかを変更する必要があるのであれば、その方法をご教授いただけますでしょうか。


>
>>この手法で正しいとは思うのですが、オリジナルTextBoxクラスへの
>>アクセス方法がわかりません。
> もしかして、イベント処理を行いたいのは Form 上ではなく、
> カスタムコントロール上で、ベースとなる TextBox のイベントを
> 利用したいという意味でしょうか。
>
> だとしたら、カスタムコントロールからは、元のコントロールのイベントを
> 利用するのではなく、On何某 のメソッドをオーバーライドした方が良いでしょう。

_____________________________
_____________________________
最終目的は、
【複数及び先に挙げた2種類のテキストボックスを
一括で右クリックコンテキストメニューイベント処理したい】
です。
コンテキストメニュー内容は
・元に戻す
・切り取り
・コピー
・貼り付け
・削除
・すべて選択
と一般的なものです。
_____________________________
_____________________________

その上で現段階の問題は、
魔界の仮面弁士 さんに否定されましたが
Operatorでカスタムテキストボックスに追記
(↓↓↓ここから が該当部)
Public Sub New()
MyBase.New()
InitializeComponent()
Me.PermitChars = New Char() {}
End Sub
↓↓↓ここから
'MyTextBox型からTextBox型への変換
Public Shared Operator +(ByVal val As MyTextBox) As TextBox
Return New TextBox()
End Operator
してみると、両テキストボックスを
コンテキストメニューのそれぞれのイベントで処理できるようになりました。
(ですが、先に記したようにそんなことしなくてもいい様であれば、
その方法をご教授いただけますと幸いです。)

ですが右クリックでコンテキストメニュー動作と
Ctrl+Z、X、C、V、DeleteキーやBackSpaseキーと
動作が合致してきません。
コンテキストメニューアイテムのそれぞれの
ShortcutKeysプロパティにはCtrl+Z、X、C、Vと振っています。
(今はキー入力Ctrl+Vで張り付けると、『元に戻す(Ctrl+Z)』で.CanUndo=Falseで
Undoされません。)
これは、
http://bbs.wankuma.com/index.cgi?mode=al2&namber=75745
Undoの内容のチェック
での質問内容です。
この点でも同じく何か対策をお教えいただけませんでしょうか。


ばらばらになって、掲示板の使い方がいまいちなのかもかも知れませんが、
(そのいい解決方法もあるようでしたら、
投稿を自分で整理したりはできないようなので、
あちらは閉めた方が(解決済みにする)方がいいのでしょうか。)
お手数お掛け致します、どうぞ引き続き多くのご意見いただけますよう、
よろしくお願い致します。

■■■■■以下、追記します。■■■■■
http://dobon.net/vb/bbs/log3-49/29192.html
こちらで、なんとなく意味が分かりました。
『プログラムからの入力』だから、
Undoバッファで反応しないという事の様ですね。
(知ってはいたものの、なかなか各々の知識がつながらず、恐縮です。)

そこで、追加で尋ねたいことは、
URL先でのヴァン さんの投稿で
・『undoって自分で実装するんじゃ』
・『文字列をバッファに格納して行って、Undoすれば』
とあります。
この方法をお教えください。
Undoバッファの概念がいまいちわかっておりませんが、
読むとおおよそどういう処理をしていくかがわかりますので。
何か例を挙げていただけますと幸いです。
(向こう
http://bbs.wankuma.com/index.cgi?mode=al2&namber=75745
につなげたいですがどうすればいいでしょうか。)

お手数お掛け致します、どうぞよろしくお願い致します。
引用返信 編集キー/
■75845 / inTopicNo.4)  Re[3]: オリジナルTextBoxにカスタムコントロールをあてる方法
□投稿者/ 魔界の仮面弁士 (312回)-(2015/05/08(Fri) 14:30:45)
No75843 (魔界の仮面弁士 さん) に返信
> なぜ継承しているのに、イベントが正しく発生しないのか今現在わかりません。
現在、どういう実装を行っているのかを明らかにするために、
「現象を再現可能な最低限のコード」を提示できないでしょうか。

試しに継承させてみましたが、少なくとも下記のコードでは
標準イベントが問題なく発生しているようですが…。

Public Class Form1
 Private WithEvents TextBox0 As TextBoxBase
 Private WithEvents TextBox1 As TextBox
 Private WithEvents TextBox2 As MyTextBox

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  Me.TextBox0 = New TextBox() With {.Text = "0", .Dock = DockStyle.Top}
  Me.TextBox1 = New TextBox() With {.Text = "1", .Dock = DockStyle.Top}
  Me.TextBox2 = New MyTextBox() With {.Text = "2", .Dock = DockStyle.Top}

  Me.Controls.Add(Me.TextBox2)
  Me.Controls.Add(Me.TextBox1)
  Me.Controls.Add(Me.TextBox0)
 End Sub

 Private Sub TextBox0_TextChanged(sender As Object, e As EventArgs) Handles TextBox0.TextChanged
  Me.TextBox0.BackColor = Color.FromArgb(&HFF000000 Or New Random().Next())
 End Sub

 Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
  Me.TextBox1.BackColor = Color.FromArgb(&HFF000000 Or New Random().Next())
 End Sub

 Private Sub TextBox2_TextChanged(sender As Object, e As EventArgs) Handles TextBox2.TextChanged
  Me.TextBox2.BackColor = Color.FromArgb(&HFF000000 Or New Random().Next())
 End Sub

 'とりあえず単純に継承させてみただけ
 Public Class MyTextBox
  Inherits TextBox
 End Class
End Class



> 【複数及び先に挙げた2種類のテキストボックスを
> 一括で右クリックコンテキストメニューイベント処理したい】
この部分について、状況をもう少し詳しく教えていただけないでしょうか。

標準のコンテキストメニューに対しての操作を、
NativeWindow クラスまたは TextBox クラスの
WndProc メソッドで拾っている、ということでしょうか。

それとも、ContextMenuStrip プロパティを
差し替えている、ということでしょうか。


後者の場合、その差し替え作業をどのクラスで担当させるのかにもよりますが、
たとえば Form 側で請け負うなら、Load イベントなどで
 For Each txt In Me.Controls.OfType(Of TextBox)()
  txt.ContextMenuStrip = Me.ContextMenuStrip1
 Next
などと書くことができるかと思います。これならまとめて処理できます。


Form ではなく、Panel などの下の TextBox まで
再帰処理させるのであればこんな感じですかね。

 'For Each txt In Me.Controls.OfType(Of TextBox)()
 For Each txt In GetAllControls(Me).OfType(Of TextBox)()
  txt.ContextMenuStrip = Me.ContextMenuStrip1
 Next
Private Function GetAllControls(parent As Control) As IEnumerable(Of Control)
 Dim controls = parent.Controls.Cast(Of Control)()
 Return controls.SelectMany(Function(c) GetAllControls(c)).Concat(controls)
End Function

なお、コンテキストメニュー側で、どのコントロールが右クリックされたのかを
調べたいのであれば、ContextMenuStrip1.SourceControl を見ることができます。
(ToolStripMenuItem の場合は、sender の Owner が ContextMenuStrip です)

ただし SourceControl は Nothing を返す場合もありますので、そのときには
以前の SourceControl の内容を用いたり、ActiveControl で代用するなどの
処置が必要になってきます。
http://dobon.net/vb/dotnet/control/cmsourcecontrol.html



> 'MyTextBox型からTextBox型への変換
> Public Shared Operator +(ByVal val As MyTextBox) As TextBox
んん? これは型変換演算子ではなく、加算演算子ですよね。


> Return New TextBox()
フォーム上のテキストボックスを返すのではなく、
新たに用意した(フォーム上に無い)TextBox を
返却させているようですね。

何のために、新たな TextBox を New しているのか読み取れませんでしたが、
ここで生成されたインスタンスの管理は、どのように行われているのでしょうか?



> コンテキストメニューアイテムのそれぞれの
> ShortcutKeysプロパティにはCtrl+Z、X、C、Vと振っています。

標準割当のキー操作と競合しないよう、各 TextBox の ShortcutsEnabled を
False にしてありますか?
引用返信 編集キー/
■75846 / inTopicNo.5)  Re[3]: オリジナルTextBoxにカスタムコントロール
□投稿者/ 魔界の仮面弁士 (313回)-(2015/05/08(Fri) 14:46:23)
2015/05/08(Fri) 14:48:32 編集(投稿者)

No75844 (ni さん) に返信
> URL先でのヴァン さんの投稿で
> ・『undoって自分で実装するんじゃ』
> ・『文字列をバッファに格納して行って、Undoすれば』
> とあります。

手間をかければ、回数上限の無い Undo や、(RichTextBox のように)Redo 操作も作りこめるでしょう。

ただし作りこむ場合は、先に Azulean さんも書かれていたように、
『どこまでを1操作と見なすか』が重要になってきますね。


> この方法をお教えください。

「Memento パターン」で実装するのが定番かと思います。

アルゴリズムの話なので、VB や TextBox などに限定して調べる必要は
ないと思いますが、手っ取り早く VB での実装例をみたいのであれば、
結城さんの Java 実装を、中西さんが VB 化したものや
今は亡き なおこ(・∀・) さんの実装例などがあります。
http://hccweb1.bai.ne.jp/tsune-1/VisualBasic/memento.html
http://naoko.wankuma.com/designpatterns/designpatterns_0018_memento.html
引用返信 編集キー/
■75847 / inTopicNo.6)  Re[4]: オリジナルTextBoxにカスタムコントロール
□投稿者/ 魔界の仮面弁士 (314回)-(2015/05/08(Fri) 16:33:37)
No75846 (魔界の仮面弁士) に追記
> ただし作りこむ場合は、先に Azulean さんも書かれていたように、
> 『どこまでを1操作と見なすか』が重要になってきますね。

サンプルを書いてみました。
「TextChanged が発生したとき」を1操作としたシンプルな実装にしています。


Public Class Form1

 Private UndoBuffer As New Stack(Of String)()

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  Me.UndoBuffer.Push(Me.TextBox1.Text)
 End Sub

 Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
  '変更結果を保存する(前回と同じ内容なら無視)
  If Not Me.UndoBuffer.Any() Then
   If Me.UndoBuffer.Peek() = Me.TextBox1.Text Then
    Return
   End If
  End If
  Me.UndoBuffer.Push(Me.TextBox1.Text)
 End Sub

 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  If Me.UndoBuffer.Any() Then
   '「現在のテキスト」を読み捨てる
   Me.UndoBuffer.Pop()
  End If
  If Me.UndoBuffer.Any() Then
   '「前回のテキスト」を設定する
   Me.TextBox1.Text = Me.UndoBuffer.Pop()
  End If
 End Sub

End Class
引用返信 編集キー/
■75882 / inTopicNo.7)  Re[5]: オリジナルTextBoxにカスタムコントロール
□投稿者/ ni (6回)-(2015/05/14(Thu) 12:02:16)
2015/05/14(Thu) 13:41:11 編集(投稿者)

魔界の仮面弁士 さん、ありがとうございます。

理解とその実行と結果を確認するのに時間がかかり、返信が遅れましたが、
魔界の仮面弁士 さんよりのご意見、サンプルにより知識が広げられ、
いろんな手段がある事がわかりました。
何回お礼を言っても足りないくらいうれしく思っております。
ご教授いただいた内容から、書いていただいたコードを基に
折り返し回答及び質問致します。
また、簡単にお答えいただければ非常に幸いです。

@
おおよそ
■75845 / inTopicNo.4
で書いていただいたようなコードになっています。

やりたいことは
・半角数字のみの制限テキストボックス(MyTextBox)
・場合により半角英記号のみの制限テキストボックス(MyTextBox)
→MyTextBoxクラス内にはURL先の文字制限が書かれています。
下のURL先の方法でForm1_Load時にそれぞれのテキストボックス制限文字が
定義されています。
http://jeanne.wankuma.com/tips/vb.net/textbox/permitchars.html
・普通のテキストボックス(System.Windows.Forms.TextBox)
________________________________________以上の3つ

を同時にイベント処理したい、
各々の必要な他の処理は外で処理したい、
という事です。
(この返事で一切答えに間に合ってない場合は。恐らく私の浅はかさ無知が
問題ですので、その程度の知識とお捉えください。)
魔界の仮面弁士 さんの例ではTextBox0、TextBox1、TextBox2、
と3つ分のイベントをバラバラで回していらっしゃいますが、
●TextBoxとMyTextBoxを(同じ処理内容の為)一括で処理したいときは、

>  Private WithEvents TextBox0 As TextBoxBase
>  Private Sub TextBox0_TextChanged(sender As Object, e As EventArgs) Handles TextBox0.TextChanged
>   Me.TextBox0.BackColor = Color.FromArgb(&HFF000000 Or New Random().Next())
>  End Sub

この【Private Sub TextBox0_TextChanged】の中に書けばいいという事でいいのでしょうか。
((例を書いていただくまで"TextBoxBase"の概念を理解できていませんでしたが、
ここで処理すると、全部意図するように回っているので、そうなんだなーと…。。。)
理解が正しいという事であれば表題の件は、解決しました。
ありがとうございます。
________________________________________
________________________________________

A
(しかしながら、
> 標準のコンテキストメニューに対しての操作を、
> NativeWindow クラスまたは TextBox クラスの
> WndProc メソッドで拾っている、ということでしょうか。
>
> それとも、ContextMenuStrip プロパティを
> 差し替えている、ということでしょうか。
の辺りの意味が解りませんでした。
For Each 部はわかります。)

(もう一つ問題が。
ご提案いただいたTextBoxBaseを取り入れて後、なぜかまったく
何のときも従来のUndoを格納しなくなりました。
TextBoxBaseでの処理が何か関係あるのでしょうか。
ですのでなおさらスタックを用意する必要が出てきました。)
________________________________________
________________________________________

B
> サンプルを書いてみました。
> 「TextChanged が発生したとき」を1操作としたシンプルな実装にしています。
Stackという事はどんどん積みあがっていくイメージですが
(調べたところ)、
通常のメモ帳.txtと同じように、
『Ctrl+Zコマンドで1つ前に戻るだけ』の動作を、元に戻すでぐるぐる回したい
と考えております。
(Stackで作ってみたところ、Stackでも.Clearや.TrimExcessで空にする事も
学習させていただきました。
使える事は使えますのでせっかく書いたので今はそのコードのままです。)

例)
テキストボックスに、
[AAA]
→(元に戻す(Ctrl+Z))
[BBB]
→(元に戻す(Ctrl+Z))
[AAA]
(…元に戻す(Ctrl+Z)でこの繰り返し。)

常に、最後のUndoが戻ってくる、右クリックコンテキストメニュー内容は
本当にそっくりそのままメモ帳仕様を希望しております。
●1つの場合はわざわざStackを使用しなくてもいいと考えてよろしいでしょうか。

その場合、メモ帳では、アルファベット入力の際ある程度の単位で【元に戻す】
が行われていますが、その『区切り』が何か私にはわからないのですが、
ご存じでしょうか。
(その『区切り』で文字を取得しておいて、『元に戻す』実行時その文字を返す、と言う動き。)
変換が必要な日本語入力の場合は
『MouseDown』イベント
『Enterキー』
『BackSpaceキー』
『Delete』
判断のように思います。
●半角英数入力の場合もそういったキーのように感じますが、いかがでしょうか。
________________________________________
________________________________________

C
さらに追加の質問で申し訳ないのですが、
先の
テキストボックスでいくつかの文字列がある時、フォーカスがなくとも
【インターネットブラウザの様に右クリックでカーソルがポイント位置に移動する】
と言う動きをMauseDownイベント内に書いているのですが、移動してくれません。
●原因がわかるでしょうか。
Public Class Form1

Private WithEvents TextBox0 As TextBoxBase
Friend Shared sp As System.Drawing.Point
Private Const MOUSEEVENTF_LEFTDOWN = &H2
Friend Sub TextBox0_MouseDown(sender As Object, e As MouseEventArgs) _
Handles TextBox1.MouseDown, TextBox2.MouseDown
sp = System.Windows.Forms.Cursor.Position
If (Control.MouseButtons And MouseButtons.Right) = MouseButtons.Right Then
TBBase.Focus()
NativeMethods.mouse_event(MOUSEEVENTF_LEFTDOWN, sp)←←←ココ←←←
End If
End Sub

End Class

Friend NotInheritable Class NativeMethods

'マウスを擬似的に動作させる
<System.Runtime.InteropServices.DllImport("USER32.DLL")> _
Friend Shared Sub mouse_event(ByVal dwFlags As Integer, ByVal pt As System.Drawing.Point)
End Sub

End Class

また、このカーソル移動が先に記した、TextChangedイベントが発生しない理由(ユーザー入力でない)なのですが、
●この場合、右クリックメニュー(ContextMenuStrip)イベント時
・RemoveHandlerで従来のTextChangedイベントに回らない様にし、
・AddhandlerでToolStripMenuItem様のTextChangedイベントを作ってあげればいいのでしょうか。

進行形で、試用してない質問等もありますが、よろしければご意見いただけますと幸いです。

以上、よろしくお願い致します。
引用返信 編集キー/
■75887 / inTopicNo.8)  Re[6]: オリジナルTextBoxにカスタムコントロール
□投稿者/ 魔界の仮面弁士 (319回)-(2015/05/14(Thu) 15:12:55)
No75882 (ni さん) に返信
> を同時にイベント処理したい、

MyTextBox は、System.Windows.Forms.TextBox を継承して作られています。

TextBox や RichTextBox は System.Windows.Forms.TextBoxBase を継承していますし、
TextBox は System.Windows.Forms.Control を継承しています。
そして Control は Object から継承されています。

…ここまでは認識されているでしょうか?


アプリ作成時に、まずは Form1 クラスが用意されるかと思いますが、
これも、System.Windows.Forms.Form クラスを継承したものですし、
その Form クラスは、System.Windows.Forms.Control を継承しています。


そして Control クラスには、TextChanged イベントや MouseDown イベントや Click イベントが
用意されているため、Control の派生クラスである Form や Form1、TextBox や MyTextBox も
同様にこれらのイベントを利用できるはずです;本来は。

にもかかわらず
>> なぜ継承しているのに、イベントが正しく発生しないのか今現在わかりません。
という状態になるのであれば、イベントが発生しないというよりは、
コーディング上の問題により、
 ・自作クラス MyTextBox が、イベントを発生させていない
 ・利用側となる Form1 で、そのイベントを受け取っていない
のいずれかであろうと推察します。



> 魔界の仮面弁士 さんの例ではTextBox0、TextBox1、TextBox2、
> と3つ分のイベントをバラバラで回していらっしゃいますが、
一箇所で受け取りたいのであれば、
Private Sub TextBoxes_TextChanged(sender As Object, e As EventArgs) Handles TextBox0.TextChanged, TextBox1.TextChanged, TextBox2.TextChanged
のように、Handles 句で対象イベントを列挙する方法があります。

ただし、Handles 句を使う場合には、WithEvents 変数も必要になります。
コントロールが 10 個あるなら、WithEvents 変数も10個必要です。

あるいは、「AddHandler ステートメント」を用いるのも良いでしょう。
これなら、WithEvents 変数を用意せずとも、複数のイベントをまとめて処理できます。
解除する場合は、ご存知のように「RemoveHandler」です。


Public Class Form1
 Private Sub Test(sender As Object, e As EventArgs)
  MsgBox("クリック@" & sender.GetType().Name & "@" & sender.Name)
 End Sub

 Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
  AddHandler Me.TextBox1.Click, AddressOf Me.Test
  AddHandler Me.Label1.Click, AddressOf Me.Test
  AddHandler Me.ListBox1.Click, AddressOf Me.Test
  AddHandler Me.Click, AddressOf Me.Test
 End Sub
End Class


もし、
  AddHandler Me.TextBox1.Click, AddressOf Me.Test
  AddHandler Me.TextBox1.Click, AddressOf Me.Test
のように複数回に割り当てると、イベント通知も複数回発生するので注意して下さい。




>>標準のコンテキストメニューに対しての操作を、
>>NativeWindow クラスまたは TextBox クラスの
>>WndProc メソッドで拾っている、ということでしょうか。
>>
>>それとも、ContextMenuStrip プロパティを
>>差し替えている、ということでしょうか。
> の辺りの意味が解りませんでした。

分かりにくくてすみません。
TextBox のコンテキストメニューは、通常、OS で用意されたものが使われますよね。

「ContextMenuStrip プロパティを差し替える」というのは、
ツールボックスからContextMenuStrip をフォームに貼り、それを TextBox の
ContextMenuStrip プロパティに割り当てることで、標準のメニューではなく
自作のメニューにするということです。

そして「WndProc メソッド」の方は、じゃんぬさんの MyTextBox で使われている方法です。
『Protected Overrides Sub WndProc(ByRef m As Message)』の中で、
WM_PASTE メッセージを処理している部分がそれにあたります。


そして、継承せずに WndProc を処理する場合には、NativeWindow クラスを使えます。

たとえばこんな感じです。下記を実行すると、「コンテキストメニュー」や
「Ctrl+V」「Shift+Insert」を含むあらゆる貼り付け処理を事前に横取りできます。

--------------------------
Imports System.ComponentModel
Public Class Form1
  Private WithEvents sample As NativeWindowSample

  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    sample = New NativeWindowSample(Me.TextBox1)
  End Sub

  Private Sub sample_Pasting(sender As Object, e As CancelEventArgs) Handles sample.Pasting
    If MsgBox("貼り付けますか", vbQuestion Or vbOKCancel) = vbCancel Then
      e.Cancel = True
    End If
  End Sub

End Class

Public Class NativeWindowSample
  Inherits NativeWindow
  Private Owner As Form1
  Public Event Pasting As CancelEventHandler

  Public Sub New(owner As Control)
    MyBase.AssignHandle(owner.Handle)
  End Sub

  Protected Overrides Sub WndProc(ByRef m As Message)
    'Const WM_CUT = &H300
    'Const WM_COPY = &H301
    Const WM_PASTE = &H302
    'Const WM_CONTEXTMENU = &H7B

    Dim e As New CancelEventArgs(False)
    If m.Msg = WM_PASTE Then
      RaiseEvent Pasting(Me, e)
    End If
    If Not e.Cancel Then
      MyBase.WndProc(m)
    End If
  End Sub
End Class
--------------------------


> ご提案いただいたTextBoxBaseを取り入れて後、なぜかまったく
TextBoxBase は利用しないでください。(継承関係の参考として提示しただけです。すみません)
通常は、TextBox あるいは MyTextBox をそのまま利用すれば OK です。


> TextBoxBaseでの処理が何か関係あるのでしょうか。
関係しないと思います。WithEvents 変数の扱いを間違えていたりはしませんか?



> 常に、最後のUndoが戻ってくる、右クリックコンテキストメニュー内容は
> 本当にそっくりそのままメモ帳仕様を希望しております。
であれば無理に作りこまず、標準の Undo 動作を活かす方法を模索するのがよろしいかと。


> テキストボックスでいくつかの文字列がある時、フォーカスがなくとも
> 【インターネットブラウザの様に右クリックでカーソルがポイント位置に移動する】
コンテキストメニューを無効にしてみましたが、その場合、右クリックや中クリックでは、
フォーカスの移動が行われないようですね。

とりあえず、こんな感じで如何でしょう。
WndProc メソッドを下記のように実装してみて下さい。

 Select Case m.Msg
  Case WM_RBUTTONDOWN '&H204
   Dim m2 = Message.Create(m.HWnd, m.Msg, m.WParam, m.LParam)
   m.Msg = WM_LBUTTONDOWN '&H201
   MyBase.WndProc(m)
   MyBase.WndProc(m2)
   Return
  Case …
引用返信 編集キー/
■75891 / inTopicNo.9)  Re[7]: オリジナルTextBoxにカスタムコントロール
□投稿者/ ni (7回)-(2015/05/14(Thu) 16:30:08)
No75887 (魔界の仮面弁士 さん) に返信

早速のご返信ありがとうございます。

最近わかったことをお伝えします。
コードを修正しながら載せる.exeを起動し、『プロセスにアタッチ』させ、
コードの動きを確認しているのですが、
時々VSE2013の『エラー一覧』にカスタムコントロールのMyTextBoxが見当たらない、と言ったようながメッセージが出たり、
検証のために、一部コードを省略したにもかかわらず、そこにあるかのように動作する事があるのですが、
もしかすると、『偶然回ったり』、『時々エラーしたり』するのはそういう事なのでしょうか。

フォーム自体ではそんなに複雑な事はしていないのですが、元の.exeの呼び出し階層が複雑だからでしょうか、
VSE2013自体を再起動すると、そういう動きはしないのですが、そういう可能性はあるのでしょうか。

もう少し教えて頂いたことに基づいて、書いてみます。
引用返信 編集キー/
■75947 / inTopicNo.10)  Re[7]: オリジナルTextBoxにカスタムコントロール
□投稿者/ ni (8回)-(2015/05/20(Wed) 09:49:30)
2015/05/20(Wed) 10:06:56 編集(投稿者)

No75887 (魔界の仮面弁士 さん) に返信

度々の質問を失礼致します。
多くの言葉を頂き大変勉強させていただいております。
せっかくですのでもう少しお時間の許す限り、お付き合いいただければ幸いに存じます。

>TextBoxBase は利用しないでください。
の『利用しない』、この禁止の適用範囲がわかりません。
それではなぜTextBoxBase.TextChangedがあるのだろう、と思ってしまいます。
どの様なときにTextBoxBase.TextChangedを使うのでしょうか。

なぜそう思うのかは、
Public Class Form1
Private WithEvents TextBox0 As TextBoxBase
Private WithEvents TextBox1 As TextBox
Private WithEvents TextBox2 As MyTextBox
Enum EnableMode
StrNo = 0 '文字列無し
SelectNo = 1 '選択文字列無し
SelectAllTextBox = 2 '文字列全選択、TextBox
SelectAllMyTextBox = 3 '文字列全選択、MyTextBox
SelectSomeTextBox = 4 '文字列あり、選択有り、TextBox
SelectSomeTextBox = 5 '文字列あり、選択有り、MyTextBox
End Enum



↓↓↓↓↓↓↓↓↓↓↓↓ここから↓↓↓↓↓↓↓↓↓↓↓↓↓↓

Friend Sub AllTextBox_MouseDown(sender As Object, e As MouseEventArgs) Handles TextBox0.MouseDown
Dim sp sp As System.Drawing.Point= System.Windows.Forms.Cursor.Position
If (Control.MouseButtons And MouseButtons.Right) = MouseButtons.Right Then
ContextMenuStrip_FormYSKAMPROP.Show(sp)
If TextBox0.TextLength = 0 Then
MenuEnabled(EnableMode.StrNo, TextBox0)
ElseIf TexTBox0.SelectionLength = 0 Then
MenuEnabled(EnableMode.SelectNo, TextBox0)
ElseIf TextBox0.GetType.ToString = "ClassLibrary.TextBox" AndAlso TexTBox0.SelectionLength = TextBox0.TextLength Then
MenuEnabled(EnableMode.SelectAllTextBox, TextBox0)
ElseIf TextBox0.GetType.ToString = "ClassLibrary.MyTextBox" AndAlso TexTBox0.SelectionLength = TextBox0.TextLength Then
MenuEnabled(EnableMode.SelectAllMyTextBox, TextBox0)
ElseIf TextBox0.GetType.ToString = "ClassLibrary.TextBox" AndAlso TexTBox0.SelectionLength > 0 Then
MenuEnabled(EnableMode.SelectSomeTextBox, TextBox0)
ElseIf TextBox0.GetType.ToString = "ClassLibrary.MyTextBox" AndAlso TexTBox0.SelectionLength > 0 Then
MenuEnabled(EnableMode.SelectSomeMyTextBox, TextBox0)
End If
End if
End Sub
Private Sub MenuEnabled(ByVal strEnable As EnableMode, ByVal TextBox0 As TextBoxBase)

Select Case strEnable
Case EnableMode.StrNo
ToolStripMenuItem_CUT.Enabled = False
ToolStripMenuItem_COPY.Enabled = False
ToolStripMenuItem_DELETE.Enabled = False
ToolStripMenuItem_SELECTALL.Enabled = False
Case EnableMode.SelectNo



End Sub
↑↑↑↑↑↑↑↑↑↑↑↑ここまで↑↑↑↑↑↑↑↑↑↑↑↑↑↑
こうイベントを一括して回したいからです。
【こう言う使い方はいわゆる、『邪道』なのですか。】
(初めからこういう事をしていいのか?できるのか?という事を
聞きたかったのですが、様々な助言が新鮮かつ疑問に思う事もあり、
簡潔な質問にならずすみませんでした。
後、他者と初めて話題にする為、語彙力がなく説明方法がわかりませんでした、
すみません。)


______________________________________________________________________
______________________________________________________________________
>> コンテキストメニューアイテムのそれぞれの
>> ShortcutKeysプロパティにはCtrl+Z、X、C、Vと振っています。
>標準割当のキー操作と競合しないよう、各 TextBox の ShortcutsEnabled を
>False にしてありますか?
Falseでなかったので、
@それぞれのTextBox.ShortcutKeys = True だったところをFalseにし、
ToolStripMenuItem_UNDOREDO.ShortcutsEnabled = Ctrl+Z
ToolStripMenuItem_CUT.ShortcutKeys = Ctrl+X
ToolStripMenuItem_COPY.ShortcutKeys = Ctrl+C
ToolStripMenuItem_PASTE.ShortcutKeys = Ctrl+V
ToolStripMenuItem_DELETE.ShortcutKeys = DEL
ToolStripMenuItem_SELECTALL.ShortcutKeys = Ctrl+A
(ToolStripMenuItem_***.ShortcutKeysはこれまで通りです。)
としたところ、
ショートカットキー入力では、どのToolStripMenuItemイベントにも回らず、
反応しなくなりまして、
Aさらには.ShortcutsEnabled = False。と戻しても同様反応しなくなりました。

>標準の Undo 動作を活かす方法を模索するのがよろしいかと。
Bという事で元に戻しております。
そして、各TextBox(またはMyTextBox).Textの内容が変わってものUndoにも
格納しなくなったようです。

と考えているうちに、!!!
いっぺんに改変してしまって自身で状況がつかめておらずすみません。
【どれもこれも、通常のイベントに反応しないのは
もしかすると、すべての原因は『ユーザー入力でない』からでしょうか。】
じゃんぬ さんのこのMyTextBox
http://jeanne.wankuma.com/tips/vb.net/textbox/permitchars.html
イベント発生を横取りという事は、
通常イベントも発生しなくなるという事なのでしょうか。
(すごい基本的な事なのかも知れない…;;;)

Private Sub undoredo_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles ToolStripMenuItem_UNDOREDO.Click
Dim CMSSource As Control = ContextMenuStrip_FormYSKAMPROP.SourceControl
If CMSSource.GetType.ToString = "YSKAMPROPClassLibrary.MyTextBox" Then(●※1●)
If MyTexBox.CanUndo = True Then
Dim TextBox As MyTextBox = CType(CMSSource, MyTextBox)
TextBox.Undo()
End If
End If
End Sub

●※1●
また少し別の質問になってしまいますが、
ここで"MyTextBox"とTextBoxの振り分けをしていますが、KeyDownイベント中などで振り分けた方が
いいのでしょうか。
どこでもできる様な規制のない事に対して、
どこでするのが理想的等、基本的な順序の様なものがもしあるようでしたら、助言いただけますと助かります。


______________________________________________________________________
______________________________________________________________________

> とりあえず、こんな感じで如何でしょう。
> WndProc メソッドを下記のように実装してみて下さい。
>
>  Select Case m.Msg
>   Case WM_RBUTTONDOWN '&H204
>    Dim m2 = Message.Create(m.HWnd, m.Msg, m.WParam, m.LParam)
>    m.Msg = WM_LBUTTONDOWN '&H201
>    MyBase.WndProc(m)
>    MyBase.WndProc(m2)
>    Return

恐れ入ります、(書いている内容の意味は分かるのですが)これをどう実装すれば
機能するのかがわかりません、
(WndProcメソッドは先のじゃんぬ さんの例で使ってみて、
その意味を魔界の仮面弁士 さんの説明で何となく理解を得ました。)
いろいろ試しましたが、目的の様に機能はしませんでした。
もう少し詳細お教えいただけると助かります。

理解が悪く申し訳ないです。
そして、この『左クリックを一旦入れる』はユーザー入力ではないから、
ここでマウスイベントは発生しないと考えていいのでしょうか?


______________________________________________________________________
______________________________________________________________________

そして最後の質問です、またトピック(表題)と異なる質問ですが、
前回記した件です、
いろんな種類のエラーが出た後、最終的にはこれが本当に伝えたいエラーなのだろうと
思うものが、あるのですが、そのエラーの意味が解りません。
このエラーがあってもコンパイル(ビルド)はでき、デバックもできます。
もしお分かりでしたら、解説いただけますと助かります。
・・
メッセージ・・・
型MyTextBoxが見つかりませんでした。この型を含むアセンブリが参照していることを確認してください。
変数'TextBox2'は宣言されていないか、または割り当てられていません。
(これが最初出ていて、放置しておくと、)



'ClassLibrary.MyTextBox'は、ターゲット フレームワークでは使用できません。

※もちろんMyTextBoxはアセンブリ内に存在しています。
ターゲットフレームワークとMyTextBoxの相性?がうまくいっていないという意味なのでしょうか。


度々簡潔でなく複数の質問で読みづらいかとは存じますが、ご意見いただけますと幸いです。
引用返信 編集キー/
■75952 / inTopicNo.11)  Re[8]: オリジナルTextBoxにカスタムコントロール
□投稿者/ 魔界の仮面弁士 (336回)-(2015/05/20(Wed) 12:08:17)
No75947 (ni さん) に返信
> >TextBoxBase は利用しないでください。
> の『利用しない』、この禁止の適用範囲がわかりません。

TextBoxBase は、TextBox と RichTextBox の共通部分を纏めた「抽象クラス」です。
ですから通常は、TextBox や RichTextBox を使えば十分であろうかと思います。

イベント利用時においても、
 Private WithEvents TextBox0 As TextBoxBase
よりは
 Private WithEvents TextBox1 As MyTextBox
 Private WithEvents TextBox2 As TextBox
などのようにしておいた方が分かりやすいのではないでしょうか。


また、独自のテキスト コントロール クラスを作成する場合にしても、
TextBoxBase から継承するのではなく、TextBox あるいは RichTextBox から
継承させた方が手っ取り早いです。

https://msdn.microsoft.com/ja-jp/library/system.windows.forms.textboxbase.aspx
》 継承時の注意

》 通常、TextBoxBase から継承することはありません。
》 独自のテキスト コントロール クラスを作成するには、
》 TextBox または RichTextBox から継承します。



ただ、TextBox と RichTextBox (そして MyTextBox) に共通する基本型であるということから、
 For Each txt In Me.Controls.OfType(Of TextBoxBase)()
  txt.Clear()
 Next
のように、TextBox と RichTextBox を同一視したいような場面で使う分には問題ないと思います。
(これは、Control 型についても言えることです)



> それではなぜTextBoxBase.TextChangedがあるのだろう、と思ってしまいます。
TextChanged イベントを実装しているのは、TextBoxBase クラスではありません。
その派生元である Control クラスにて実装されています。

https://msdn.microsoft.com/ja-jp/library/system.windows.forms.textboxbase_events.aspx
》 TextChanged
》 Text プロパティの値が変更された場合に発生します。 (Control から継承されます。)

TextBoxBase は Control を継承しているため、TextChanged イベントもそのまま利用できます。
TextBox で TextChanged が使えるのも同様の理由ですね。

そのため、先のコードを
 'Private WithEvents TextBox0 As TextBoxBase
 Private WithEvents TextBox0 As Control
と変更したとしても、Sub TextBox0_TextChanged(… で拾えるはずです。


ただし、Control の継承クラスであっても、NumericUpDown や ListView などのように、
「Text プロパティが非表示」なクラスの場合は、TextChanged イベントを
利用するべきではありません。

実際には、これらのコントロールにも TextChanged イベントは存在するのですが、
.NET Framework での内部実装(infrastructure)のために使われており、
利用者側での利用を想定したものではないためです。こうしたクラスの TextChanged は、
プロパティウィンドウやコードウィンドウのイベント一覧から隠されています。
引用返信 編集キー/
■75954 / inTopicNo.12)  Re[9]: オリジナルTextBoxにカスタムコントロール
□投稿者/ ni (9回)-(2015/05/20(Wed) 14:05:35)
No75952 (魔界の仮面弁士 さん) に返信

魔界の仮面弁士 さん多くの助言よりたくさんの事が勉強できました、
本当にありがとうございます。
今回のコメントと
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?forum=7&topic=27392
これを読んで、意味が解りました。
私がしようとしている事自体が、ある意味おかしなことなんですね。
(できない事、と言うよりは結局はいろいろ用意しないといけないため、
素直に一個一個処理するのがまっとうという事ですね。
プログラミング自体にショートカットはない→したかったら自分で作れ、
という事ですね。)

そして、カスタムコントロール作成なんて敷居が高い事と思っていましたが、
高いも低いも必要なときは作らなくてはいけない、と解りました。

全部の疑問が解決しました。
カスタムコントロールもそれぞれ機能しています。
(エラーも変なところにカスタムのクラスを書くからでした。
←それぞれが分かれて存在する意味も分かっていませんでした。
今も分かっていませんが…)

また、無知からくるおかしな質問をする事があろうかと思いますが、
ご縁があればどうぞよろしくお願い致します。
本当にありがとうございました。
解決済み
引用返信 編集キー/
■75956 / inTopicNo.13)  Re[8]: オリジナルTextBoxにカスタムコントロール
□投稿者/ 魔界の仮面弁士 (337回)-(2015/05/20(Wed) 14:14:10)
2015/05/20(Wed) 14:17:49 編集(投稿者)

# 既に「解決済み」だったようなので、チェックを付け直しておきます。

No75947 (ni さん) に返信
> それぞれのTextBox.ShortcutKeys = True だったところをFalseにし、
(中略)
> ショートカットキー入力では、どのToolStripMenuItemイベントにも回らず、
> 反応しなくなりまして、

TextBox は、標準で Ctrl+X や Ctrl+V などの操作を受け付けますよね。
その操作が TextBox と ToolStripMenuItem の両方に流れてしまうと、
クリップボード操作が二重に実施されてしまう可能性があり、
誤動作の要因となることを懸念しています。

ゆえに、キー操作を受け付ける箇所を一箇所に集約すべきかと思います。
そのための実装としては、下記のような物が思い当たります。

(案1) TextBox と ToolStripMenuItem のショートカットキーを無効にし、Form 側でキー入力を受け取って処理する。
(案2) TextBox のショートカットキーを無効にし、ToolStripMenuItem でショートカットキーを受け取って処理する。
(案3) TextBox のショートカットキーを有効にし、クリップボードからの貼り付け内容の改修などは、WndProc の WM_PASTE 等で処理する。


> 『ユーザー入力でない』からでしょうか
標準のアンドゥ動作は、ユーザー入力のみに対して反応します。
Text プロパティを直接編集した場合は対象となりません。


'これらは Undo の対象になりません
TextBox1.Text = "あいうえお"
TextBox2.SelectedText = "あいうえお"

'これらは Undo できます
TextBox3.Paste("あいうえお")
TextBox4.Paste()



> じゃんぬ さんのこのMyTextBox
> http://jeanne.wankuma.com/tips/vb.net/textbox/permitchars.html
> イベント発生を横取りという事は、
> 通常イベントも発生しなくなるという事なのでしょうか。
> (すごい基本的な事なのかも知れない…;;;)

未検証ですが、コードを見る限りでは、「TextBox のイベントを利用している箇所」も
「イベントを横取りしている箇所」も見当たりませんでした。


もしも「イベントを横取りして、イベントが発生しないようにする」のであれば、
『Onイベント名』 メソッドを "Overdies" して、その中で
『MyBase.Onイベント名』のメソッドを「あえて呼ばない」ことで実現できます。

イメージコードとしてはこんな感じ。


Class MyTextBox
 Inherits TextBox
 Protected Overrides Sub OnTextChanged(e As EventArgs)
  If 横取りする場合 Then
   代替処理()  'ここを通った場合は、利用側に「TextChanged イベント」が通知されない
  Else
   '下記を呼ぶことで、「TextChanged イベント」が発生する
   MyBase.OnTextChanged(e)
  End If
 End Sub
End Class


> どこでもできる様な規制のない事に対して、
> どこでするのが理想的等、基本的な順序の様なものがもしあるようでしたら、助言いただけますと助かります。

フォーム全体で共通する処理、たとえば「Enter キーでフォーカス移動したい」とか
「F5 キーを押したら、データを再取得したい」といった処理の場合には、
Form 側で処理するのが便利です。この場合は、処理の内容に応じて
「KeyPreview プロパティを True にした上でキーボードイベントを処理する」か、
「プリプロセス系のメソッドをオーバーライドする」ことで実装します。
http://www.atmarkit.co.jp/fdotnet/dotnettips/243winkeyproc/winkeyproc.html

フォーム全体で共通ではなく、特定のコントロールのみで処理できれば良いのなら、
そのコントロールのイベントで処置するのが良いでしょう。

また、そのようなコントロールが繰り返し使われるような場合には、
MyTextBox のように、そのコントロールを継承して処置するのが楽だと思います。


> 型MyTextBoxが見つかりませんでした。
このコードが出るという事は、コーディングミスにより、
Class MyTextBox が正しくコンパイルされていない可能性があります。


> この型を含むアセンブリが参照していることを確認してください。
MyTextBox クラスを定義したあと、そのコードを「ビルド」してください。

一度もビルドしていない場合、Visual Studio のデザイナがそれを利用できず、
フォームに貼って利用する場合に、参照を解決できない可能性があります。

また、作成済みの MyTextBox の内容を大きく変更した場合も、
ビルドしなおしておいたほうが良いでしょう。


> 変数'TextBox2'は宣言されていないか、または割り当てられていません。
たとえば、MyTextBox をフォームに TextBox2 の名で貼って利用していたとします。

その後、Class MyTextBox のコードに「誤った修正」を施してしまい、
コンパイルは通るものの、実際に実行した場合において、
MyTextBox のコンストラクタやプロパティ設定等で例外が
発生するような状況に陥っていたとします。

この状況でフォームデザイナを開いたときに、
そのようなエラーが報告されてしまう可能性があります。
https://msdn.microsoft.com/ja-jp/library/4008y84t.aspx
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -