DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

Enterキーを押した時、まるでTabキーを押したかのように、次のコントロールにフォーカスを移す

フォームがアクティブの時にTabキーを押すことによって、次のコントロールにフォーカスを移動させる(次のコントロールをアクティブにする、選択する)ことができます。ここでは、これを同じことをEnterキーが押された時にも行われるようにする方法を紹介します。

ProcessDialogKeyメソッドとProcessTabKeyメソッドを使用する方法

Enterキーが押されたかどうか知る方法は、「コントロールで矢印、Tab、Enter、Escキーが押されたことを知る」で説明しています。この内、ここではForm.ProcessDialogKeyメソッドをオーバーライドする方法を使用することにします。(KeyDownイベントを使用する方法については、後述します。)

また次のコントロールにフォーカスを移動させる方法は、「次のタブオーダーのコントロールをアクティブに(選択、フォーカスを移動)する」で説明しています。ここでは、Form.ProcessTabKeyメソッドを使用することにします。(他の方法については、後述します。)

以下に例を示します。この例では、CtrlキーかAltキーが押されている時は本来の動作をさせていますので、複数行TextBoxで改行したい場合は、Ctrlキーを押しながらEnterキーを押すことで対処できます。このコードはフォームのクラス内に記述してください。

VB.NET
コードを隠すコードを選択
'Imports System.Windows.Forms

