フォームがアクティブの時にタブキーを押すと、現在アクティブな(選択されている、フォーカスのある)コントロールの次のコントロールがアクティブになります。この順番を「タブオーダー」といいます。ここでは、タブオーダーに従って、次のコントロールをアクティブにする方法を紹介します。
なお、単純に指定したコントロールをアクティブにする方法は、「指定したコントロールをアクティブにする」で説明しています。
指定したコントロールの次のコントロールをアクティブにするには、Control.SelectNextControlメソッドを使用します。
フォームに配置されているTextBox1コントロールの次のコントロールをアクティブにするには、次のようにします。なおこのコードは、フォームクラス内に書かれているものとします。
'TextBox1の次のコントロールを選択する Me.SelectNextControl(Me.TextBox1, True, True, True, True)
//TextBox1の次のコントロールを選択する this.SelectNextControl(this.TextBox1, true, true, true, true);
SelectNextControlメソッドはパラメータの数が多いので、すべてパラメータについて説明しておきます。一番目のパラメータは、基準となるコントロールです。2番目のパラメータは、タブオーダーが次のコントロールをアクティブにするか、前のコントロールをアクティブにするかです。つまり、これをFalseにすると、Shiftキーを押しながらTabキーを押した時のように、前のコントロールがアクティブになります。3番目のパラメータは、TabStopがFalseのコントロールを無視するかどうか(無視する時は、True)です。4番目のパラメータは、子コントロールの子コントロールも含めるか(含めるときは、True)です。5番目のパラメータは、タブオーダーの最後のコントロールが基準とされている時に最初に戻るか(最初に戻る時は、True)です。
SelectNextControlメソッドは、次のコントロールをアクティブにした時にTrueを返し、しなかった時にFalseを返します。例えば5番目のパラメータがFalseで、1番目のパラメータが最後のコントロールであった場合は、次のコントロールがアクティブになりませんので、Falseを返します。
もう一つ、よくある例を示します。次の例では、現在選択されているコントロールの次のタブオーダーのコントロールをアクティブにしています。なお、現在選択されているコントロールを取得する方法は、「現在アクティブな(選択されている、フォーカスのある)コントロールを取得する」をご覧ください。
'次のコントロールを選択する Me.SelectNextControl(Me.ActiveControl, True, True, True, True)
//次のコントロールを選択する this.SelectNextControl(this.ActiveControl, true, true, true, true);
Control.GetNextControlメソッドを使用すると、指定したコントロールの次のコントロールを取得することができます。よって、このメソッドと「指定したコントロールをアクティブにする」で紹介している方法を組み合わせることでも、SelectNextControlメソッドと同じことができます。
ただしGetNextControlメソッドは、タブオーダーが最後のコントロールの次のコントロールを取得しようとすると、NULLを返すことに注意してください。
'次のコントロールを取得する Dim nextControl As Control = Me.GetNextControl(Me.ActiveControl, True) '前のコントロールを取得する場合は、次のようにする 'Dim nextControl As Control = Me.GetNextControl(Me.ActiveControl, False) '次のコントロールを選択する If Not (nextControl Is Nothing) Then nextControl.Select() End If
//次のコントロールを取得する Control nextControl = this.GetNextControl(this.ActiveControl, true); //前のコントロールを取得する場合は、次のようにする //Control nextControl = this.GetNextControl(this.ActiveControl, false); //次のコントロールを選択する if (nextControl != null) { nextControl.Select(); }
ProcessTabKeyメソッドは、タブキーが押された時に呼び出されるメソッドです。よってこのメソッドを使用することで、次のコントロールをアクティブにすることができます。
このメソッドは、ContainerControlクラスにプロテクトメソッドとして存在します。つまり、このメソッドは、FormやSplitContainer、UserControlのようにContainerControlから継承したコントロールで使用できますが、外部のクラスからは呼び出すことができません。
以下のコードがフォームクラスに記述されている時、Form.ProcessTabKeyメソッドによって、次のコントロールがアクティブになります。
'次のコントロールをアクティブにする Me.ProcessTabKey(True) '前のコントロールをアクティブにするには、次のようにする 'Me.ProcessTabKey(False)
//次のコントロールをアクティブにする this.ProcessTabKey(true); //前のコントロールをアクティブにするには、次のようにする //this.ProcessTabKey(false);
補足:フォームのProcessTabKeyメソッドは、最後のコントロールがアクティブの時に呼び出すと、最初のコントロールがアクティブになります。しかしそれ以外のほとんどのコントロールでは、最後のコントロールがアクティブの時に呼び出すと、フォーカスは移動せず、ProcessTabKeyメソッドはFalseを返します。
少し強引ですが、SendKeys.Sendメソッドを使用してタブキーを押したふりをすることで、次のコントロールをアクティブにするという方法もあります。
'Tabキーを押したふりをして、次のコントロールをアクティブにする SendKeys.Send("{TAB}") 'Shift+Tabキーを押したふりをして、前のコントロールをアクティブにする 'SendKeys.Send("+{TAB}")
//Tabキーを押したふりをして、次のコントロールをアクティブにする SendKeys.Send("{TAB}"); //Shift+Tabキーを押したふりをして、前のコントロールをアクティブにする //SendKeys.Send("+{TAB}");
フォームにSplitContainerコントロールが配置されている時、今まで紹介してきたようなメソッドをフォームで使用すると、タブキーを押した時のようにはフォーカスが移動しないかもしれません。例えば、SplitContainerコントロール内のコントロールがアクティブな時にフォームのSelectNextControlメソッドを呼び出すと、たとえSplitContainerコントロール内に次のコントロールがあったとしても、SplitContainerコントロール外の次のコントロールがアクティブになります。
このような問題は、SendKeys.Sendメソッドを使った方法以外の、SelectNextControl、GetNextControl、ProcessTabKeyメソッドで起こります。
この問題はフォームのSelectNextControlメソッドを呼び出していることが原因ですので、SplitContainerコントロールのSelectNextControlメソッドを呼び出すことで解決できます。例えば、フォームにSplitContainerコントロールが一つだけある場合は、以下のようにして次のコントロールをアクティブにすることができます。
'SplitContainer1内のコントロールがアクティブか調べる If Me.ActiveControl Is Me.SplitContainer1 Then 'SplitContainer1のSelectNextControlを呼び出す If Me.SplitContainer1.SelectNextControl( _ Me.SplitContainer1.ActiveControl, True, True, True, False) Then 'SplitContainer1内の次のコントロールがアクティブになったら終了 Return End If End If 'フォームのSelectNextControlを呼び出す Me.SelectNextControl(Me.ActiveControl, True, True, True, True)
//splitContainer1内のコントロールがアクティブか調べる if (this.ActiveControl == this.splitContainer1) { //splitContainer1のSelectNextControlを呼び出す if (this.splitContainer1.SelectNextControl( this.splitContainer1.ActiveControl, true, true, true, false)) { //splitContainer1内の次のコントロールがアクティブになったら終了 return; } } //フォームのSelectNextControlを呼び出す this.SelectNextControl(this.ActiveControl, true, true, true, true);
このようにSplitContainerコントロールが一つしかない時は簡単ですが、SplitContainerの中にSplitContainerがあるケースを考慮しなければならない場合は、複雑になります。そのようなケースも考慮して次のコントロールをアクティブするメソッドの例を以下に示します。
'Imports System.Windows.Forms 'Imports System.Collections.Generic ''' <summary> ''' フォーム上の次のコントロールをアクティブにする ''' </summary> ''' <param name="f">対象のフォーム</param> ''' <param name="forward"> ''' 次のコントロールをアクティブにする場合は、True。 ''' 前のコントロールをアクティブにする場合は、False。 ''' </param> Public Shared Sub SelectNextControlInForm(f As Form, forward As Boolean) 'アクティブコントロールの親ContainerControlをすべてリストに追加する Dim ccList As New List(Of ContainerControl)() Dim cc As ContainerControl = f While TypeOf cc.ActiveControl Is ContainerControl cc = DirectCast(cc.ActiveControl, ContainerControl) ccList.Insert(0, cc) End While '深いContainerControlから順にSelectNextControlを呼び出す For Each ct As ContainerControl In ccList If ct.SelectNextControl( _ ct.ActiveControl, forward, True, True, False) Then '次のコントロールがアクティブになったら、終了 Return End If Next 'フォームのSelectNextControlを呼び出す f.SelectNextControl(f.ActiveControl, forward, True, True, True) End Sub
//using System.Windows.Forms; //using System.Collections.Generic; /// <summary> /// フォーム上の次のコントロールをアクティブにする /// </summary> /// <param name="f">対象のフォーム</param> /// <param name="forward"> /// 次のコントロールをアクティブにする場合は、True。 /// 前のコントロールをアクティブにする場合は、False。 /// </param> public static void SelectNextControlInForm(Form f, bool forward) { //アクティブコントロールの親ContainerControlをすべてリストに追加する List<ContainerControl> ccList = new List<ContainerControl>(); ContainerControl cc = f; while (cc.ActiveControl is ContainerControl) { cc = (ContainerControl)cc.ActiveControl; ccList.Insert(0, cc); } //深いContainerControlから順にSelectNextControlを呼び出す foreach (ContainerControl ct in ccList) { if (ct.SelectNextControl(ct.ActiveControl, forward, true, true, false)) { //次のコントロールがアクティブになったら、終了 return; } } //フォームのSelectNextControlを呼び出す f.SelectNextControl(f.ActiveControl, forward, true, true, true); }
上記のような方法にも、問題があります。SelectNextControlメソッドでは、SplitContainerの分割線を選択状態にすることができません。
SplitContainerの分割線を選択状態にするには、SplitContainerのProcessTabKeyメソッドを使うしかなさそうです(または、自分でタブオーダーを調べて、「指定したコントロールをアクティブにする」で紹介している方法でコントロールをアクティブにするという方法もあります)。しかしProcessTabKeyメソッドはプロテクトメソッドですので、SplitContainerクラスを継承したクラスを作成してProcessTabKeyメソッドを呼び出すメソッドを公開するか、「隠蔽されている非パブリックメンバを呼び出す」のような方法で強引に呼び出すかといった手間が必要になります。
「隠蔽されている非パブリックメンバを呼び出す」の方法でProcessTabKeyメソッドを呼び出す例を示します。このメソッドをSelectNextControlの代わりに使用すると、SplitContainerの分割線も選択されるようになります。
'Imports System.Windows.Forms ''' <summary> ''' 指定したContainerControlのProcessTabKeyメソッドを呼び出す ''' </summary> ''' <param name="cc">対象のContainerControl</param> ''' <param name="forward">前方に移動する時はTrue。後方はFalse。</param> ''' <returns>コントロールが選択された時はTrue。</returns> Public Shared Function InvokeProcessTabKey( _ cc As ContainerControl, forward As Boolean) As Boolean Return CBool(cc.GetType().InvokeMember("ProcessTabKey", _ System.Reflection.BindingFlags.NonPublic Or _ System.Reflection.BindingFlags.Instance Or _ System.Reflection.BindingFlags.InvokeMethod, _ Nothing, cc, New Object() {forward})) End Function
//using System.Windows.Forms; /// <summary> /// 指定したContainerControlのProcessTabKeyメソッドを呼び出す /// </summary> /// <param name="cc">対象のContainerControl</param> /// <param name="forward">前方に移動する時はTrue。後方はFalse。</param> /// <returns>コントロールが選択された時はTrue。</returns> public static bool InvokeProcessTabKey(ContainerControl cc, bool forward) { return (bool)cc.GetType().InvokeMember("ProcessTabKey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod, null, cc, new object[] { forward }); }