<System.Security.Permissions.UIPermission( _
    System.Security.Permissions.SecurityAction.Demand, _
    Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _
Protected Overrides Function ProcessDialogKey(keyData As Keys) As Boolean
    'Returnキーが押されているか調べる
    'AltかCtrlキーが押されている時は、本来の動作をさせる
    If ((keyData And Keys.KeyCode) = Keys.Return) AndAlso _
        ((keyData And (Keys.Alt Or Keys.Control)) = Keys.None) Then
        'Tabキーを押した時と同じ動作をさせる
        'Shiftキーが押されている時は、逆順にする
        Me.ProcessTabKey((keyData And Keys.Shift) <> Keys.Shift)
        '本来の処理はさせない
        Return True
    End If

    Return MyBase.ProcessDialogKey(keyData)
End Function
C#
コードを隠すコードを選択
//using System.Windows.Forms;

[System.Security.Permissions.UIPermission(
    System.Security.Permissions.SecurityAction.Demand,
    Window = System.Security.Permissions.UIPermissionWindow.AllWindows)]
protected override bool ProcessDialogKey(Keys keyData)
{
    //Returnキーが押されているか調べる
    //AltかCtrlキーが押されている時は、本来の動作をさせる
    if (((keyData & Keys.KeyCode) == Keys.Return) &&
        ((keyData & (Keys.Alt | Keys.Control)) == Keys.None))
    {
        //Tabキーを押した時と同じ動作をさせる
        //Shiftキーが押されている時は、逆順にする
        this.ProcessTabKey((keyData & Keys.Shift) != Keys.Shift);
        //本来の処理はさせない
        return true;
    }

    return base.ProcessDialogKey(keyData);
}

Tabキーを押した時と同じ動作をさせる別の方法

上記の例ではForm.ProcessTabKeyメソッドを使用してTabキーを押した時と同じ動作をさせています。この部分は、「次のタブオーダーのコントロールをアクティブに(選択、フォーカスを移動)する」にある別の方法を使用することもできます。

SendKeys.Sendメソッドを使用する方法の場合、上記のProcessTabKeyメソッドを使用している部分は、以下のようになります。

VB.NET
コードを隠すコードを選択
'Tabキーを押した時の動作をさせる
SendKeys.Send("{TAB}")
C#
コードを隠すコードを選択
//Tabキーを押した時の動作をさせる
SendKeys.Send("{TAB}");
補足:Shiftキーが押されている時は「SendKeys.Send("+{TAB}")」を呼び出すというふうにしてしまうと、Shiftキーを押したままEnterキーを複数押した時、はじめの1回しか逆順にフォーカスが移動しなくなってしまいます。

また、Control.SelectNextControlメソッドを使用する方法の場合は、次のようになります。

VB.NET
コードを隠すコードを選択
'次のタブオーダーのコントロールにフォーカスを移動させる
'Shiftキーが押されている時は、逆順にする
Me.SelectNextControl(Me.ActiveControl, _
    ((keyData And Keys.Shift) <> Keys.Shift), True, True, True)
C#
コードを隠すコードを選択
//次のタブオーダーのコントロールにフォーカスを移動させる
//Shiftキーが押されている時は、逆順にする
this.SelectNextControl(this.ActiveControl,
    ((keyData & Keys.Shift) != Keys.Shift), true, true, true);
補足:フォームにSplitContainerコントロールが配置されている場合の対処法は、「次のタブオーダーのコントロールをアクティブに(選択、フォーカスを移動)する」で紹介されているもの以外に、SplitContainerクラスのProcessDialogKeyメソッドを次のようにオーバーライドするという方法も考えられます。
VB.NET
コードを隠すコードを選択
'Imports System.Windows.Forms

Public Class SplitContainerEx
    Inherits SplitContainer
    <System.Security.Permissions.UIPermission( _
        System.Security.Permissions.SecurityAction.Demand, _
        Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _
    Protected Overrides Function ProcessDialogKey(keyData As Keys) As Boolean
        If ((keyData And Keys.KeyCode) = Keys.Return) AndAlso _
            ((keyData And (Keys.Alt Or Keys.Control)) = Keys.None) Then
            Return Me.ProcessTabKey((keyData And Keys.Shift) <> Keys.Shift)
        End If
        Return MyBase.ProcessDialogKey(keyData)
    End Function
End Class
C#
コードを隠すコードを選択
//using System.Windows.Forms;

public class SplitContainerEx : SplitContainer
{
    [System.Security.Permissions.UIPermission(
        System.Security.Permissions.SecurityAction.Demand,
        Window = System.Security.Permissions.UIPermissionWindow.AllWindows)]
    protected override bool ProcessDialogKey(Keys keyData)
    {
        if (((keyData & Keys.KeyCode) == Keys.Return) &&
            ((keyData & (Keys.Alt | Keys.Control)) == Keys.None))
        {
            return this.ProcessTabKey((keyData & Keys.Shift) != Keys.Shift);
        }
        return base.ProcessDialogKey(keyData);
    }
}

KeyDownイベントを使用する方法

現在フォーカスのあるコントロールのKeyDownイベントを使ってEnterキーが押されたかを調べることもできます。この場合、フォームにあるすべてのコントロールのKeyDownイベントを捕捉するのは面倒ですので、フォームのKeyPreviewプロパティをTrueにして、フォームのKeyDownイベントだけを捕捉するようにすればよいでしょう(詳しくは、「KeyPressなどのキーイベントをすべてフォームが受け取るようにする」)。

Buttonコントロールなど一部のコントロールでは、Enterキーを押してもKeyDownイベントが発生しませんので、この方法ではフォーカスが移動しません。よって、フォームにButtonコントロールを配置している場合は、ButtonコントロールのTabStopプロパティをFalseにしてフォーカスが移動しないようにするなどの工夫が必要になるでしょう。

また、フォームのAcceptButtonプロパティにButtonが設定されている時は、Enterキーを押すとそのButtonがクリックされたことになってしまうため、やはりフォーカスは移動しません(AcceptButtonプロパティについて詳しくは、「フォームにOKボタン、キャンセルボタンを付ける」)。よって、AcceptButtonプロパティにはなにも設定しないようにします。

この方法を使った具体例を以下に示します。

VB.NET
コードを隠すコードを選択
'Imports System.Windows.Forms

'フォームのLoadイベントハンドラ
Private Sub Form1_Load(sender As Object, e As EventArgs) _
        Handles MyBase.Load
    'AcceptButtonを無効にする
    Me.AcceptButton = Nothing

    'キーイベントをフォームで受け取る
    Me.KeyPreview = True
    'KeyDownイベントハンドラを追加
    AddHandler Me.KeyDown, New KeyEventHandler(AddressOf Form1_KeyDown)
End Sub

'KeyDownイベントハンドラ
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs)
    'Enterキーが押されているか確認
    'AltかCtrlキーが押されている時は無視する
    If (e.KeyCode = Keys.Enter) AndAlso _
        Not e.Alt AndAlso Not e.Control Then
        'あたかもTabキーが押されたかのようにする
        'Shiftが押されている時は前のコントロールのフォーカスを移動
        Me.ProcessTabKey(Not e.Shift)

        e.Handled = True
        '.NET Framework 2.0以降
        e.SuppressKeyPress = True
    End If
End Sub
C#
コードを隠すコードを選択
//using System.Windows.Forms;

//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, EventArgs e)
{
    //AcceptButtonを無効にする
    this.AcceptButton = null;

    //キーイベントをフォームで受け取る
    this.KeyPreview = true;
    //KeyDownイベントハンドラを追加
    this.KeyDown += new KeyEventHandler(Form1_KeyDown);
}

//KeyDownイベントハンドラ
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    //Enterキーが押されているか確認
    //AltかCtrlキーが押されている時は無視する
    if ((e.KeyCode == Keys.Enter)
        && !e.Alt && !e.Control)
    {
        //あたかもTabキーが押されたかのようにする
        //Shiftが押されている時は前のコントロールのフォーカスを移動
        this.ProcessTabKey(!e.Shift);

        e.Handled = true;
        //.NET Framework 2.0以降
        e.SuppressKeyPress = true;
    }
}
補足:この方法では単一行テキストボックスでEnterキーを押すとビープ音が鳴りますが、これを防ぐ方法は、「単一行テキストボックスでEnterやEscapeキーを押した時にビープ音が鳴らないようにする」で説明しています。

KeyUpイベントとの違い

KeyDownイベントの代わりに、KeyUpイベントを使うこともできます。両者の違いを以下に示します。

  • Buttonなどのコントロールにフォーカスがある時は、Enterキーの押下でKeyDownイベントが発生しません。KeyUpイベントならば発生します。
  • KeyUpイベントでは、IMEで文字を確定する時のEnterキーの押下でもKeyEventArgs.KeyCodeプロパティの値がKeys.Returnになるため、これだけでは通常のEnterの押下と区別ができません。KeyDownイベントでは、IME確定時のEnterキーの押下ではKeyEventArgs.KeyCodeプロパティがKeys.ProcessKeyになるため、通常のEnterと区別できます。
  • メッセージボックスをEnterキーの押下で閉じると、その直後にKeyUpイベントが発生してしまいます。KeyDownイベントは発生しません。

複数行テキストボックスで改行する

今までの例では、複数行テキストボックス(またはリッチテキストボックス)にフォーカスがある時にEnterキーを押すと、次のコントロールにフォーカスが移動します。しかしCtrlキーが押されている時はそうしないようにしていますので、Ctrlを押しながらEnterキーを押すことで改行文字を入力することができます。

もし複数行テキストボックスではEnterキーで改行したい(そして、Ctrl+Enterキーで次のコントロールにフォーカスを移動させたい)という場合は、例えば次のようにすればよいでしょう。

VB.NET
コードを隠すコードを選択
'Imports System.Windows.Forms

<System.Security.Permissions.UIPermission( _
    System.Security.Permissions.SecurityAction.Demand, _
    Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _
Protected Overrides Function ProcessDialogKey(keyData As Keys) As Boolean
    'Returnキーが押されているか調べる
    'Altキーが押されている時は、本来の動作をさせる
    If ((keyData And Keys.KeyCode) = Keys.Return) AndAlso _
        ((keyData And Keys.Alt) <> Keys.Alt) Then
        '現在のコントロールが複数行テキストボックスか
        Dim isMultilineTextbox As Boolean = _
            ((TypeOf Me.ActiveControl Is TextBoxBase) AndAlso _
             DirectCast(Me.ActiveControl, TextBoxBase).Multiline)
        'Ctrlキーが押されているか
        Dim pressingCtrl As Boolean = _
            ((keyData And Keys.Control) = Keys.Control)
        '複数行テキストボックスでCtrlが押されている時と
        'それ以外のコントロールでCtrlが押されていない時
        If isMultilineTextbox = pressingCtrl Then
            'Tabキーを押した時と同じ動作をさせる
            'Shiftキーが押されている時は、逆順にする
            Me.ProcessTabKey((keyData And Keys.Shift) <> Keys.Shift)
            '本来の処理はさせない
            Return True
        End If
    End If

    Return MyBase.ProcessDialogKey(keyData)
End Function
C#
コードを隠すコードを選択
//using System.Windows.Forms;

[System.Security.Permissions.UIPermission(
    System.Security.Permissions.SecurityAction.Demand,
    Window = System.Security.Permissions.UIPermissionWindow.AllWindows)]
protected override bool ProcessDialogKey(Keys keyData)
{
    //Returnキーが押されているか調べる
    //Altキーが押されている時は、本来の動作をさせる
    if (((keyData & Keys.KeyCode) == Keys.Return) &&
        ((keyData & Keys.Alt) != Keys.Alt))
    {
        //現在のコントロールが複数行テキストボックスか
        bool isMultilineTextbox = ((this.ActiveControl is TextBoxBase) &&
            ((TextBoxBase)this.ActiveControl).Multiline);
        //Ctrlキーが押されているか
        bool pressingCtrl = ((keyData & Keys.Control) == Keys.Control);
        //複数行テキストボックスでCtrlが押されている時と
        //それ以外のコントロールでCtrlが押されていない時
        if (isMultilineTextbox == pressingCtrl)
        {
            //Tabキーを押した時と同じ動作をさせる
            //Shiftキーが押されている時は、逆順にする
            this.ProcessTabKey((keyData & Keys.Shift) != Keys.Shift);
            //本来の処理はさせない
            return true;
        }
    }

    return base.ProcessDialogKey(keyData);
}
  • 履歴:
  • 2010/12/1 サンプルで、KeyUpイベントの代わりにKeyDownイベントを使うようにした。頂いたコメントを参考に、KeyUpイベントとKeyDownイベントの違いを追記した。
  • 2014/6/30 頂いたコメントを参考にして、ProcessDialogKeyを使ったコードを追加し、これをはじめに掲載するようにした。SendKeys.SendとControl.SelectNextControlを使ったコードを追加。AltかCtrlキーが押されている時の処理を追加するなど、コードの書き換え。
  • 2014/12/8 頂いたコメントを参考に、SplitContainerに関する説明を追加。SendKeys.Sendメソッドを使った例を修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。