C#のKeyDownイベント完全ガイド|発生しない原因からキー判定・KeyPressとの違いまで解説

はじめに

C#でWindowsアプリを開発していると、「Enterキーを押したら検索する」「Escキーで画面を閉じる」「Ctrl+Sで保存する」「矢印キーでキャラクターやカーソルを動かす」といったキー操作を実装したくなる場面があります。

そのときによく使われるのが、KeyDownイベントです。

KeyDownは、キーボードのキーが押された瞬間に発生するイベントで、WinFormsやWPFなどのGUIアプリケーションで頻繁に利用されます。ただし、実際に使ってみると次のような問題にぶつかることがあります。

「KeyDownイベントが発生しない」

「Enterキーは取得できるのに矢印キーが反応しない」

「TextBoxにフォーカスがあるとFormのKeyDownが動かない」

「KeyPressとの違いがよく分からない」

「Ctrl+Sのようなショートカットキーをどう判定すればいいのか分からない」

この記事では、C#のKeyDownイベントについて、基本的な使い方からキー判定、発生しない原因、KeyPressKeyUpとの違い、実用的なサンプルまで詳しく解説します。

1. C#のKeyDownイベントとは

1-1. KeyDownイベントの基本概要

KeyDownイベントとは、キーボードのキーが押されたときに発生するイベントです。

C#のWinFormsでは、フォームやコントロールに対してKeyDownイベントを設定できます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show(e.KeyCode.ToString());
}

この例では、フォーム上でキーが押されると、押されたキーの名前がメッセージボックスに表示されます。

KeyDownは「キーが押された瞬間」を検知するイベントなので、文字入力だけでなく、Enterキー、Escキー、矢印キー、ファンクションキー、Ctrlキー、Shiftキー、Altキーなどの特殊キーも扱えます。

そのため、アプリケーションの操作性を高めるショートカットキーや、ゲームのキー操作、入力補助などに向いています。

1-2. キーが押されたときに発生するイベントの仕組み

C#のWinFormsでは、キーボード操作に関連する主なイベントとして、次の3つがあります。

KeyDown → KeyPress → KeyUp

基本的には、キーを押すとKeyDownが発生し、文字として入力可能なキーであればKeyPressが発生し、キーを離すとKeyUpが発生します。

たとえば、キーボードの「A」キーを押して離した場合は、次のような流れになります。

Aキーを押す

KeyDown発生

KeyPress発生

Aキーを離す

KeyUp発生

一方で、ShiftキーやCtrlキー、矢印キーなどは文字入力ではないため、KeyPressが発生しない場合があります。しかしKeyDownでは、こうした特殊キーも判定できます。

つまり、KeyDownは「どのキーが押されたか」を判定するためのイベントであり、KeyPressは「どの文字が入力されたか」を扱うイベントと考えると分かりやすいです。

1-3. KeyDownが使われる代表的な場面

KeyDownイベントは、次のような場面でよく使われます。

Enterキーで検索処理を実行する
Escキーで画面を閉じる
Ctrl+Sで保存する
Ctrl+C、Ctrl+Vなどのショートカットを判定する
矢印キーで項目やオブジェクトを移動する
F1キーでヘルプを表示する
数字キーでメニューを選択する
ゲームや業務アプリでキーボード操作を実装する

たとえば、Enterキーでボタンのクリック処理を呼び出す場合は、次のように書けます。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
button1.PerformClick();
}
}

このように、KeyDownを使うと、マウス操作だけでなくキーボード操作にも対応したアプリケーションを作ることができます。

1-4. WinFormsとWPFでのKeyDownイベントの違い

C#のKeyDownイベントは、WinFormsとWPFの両方で使えます。ただし、イベントの仕組みや引数の型、キーの判定方法が少し異なります。

WinFormsでは、主にSystem.Windows.Forms.KeyEventArgsを使います。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

WPFでは、主にSystem.Windows.Input.KeyEventArgsを使います。

C#
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

WinFormsではKeys.Enter、WPFではKey.Enterのように、キーを表す列挙型が異なります。

また、WPFには通常のKeyDownだけでなく、先にイベントを受け取れるPreviewKeyDownがあります。WPFのイベントは「ルーティングイベント」という仕組みで、親要素や子要素を伝わって処理される点もWinFormsとは異なります。

この記事では主にWinFormsを中心に解説し、必要に応じてWPFの違いも補足します。

2. C#でKeyDownイベントを使う基本手順

2-1. WinFormsでKeyDownイベントを設定する方法

WinFormsでKeyDownイベントを使うには、対象のフォームまたはコントロールにイベントハンドラを設定します。

最も基本的な流れは次のとおりです。

1. フォームまたはコントロールを用意する
2. KeyDownイベントを登録する
3. イベントハンドラ内で押されたキーを判定する
4. 必要な処理を実行する

フォーム全体でキー入力を受け取りたい場合は、フォームのKeyDownイベントを使います。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

ただし、フォーム上にTextBoxやButtonなどのコントロールがある場合、キー入力はフォーカスを持っているコントロールに送られるため、フォームのKeyDownが発生しないことがあります。

その場合は、後述するKeyPreviewプロパティを使います。

2-2. Visual Studioのデザイナーからイベントを追加する方法

Visual Studioのデザイナーを使うと、コードを手で書かなくてもKeyDownイベントを追加できます。

手順は次のとおりです。

1. Windowsフォームデザイナーを開く
2. 対象のフォームまたはコントロールを選択する
3. プロパティウィンドウを表示する
4. 稲妻アイコンをクリックしてイベント一覧を表示する
5. KeyDown欄をダブルクリックする
6. 自動生成されたイベントハンドラに処理を書く

たとえば、フォームのKeyDownイベントを追加すると、次のようなメソッドが生成されます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{

}

この中にキー判定処理を書きます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

デザイナーからイベントを設定すると、InitializeComponent内でイベント登録が自動的に行われます。

C#
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);

通常は自分でこのコードを編集する必要はありません。

2-3. コードでKeyDownイベントハンドラを登録する方法

デザイナーを使わず、コードでKeyDownイベントを登録することもできます。

C#
public Form1()
{
InitializeComponent();

this.KeyDown += Form1_KeyDown;
}

イベントハンドラは次のように定義します。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

TextBoxに対して設定する場合は、次のように書きます。

C#
public Form1()
{
InitializeComponent();

textBox1.KeyDown += textBox1_KeyDown;
}

private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("TextBoxでEnterキーが押されました");
}
}

コードで登録する方法は、動的に作成したコントロールにイベントを設定したい場合にも便利です。

C#
TextBox textBox = new TextBox();
textBox.Location = new Point(20, 20);
textBox.KeyDown += TextBox_KeyDown;
this.Controls.Add(textBox);

2-4. KeyEventArgsで取得できる主な情報

WinFormsのKeyDownイベントでは、イベント引数としてKeyEventArgsが渡されます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
}

このeから、押されたキーに関する情報を取得できます。

代表的なプロパティは次のとおりです。

KeyCode:押されたキー本体を表す
KeyData:キー本体と修飾キーを含む情報を表す
KeyValue:キーコードの数値を表す
Modifiers:Ctrl、Shift、Altなどの修飾キーを表す
Alt:Altキーが押されているかを表す
Control:Ctrlキーが押されているかを表す
Shift:Shiftキーが押されているかを表す
Handled:イベントを処理済みにするかどうかを表す
SuppressKeyPress:後続のキーイベントや入力を抑制するかどうかを表す

たとえば、押されたキーの情報を画面に表示するには、次のように書きます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
label1.Text =
$"KeyCode: {e.KeyCode}\n" +
$"KeyData: {e.KeyData}\n" +
$"KeyValue: {e.KeyValue}\n" +
$"Modifiers: {e.Modifiers}";
}

キー判定では、基本的にe.KeyCodeを使うことが多いです。CtrlやShiftなどとの組み合わせを判定する場合は、e.Controle.Modifiersを使います。

2-5. KeyCode・KeyData・KeyValueの違い

KeyEventArgsには、似た名前のプロパティとしてKeyCodeKeyDataKeyValueがあります。

それぞれの違いを理解しておくと、キー判定のコードが書きやすくなります。

KeyCodeは、押されたキー本体を表します。

C#
if (e.KeyCode == Keys.S)
{
MessageBox.Show("Sキーが押されました");
}

KeyDataは、キー本体に加えてCtrl、Shift、Altなどの修飾キーも含んだ情報を表します。

C#
if (e.KeyData == (Keys.Control | Keys.S))
{
MessageBox.Show("Ctrl + S が押されました");
}

KeyValueは、キーコードを数値で表します。

C#
label1.Text = e.KeyValue.ToString();

通常の判定ではKeyCodeを使い、ショートカットキーのように修飾キー込みで判定したい場合はKeyDataModifiersを使うとよいでしょう。

ただし、可読性を重視するなら、次のように書く方法が分かりやすいです。

C#
if (e.Control && e.KeyCode == Keys.S)
{
Save();
}

e.Controle.Shiftを使うと、「Ctrlキーが押されていて、かつSキーが押された」という意図が読み取りやすくなります。

3. KeyDownイベントで押されたキーを判定する方法

3-1. 特定のキーを判定する基本コード

KeyDownイベントで押されたキーを判定する基本形は、e.KeyCodeを使ったif文です。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

複数のキーを判定する場合は、if文を並べてもよいですが、キーの種類が多い場合はswitch文を使うと見やすくなります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Enter:
MessageBox.Show("Enterキー");
break;

case Keys.Escape:
MessageBox.Show("Escキー");
break;

case Keys.Space:
MessageBox.Show("Spaceキー");
break;
}
}

KeyDownでは、文字キーだけでなく、特殊キーやファンクションキーも判定できます。

C#
if (e.KeyCode == Keys.F1)
{
MessageBox.Show("ヘルプを表示します");
}

3-2. Enterキー・Escキー・Spaceキーを判定する方法

よく使うキーとして、Enterキー、Escキー、Spaceキーがあります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキーが押されました");
}

if (e.KeyCode == Keys.Escape)
{
MessageBox.Show("Escキーが押されました");
}

if (e.KeyCode == Keys.Space)
{
MessageBox.Show("Spaceキーが押されました");
}
}

Enterキーでボタン処理を実行したい場合は、PerformClickを使えます。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
buttonSearch.PerformClick();
e.SuppressKeyPress = true;
}
}

e.SuppressKeyPress = true;を設定すると、Enterキーによるビープ音や改行入力を抑制できます。

Escキーでフォームを閉じる場合は、次のように書けます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

Spaceキーは、ボタンやチェックボックスにフォーカスがあるとコントロール側で処理されることがあります。その場合は、フォーム側で受け取るためにKeyPreviewProcessCmdKeyの利用を検討します。

3-3. 矢印キーを判定する方法

矢印キーは、Keys.UpKeys.DownKeys.LeftKeys.Rightで判定します。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
label1.Top -= 5;
break;

case Keys.Down:
label1.Top += 5;
break;

case Keys.Left:
label1.Left -= 5;
break;

case Keys.Right:
label1.Left += 5;
break;
}
}

この例では、矢印キーを押すとlabel1の位置が上下左右に移動します。

ただし、矢印キーはフォーカス移動やコントロール内のカーソル移動に使われることが多いため、通常のKeyDownでは取得できない場合があります。

特に、Button、TextBox、DataGridViewなどのコントロールにフォーカスがある場合、矢印キーはコントロール側で処理されることがあります。

その場合は、次の方法を検討します。

KeyPreviewをtrueにする
PreviewKeyDownイベントを使う
IsInputKeyをオーバーライドする
ProcessCmdKeyを使う

これらの方法は、後の「特殊キーや反応しにくいキーを処理する方法」で詳しく解説します。

3-4. 数字キー・英字キーを判定する方法

数字キーを判定する場合、キーボード上部の数字キーとテンキーは別のキーとして扱われます。

キーボード上部の数字キーは、Keys.D0からKeys.D9です。

C#
if (e.KeyCode == Keys.D1)
{
MessageBox.Show("数字の1キーが押されました");
}

テンキーの数字キーは、Keys.NumPad0からKeys.NumPad9です。

C#
if (e.KeyCode == Keys.NumPad1)
{
MessageBox.Show("テンキーの1キーが押されました");
}

上部の1キーとテンキーの1キーの両方を同じ処理にしたい場合は、次のように書きます。

C#
if (e.KeyCode == Keys.D1 || e.KeyCode == Keys.NumPad1)
{
MessageBox.Show("1キーが押されました");
}

英字キーは、Keys.AからKeys.Zで判定します。

C#
if (e.KeyCode == Keys.A)
{
MessageBox.Show("Aキーが押されました");
}

KeyDownでは、Shiftキーを押しながらAキーを押しても、基本的にKeyCodeKeys.Aです。大文字・小文字の文字そのものを取得したい場合は、KeyPressやTextBoxのTextChangedを検討します。

3-5. Ctrl・Shift・Altなど修飾キーを判定する方法

Ctrl、Shift、Altなどの修飾キーは、e.Controle.Shifte.Altで判定できます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control)
{
MessageBox.Show("Ctrlキーが押されています");
}

if (e.Shift)
{
MessageBox.Show("Shiftキーが押されています");
}

if (e.Alt)
{
MessageBox.Show("Altキーが押されています");
}
}

特定のキーと組み合わせて判定する場合は、次のように書きます。

C#
if (e.Control && e.KeyCode == Keys.S)
{
MessageBox.Show("Ctrl + S が押されました");
}

Shift+Enterを判定する場合は、次のように書けます。

C#
if (e.Shift && e.KeyCode == Keys.Enter)
{
MessageBox.Show("Shift + Enter が押されました");
}

Ctrl+Shift+Sのように複数の修飾キーを判定することもできます。

C#
if (e.Control && e.Shift && e.KeyCode == Keys.S)
{
MessageBox.Show("Ctrl + Shift + S が押されました");
}

3-6. ショートカットキーを実装する方法

ショートカットキーを実装する場合は、KeyDownイベントで修飾キーと通常キーの組み合わせを判定します。

たとえば、Ctrl+Sで保存処理を実行するコードは次のとおりです。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
}

private void SaveFile()
{
MessageBox.Show("保存しました");
}

Ctrl+Nで新規作成、Ctrl+Oで開く、Ctrl+Sで保存する例です。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.N)
{
NewFile();
e.SuppressKeyPress = true;
}
else if (e.Control && e.KeyCode == Keys.O)
{
OpenFile();
e.SuppressKeyPress = true;
}
else if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
}

ショートカットキーでは、処理後にe.SuppressKeyPress = true;を設定することがよくあります。これにより、TextBoxなどに不要な文字が入力されたり、ビープ音が鳴ったりするのを防ぎやすくなります。

ただし、本格的なメニュー付きアプリケーションでは、MenuStripToolStripMenuItemShortcutKeysプロパティを使う方法もあります。

C#
saveToolStripMenuItem.ShortcutKeys = Keys.Control | Keys.S;

画面全体でショートカットキーを扱う場合は、KeyDownだけでなくProcessCmdKeyも選択肢になります。

3-7. 複数キーの組み合わせを判定する方法

Ctrl+Sのような「修飾キー+通常キー」であれば、e.Controle.Shiftを使えば簡単に判定できます。

C#
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
}

一方で、「Aキーを押しながらBキーを押す」のように、通常キー同士の同時押しを判定したい場合は、現在押されているキーを管理する必要があります。

たとえば、HashSet<Keys>を使って押下中のキーを記録できます。

C#
private HashSet<Keys> pressedKeys = new HashSet<Keys>();

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
pressedKeys.Add(e.KeyCode);

if (pressedKeys.Contains(Keys.A) && pressedKeys.Contains(Keys.B))
{
label1.Text = "A + B が押されています";
}
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
pressedKeys.Remove(e.KeyCode);
}

ゲームや操作画面では、このようにKeyDownでキーを追加し、KeyUpでキーを削除する方法がよく使われます。

ただし、キーボードの仕様や環境によっては、同時押しできるキー数に制限があります。確実に複雑な同時押しを扱いたい場合は、入力設計そのものを見直すことも大切です。

4. KeyDownイベントが発生しない原因と対処法

4-1. フォームにフォーカスが当たっていない

KeyDownイベントが発生しない原因として、まず確認したいのがフォーカスです。

キーボード入力は、基本的に現在フォーカスを持っているフォームやコントロールに送られます。そのため、対象のフォームやコントロールにフォーカスがない場合、KeyDownは発生しません。

たとえば、フォームにKeyDownイベントを書いていても、実際にはTextBoxにフォーカスがある場合、キー入力はTextBoxに送られます。

フォームにフォーカスを当てたい場合は、次のようにFocusを呼び出すことがあります。

C#
this.Focus();

ただし、フォーム上にフォーカス可能なコントロールがある場合、フォーム自体にフォーカスを当てるのは思ったように動かないことがあります。

そのため、フォーム全体でキー入力を受け取りたい場合は、KeyPreviewを使うのが一般的です。

C#
this.KeyPreview = true;

4-2. TextBoxやButtonなど別コントロールにフォーカスがある

フォーム上にTextBoxやButtonなどがある場合、多くの場合はそれらのコントロールにフォーカスがあります。

この状態でキーを押すと、フォームではなく、フォーカスを持っているコントロールのKeyDownイベントが発生します。

たとえば、TextBoxにフォーカスがある場合は、次のイベントが発生します。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
label1.Text = "TextBoxでキーが押されました";
}

フォーム側で受け取りたい場合は、フォームのKeyPreviewtrueにします。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

これにより、フォーカスがTextBoxにあっても、フォームのKeyDownイベントが先に発生します。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
MessageBox.Show("ヘルプを表示します");
}
}

ただし、すべてのキーを必ずフォームで取得できるわけではありません。Tabキーや矢印キーなど、一部のキーはコントロール側で特殊な処理に使われるため、別の対応が必要になることがあります。

4-3. FormのKeyPreviewがfalseになっている

フォーム全体でキー入力を受け取りたいのにKeyDownが発生しない場合、KeyPreviewfalseになっている可能性があります。

KeyPreviewは、フォームがコントロールより先にキーイベントを受け取るかどうかを決めるプロパティです。

初期値は通常falseです。

C#
this.KeyPreview = true;

デザイナーから設定する場合は、フォームを選択してプロパティウィンドウのKeyPreviewTrueにします。

コードで設定する場合は、コンストラクタでInitializeComponentの後に書くのが一般的です。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
}

KeyPreviewtrueにすると、フォーカスが子コントロールにあっても、フォームのキーイベントが先に呼ばれます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

フォーム全体のEscキー処理やショートカットキー処理には、KeyPreviewが非常に便利です。

4-4. 矢印キー・Tabキー・Enterキーが反応しない

KeyDownイベント自体は設定できているのに、矢印キー、Tabキー、Enterキーなどが反応しないことがあります。

これは、これらのキーがコントロールの操作やフォーカス移動に使われるためです。

たとえば、Tabキーは通常、次のコントロールへフォーカスを移動するために使われます。矢印キーは、Button間の移動やTextBox内のカーソル移動に使われることがあります。Enterキーは、フォームの既定ボタンやTextBoxの入力確定として扱われることがあります。

このようなキーは、通常の入力キーではなく「操作キー」として先に処理される場合があります。

対処法としては、次のような方法があります。

PreviewKeyDownイベントで入力キーとして扱う
IsInputKeyをオーバーライドする
ProcessCmdKeyをオーバーライドする
AcceptButtonやCancelButtonを利用する

たとえば、Buttonで矢印キーをKeyDownとして扱いたい場合は、PreviewKeyDownを使って次のように書けます。

C#
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Up ||
e.KeyCode == Keys.Down ||
e.KeyCode == Keys.Left ||
e.KeyCode == Keys.Right)
{
e.IsInputKey = true;
}
}

4-5. コントロール側でキー入力が処理されている

TextBox、Button、DataGridView、ComboBoxなどのコントロールは、キー入力に対して独自の処理を持っています。

たとえば、TextBoxでは左右矢印キーがカーソル移動に使われます。DataGridViewでは矢印キーがセル移動に使われます。ButtonではSpaceキーやEnterキーがクリック操作として扱われることがあります。

このような場合、フォーム側のKeyDownに処理を書いても、期待どおりに発生しないことがあります。

対処法は、目的によって変わります。

特定のコントロールだけでキー入力を処理したい場合は、そのコントロールのKeyDownイベントに処理を書きます。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Search();
e.SuppressKeyPress = true;
}
}

フォーム全体でショートカットキーを処理したい場合は、KeyPreviewProcessCmdKeyを使います。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
SaveFile();
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

ProcessCmdKeyは、ショートカットキーやコマンドキーを処理したい場合に有効です。

4-6. イベントハンドラが正しく登録されていない

KeyDownが発生しない場合、イベントハンドラが正しく登録されていない可能性もあります。

たとえば、次のようなメソッドを書いただけでは、イベントは発生しません。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("キーが押されました");
}

このメソッドをイベントに登録する必要があります。

デザイナーで設定するか、コードで次のように登録します。

C#
this.KeyDown += Form1_KeyDown;

コンストラクタに書く場合は、通常InitializeComponentの後に書きます。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

また、デザイナーでイベント名を変更したり、メソッドを削除したりすると、イベント登録が壊れることがあります。

その場合は、プロパティウィンドウのイベント一覧でKeyDownに正しいメソッドが設定されているか確認しましょう。

4-7. WPFでKeyDownが発生しない場合の確認ポイント

WPFでKeyDownが発生しない場合も、まずフォーカスを確認します。

WPFでは、キーボードイベントは基本的にフォーカスを持つ要素から発生します。

XML
<Window x:Class="SampleApp.MainWindow"
KeyDown="Window_KeyDown"
Focusable="True">
</Window>

コードビハインドでは次のように書きます。

C#
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

WPFでは、特定のコントロールがキーイベントを処理済みにしてしまうことがあります。その場合、親要素のKeyDownに届かないことがあります。

先にイベントを受け取りたい場合は、PreviewKeyDownを使います。

XML
<Window x:Class="SampleApp.MainWindow"
PreviewKeyDown="Window_PreviewKeyDown">
</Window>
C#
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.Close();
}
}

また、すでに処理済みになったイベントも受け取りたい場合は、AddHandlerhandledEventsTooを指定する方法があります。

C#
public MainWindow()
{
InitializeComponent();

this.AddHandler(
Keyboard.KeyDownEvent,
new KeyEventHandler(Window_KeyDown),
true);
}

WPFでは、e.KeyKey.Systemになる場合があります。Altキーとの組み合わせなどでは、e.SystemKeyも確認します。

C#
if (e.SystemKey == Key.F4)
{
// Alt + F4 など
}

4-8. KeyDownが発生しないときのチェックリスト

KeyDownが発生しないときは、次の順番で確認すると原因を見つけやすいです。

イベントハンドラが登録されているか
対象のフォームまたはコントロールにフォーカスがあるか
フォーム全体で受け取りたい場合はKeyPreviewがtrueか
TextBoxやButtonなど子コントロールにフォーカスが奪われていないか
矢印キーやTabキーなど特殊キーを扱おうとしていないか
コントロール側でキーが処理されていないか
e.HandledやSuppressKeyPressで意図せず処理を止めていないか
WPFの場合はPreviewKeyDownやAddHandlerが必要ではないか

特にWinFormsでフォーム全体のキー入力を受け取りたい場合は、まず次の3点を確認しましょう。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

そして、イベントハンドラに一時的な確認コードを入れます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show(e.KeyCode.ToString());
}

これで反応するか確認すると、イベント登録の問題なのか、特定キーだけの問題なのかを切り分けやすくなります。

5. Form全体でキー入力を受け取る方法

5-1. KeyPreviewプロパティの役割

WinFormsでフォーム全体のキー入力を受け取りたい場合、重要なのがKeyPreviewプロパティです。

通常、キー入力はフォーカスを持っているコントロールに送られます。たとえば、TextBoxにフォーカスがあると、フォームではなくTextBoxのKeyDownが発生します。

しかし、フォームのKeyPreviewtrueにすると、フォームが子コントロールより先にキーイベントを受け取れるようになります。

C#
this.KeyPreview = true;

これにより、TextBoxに入力中でも、フォーム側でEscキーやCtrl+Sなどのショートカットキーを処理しやすくなります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

KeyPreviewは、フォーム全体の共通ショートカットを実装するときに非常によく使われます。

5-2. KeyPreviewをtrueに設定する方法

KeyPreviewを設定する方法は、デザイナーから設定する方法と、コードで設定する方法があります。

デザイナーで設定する場合は、フォームを選択し、プロパティウィンドウからKeyPreviewTrueにします。

コードで設定する場合は、コンストラクタに書きます。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
}

そのうえで、フォームのKeyDownイベントを設定します。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
MessageBox.Show("ヘルプを表示します");
}
}

KeyPreviewを設定してもイベントハンドラが登録されていなければ処理は実行されません。KeyPreviewKeyDownイベント登録の両方を確認しましょう。

5-3. フォーム全体でショートカットキーを処理する実装例

フォーム全体でショートカットキーを処理する例を見てみましょう。

次のコードでは、Ctrl+Sで保存、Ctrl+Nで新規作成、Escでフォームを閉じます。

C#
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
else if (e.Control && e.KeyCode == Keys.N)
{
NewFile();
e.SuppressKeyPress = true;
}
else if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

private void SaveFile()
{
MessageBox.Show("保存しました");
}

private void NewFile()
{
MessageBox.Show("新規作成しました");
}
}

ポイントは、this.KeyPreview = true;を設定していることです。

これにより、フォーム上のTextBoxやButtonにフォーカスがあっても、フォーム側でショートカットキーを処理しやすくなります。

ただし、すべてのキーが必ずフォームに届くわけではありません。特にTabキーや矢印キーなどは、別の方法が必要になる場合があります。

5-4. TextBox入力中でもキーイベントを拾う方法

TextBoxにフォーカスがある状態でもフォーム側でキーイベントを拾いたい場合は、KeyPreviewを使います。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

たとえば、TextBox入力中でもEscキーでフォームを閉じたい場合は、次のように書きます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

TextBox入力中でもCtrl+Sで保存したい場合は、次のように書きます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
}

一方で、EnterキーをTextBox固有の処理として扱いたい場合は、TextBoxのKeyDownイベントに書くほうが自然です。

C#
private void textBoxSearch_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Search();
e.SuppressKeyPress = true;
}
}

フォーム全体の共通処理なのか、特定の入力欄だけの処理なのかを分けて考えることが大切です。

5-5. KeyPreviewで解決できないケース

KeyPreviewは便利ですが、万能ではありません。

次のようなケースでは、KeyPreviewtrueにしても期待どおりにKeyDownが発生しないことがあります。

Tabキーがフォーカス移動として処理される
矢印キーがコントロール内の移動に使われる
Enterキーが既定ボタンのクリックとして処理される
DataGridViewなどの複雑なコントロールがキーを内部処理する
コントロールがキーイベントを処理済みにしている

このような場合は、PreviewKeyDownIsInputKeyProcessCmdKeyなどを使う必要があります。

たとえば、ショートカットキーを確実に処理したい場合は、ProcessCmdKeyが有効です。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
SaveFile();
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

return true;を返すと、そのキー入力を処理済みとして扱います。KeyDownでうまく拾えないショートカットキーは、ProcessCmdKeyで処理することを検討しましょう。

6. 特殊キーや反応しにくいキーを処理する方法

6-1. 矢印キーがKeyDownで反応しない理由

矢印キーは、KeyDownで取得できる場合もありますが、コントロールの種類やフォーカス状態によっては反応しないことがあります。

理由は、矢印キーが「入力文字」ではなく、コントロールの操作に使われるキーだからです。

たとえば、TextBoxでは左右矢印キーがカーソル移動に使われます。DataGridViewではセル移動に使われます。ButtonやRadioButtonでは、フォーカス移動に使われる場合があります。

そのため、矢印キーは通常のKeyDownイベントより前に、コントロール側で処理されることがあります。

フォーム上で矢印キーを使ってオブジェクトを移動したい場合、単純に次のコードを書いても反応しないことがあります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
label1.Left -= 5;
}
}

この場合は、KeyPreviewを設定したり、対象コントロールでPreviewKeyDownを使ったり、フォームのProcessCmdKeyで処理したりします。

6-2. TabキーがKeyDownで取得しにくい理由

Tabキーは、通常「次のコントロールへフォーカスを移動するキー」として扱われます。

そのため、KeyDownでTabキーを取得しようとしても、イベントが発生しないことがあります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
{
MessageBox.Show("Tabキー");
}
}

このコードが動かない場合、Tabキーがフォーカス移動として先に処理されている可能性があります。

Tabキーを独自の操作に使いたい場合は、ProcessCmdKeyを使う方法があります。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Tab)
{
MessageBox.Show("Tabキーを処理しました");
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

ただし、Tabキーはユーザーが画面内を移動するための重要なキーです。むやみに独自処理に使うと、操作性やアクセシビリティが悪くなる可能性があります。

本当にTabキーを独自処理に使う必要があるか、慎重に判断しましょう。

6-3. PreviewKeyDownイベントを使う方法

WinFormsには、KeyDownの前段階でキーを確認できるPreviewKeyDownイベントがあります。

PreviewKeyDownは、矢印キーやTabキーなど、通常は入力キーとして扱われにくいキーを入力キーとして扱いたい場合に使えます。

たとえば、Buttonで矢印キーを取得したい場合は、次のように書きます。

C#
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Up ||
e.KeyCode == Keys.Down ||
e.KeyCode == Keys.Left ||
e.KeyCode == Keys.Right)
{
e.IsInputKey = true;
}
}

そのうえで、KeyDownイベントに処理を書きます。

C#
private void button1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
MessageBox.Show("左キーが押されました");
}
}

PreviewKeyDowne.IsInputKey = true;を設定することで、そのキーを通常の入力キーとして扱わせることができます。

矢印キーがKeyDownで反応しない場合に試したい方法のひとつです。

6-4. IsInputKeyを使って特殊キーを入力キーとして扱う方法

より本格的に特殊キーを入力キーとして扱いたい場合は、コントロールを継承してIsInputKeyをオーバーライドします。

たとえば、矢印キーを入力キーとして扱うカスタムButtonを作る例です。

C#
public class ArrowButton : Button
{
protected override bool IsInputKey(Keys keyData)
{
switch (keyData & Keys.KeyCode)
{
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
return true;
}

return base.IsInputKey(keyData);
}
}

このようにすると、矢印キーが入力キーとして扱われ、KeyDownで取得しやすくなります。

C#
private void arrowButton1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
MessageBox.Show("上キー");
}
}

IsInputKeyのオーバーライドは、特定のコントロールで特殊キーを継続的に扱いたい場合に向いています。

一時的な対応ならPreviewKeyDown、ショートカットキー全体を処理するならProcessCmdKeyを使うなど、目的に応じて使い分けましょう。

6-5. ProcessCmdKeyでショートカットキーを処理する方法

ProcessCmdKeyは、コマンドキーやショートカットキーを処理するためのメソッドです。

KeyDownで取得しにくいキーや、フォーム全体のショートカットキーを処理したい場合に便利です。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
SaveFile();
return true;
}

if (keyData == Keys.Escape)
{
this.Close();
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

return true;を返すと、そのキーは処理済みになります。

return base.ProcessCmdKey(ref msg, keyData);を返すと、通常の処理に戻します。

矢印キーを処理する例です。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Left:
label1.Left -= 5;
return true;

case Keys.Right:
label1.Left += 5;
return true;

case Keys.Up:
label1.Top -= 5;
return true;

case Keys.Down:
label1.Top += 5;
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

ProcessCmdKeyは強力ですが、使いすぎると通常のコントロール操作を妨げることがあります。ショートカットキーや特殊キーを明確に処理したい場面で使うのがおすすめです。

6-6. KeyDown・PreviewKeyDown・ProcessCmdKeyの使い分け

KeyDownPreviewKeyDownProcessCmdKeyは、どれもキー入力に関係しますが、目的が異なります。

通常のキー入力を扱いたい場合は、まずKeyDownを使います。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Search();
}
}

矢印キーなどを入力キーとして扱いたい場合は、PreviewKeyDownを使います。

C#
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
{
e.IsInputKey = true;
}
}

ショートカットキーやコマンドキーをフォーム全体で処理したい場合は、ProcessCmdKeyを使います。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
SaveFile();
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

使い分けの目安は次のとおりです。

通常のキー判定:KeyDown
矢印キーなどを入力キー扱いにしたい:PreviewKeyDown
特定コントロールで特殊キーを常に扱いたい:IsInputKey
ショートカットキーを強めに処理したい:ProcessCmdKey
WPFで先にキーを受け取りたい:PreviewKeyDown

まずはKeyDownで実装し、反応しないキーがある場合にKeyPreviewPreviewKeyDownProcessCmdKeyを検討するとよいでしょう。

7. KeyDown・KeyPress・KeyUpの違い

7-1. KeyDown・KeyPress・KeyUpの発生順序

キーボード操作に関する代表的なイベントは、KeyDownKeyPressKeyUpです。

基本的な発生順序は次のとおりです。

KeyDown

KeyPress

KeyUp

たとえば、Aキーを押して離すと、次の順番でイベントが発生します。

Aキーを押す

KeyDown

KeyPress

Aキーを離す

KeyUp

ただし、すべてのキーでKeyPressが発生するわけではありません。KeyPressは文字入力に関係するイベントなので、Ctrlキー、Shiftキー、Altキー、矢印キー、ファンクションキーなどでは発生しない場合があります。

一方、KeyDownKeyUpは、特殊キーを含む多くのキーで発生します。

7-2. KeyDownとKeyPressの違い

KeyDownKeyPressの大きな違いは、「キー」を扱うか「文字」を扱うかです。

KeyDownは、どのキーが押されたかを扱います。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキー");
}
}

KeyPressは、どの文字が入力されたかを扱います。

C#
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsDigit(e.KeyChar))
{
MessageBox.Show("数字が入力されました");
}
}

たとえば、Shift+Aを押した場合、KeyDownでは「Aキーが押された」と判定します。一方、KeyPressでは入力文字として「A」を扱います。

数字だけ入力させたい、特定の文字を入力禁止にしたい、といった文字入力制御ではKeyPressが使われることがあります。

ただし、日本語入力や貼り付け入力、IME変換などを考慮すると、KeyPressだけで入力制御を完結させるのは難しい場合があります。

7-3. KeyDownとKeyUpの違い

KeyDownはキーを押したときに発生し、KeyUpはキーを離したときに発生します。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
label1.Text = "キーが押されました";
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
label1.Text = "キーが離されました";
}

ゲームや操作画面では、KeyDownKeyUpを組み合わせて「押している間だけ移動する」という処理を作ることがあります。

C#
private bool isRightPressed = false;

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Right)
{
isRightPressed = true;
}
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Right)
{
isRightPressed = false;
}
}

KeyDownだけを使うと、キーを長押ししたときに連続してイベントが発生することがあります。押下状態を管理したい場合は、KeyUpも併用すると安定します。

7-4. KeyPressが向いているケース

KeyPressは、文字入力を扱いたい場合に向いています。

代表的な用途は次のとおりです。

数字だけ入力させたい
英数字だけ入力させたい
特定の記号を入力禁止にしたい
入力された文字を判定したい

たとえば、TextBoxに数字だけ入力させる簡単な例は次のとおりです。

C#
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
{
e.Handled = true;
}
}

このコードでは、制御文字と数字以外の入力を拒否しています。

ただし、右クリック貼り付けやIME入力、ドラッグ&ドロップ、プログラムからの代入などはKeyPressだけでは完全に制御できないことがあります。

厳密な入力検証が必要な場合は、TextChangedValidating、入力後のバリデーションも組み合わせましょう。

7-5. KeyDownが向いているケース

KeyDownは、キー操作やショートカットキーを扱いたい場合に向いています。

代表的な用途は次のとおりです。

Enterキーで処理を実行する
Escキーでキャンセルする
Ctrl+Sで保存する
F1キーでヘルプを表示する
矢印キーで移動する
ShiftやCtrlとの組み合わせを判定する
キーを押した瞬間に処理したい

たとえば、Ctrl+Sの判定はKeyDownが向いています。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
}

特殊キーや修飾キーを扱うなら、基本的にはKeyDownを使うと覚えておくとよいでしょう。

7-6. KeyPressが非推奨・使いにくい場面

KeyPressは便利ですが、すべてのキー入力に向いているわけではありません。

特に次のような場面では使いにくいです。

CtrlキーやShiftキーを判定したい
矢印キーを判定したい
ファンクションキーを判定したい
ショートカットキーを実装したい
キーを押した瞬間と離した瞬間を分けたい
IME入力や日本語入力を厳密に扱いたい
貼り付け入力も含めて入力制限したい

KeyPressは文字入力に寄ったイベントなので、キーそのものの判定には向いていません。

また、日本語入力では、キーを押した時点の文字と最終的に確定される文字が異なる場合があります。そのため、日本語を含む入力チェックをKeyPressだけで行うと、期待どおりに動かないことがあります。

文字入力の最終チェックには、入力後の値を検証する方法も検討しましょう。

7-7. 目的別の使い分け早見表

KeyDownKeyPressKeyUpの使い分けは、目的から考えると分かりやすくなります。

Enterキーを判定したい:KeyDown
Escキーを判定したい:KeyDown
Ctrl+Sを実装したい:KeyDownまたはProcessCmdKey
矢印キーを判定したい:KeyDown、PreviewKeyDown、ProcessCmdKey
入力文字を判定したい:KeyPress
数字だけ入力させたい:KeyPress、TextChanged、Validating
キーを離したタイミングを知りたい:KeyUp
押している間だけ処理したい:KeyDownとKeyUp
日本語入力後の値を検証したい:TextChanged、Validating
フォーム全体でキーを拾いたい:KeyPreview+KeyDown

基本的には、キー操作ならKeyDown、文字入力ならKeyPress、キーを離したタイミングならKeyUpです。

ただし、入力制限やショートカットキーなどは、コントロールの種類や目的に応じて別のイベントやメソッドも組み合わせる必要があります。

8. KeyDownイベントの実用サンプル

8-1. Enterキーでボタン処理を実行する

検索画面やログイン画面では、TextBoxでEnterキーを押したらボタン処理を実行したいことがあります。

C#
private void textBoxSearch_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
buttonSearch.PerformClick();
e.SuppressKeyPress = true;
}
}

private void buttonSearch_Click(object sender, EventArgs e)
{
MessageBox.Show("検索を実行しました");
}

buttonSearch.PerformClick();を使うと、ボタンのクリック処理をコードから呼び出せます。

e.SuppressKeyPress = true;を設定しているのは、Enterキーによる不要な音や改行を防ぐためです。

フォームに既定ボタンを設定したいだけなら、AcceptButtonプロパティを使う方法もあります。

C#
this.AcceptButton = buttonSearch;

この設定をすると、Enterキーで指定したボタンをクリックできます。

8-2. Escキーでフォームを閉じる

Escキーでフォームを閉じる処理は、設定画面やダイアログでよく使われます。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}

フォーム全体でEscキーを受け取りたい場合は、KeyPreviewtrueにしておくと便利です。

また、キャンセルボタンがある場合は、CancelButtonプロパティを使う方法もあります。

C#
this.CancelButton = buttonCancel;

この設定をすると、Escキーで指定したボタンのクリック処理が実行されます。

ダイアログ画面では、KeyDownで直接閉じるよりも、CancelButtonを使ったほうが自然な場合もあります。

8-3. Ctrl+Sで保存処理を実行する

Ctrl+Sは、保存処理のショートカットキーとしてよく使われます。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
}

private void SaveFile()
{
MessageBox.Show("保存しました");
}

e.ControlでCtrlキーが押されているかを確認し、e.KeyCode == Keys.SでSキーを判定しています。

KeyDataを使うと、次のようにも書けます。

C#
if (e.KeyData == (Keys.Control | Keys.S))
{
SaveFile();
e.SuppressKeyPress = true;
}

フォーム全体でより確実にショートカットキーを処理したい場合は、ProcessCmdKeyも有効です。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
SaveFile();
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

8-4. 矢印キーでオブジェクトを移動する

矢印キーでラベルや画像を移動するサンプルです。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
int move = 5;

switch (e.KeyCode)
{
case Keys.Left:
label1.Left -= move;
break;

case Keys.Right:
label1.Left += move;
break;

case Keys.Up:
label1.Top -= move;
break;

case Keys.Down:
label1.Top += move;
break;
}
}

もし矢印キーが反応しない場合は、ProcessCmdKeyで処理します。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
int move = 5;

switch (keyData)
{
case Keys.Left:
label1.Left -= move;
return true;

case Keys.Right:
label1.Left += move;
return true;

case Keys.Up:
label1.Top -= move;
return true;

case Keys.Down:
label1.Top += move;
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

ゲームや簡易エディタのような画面では、矢印キーの入力がコントロールのフォーカス移動に使われないように注意しましょう。

8-5. 数字キーで処理を分岐する

数字キーでメニューを選択するような処理も、KeyDownで実装できます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.D1:
case Keys.NumPad1:
ShowMenu1();
break;

case Keys.D2:
case Keys.NumPad2:
ShowMenu2();
break;

case Keys.D3:
case Keys.NumPad3:
ShowMenu3();
break;
}
}

private void ShowMenu1()
{
MessageBox.Show("メニュー1");
}

private void ShowMenu2()
{
MessageBox.Show("メニュー2");
}

private void ShowMenu3()
{
MessageBox.Show("メニュー3");
}

キーボード上部の数字キーはKeys.D1、テンキーはKeys.NumPad1のように別々の値になります。

両方を同じ処理にしたい場合は、上記のように複数のcaseを並べると分かりやすいです。

8-6. 入力されたキー情報を画面に表示する

デバッグや学習目的では、押されたキーの情報を画面に表示すると理解しやすくなります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
label1.Text =
$"KeyCode: {e.KeyCode}\n" +
$"KeyData: {e.KeyData}\n" +
$"KeyValue: {e.KeyValue}\n" +
$"Modifiers: {e.Modifiers}\n" +
$"Control: {e.Control}\n" +
$"Shift: {e.Shift}\n" +
$"Alt: {e.Alt}";
}

このコードを使うと、通常キー、修飾キー、ショートカットキーを押したときに、KeyEventArgsにどのような値が入るか確認できます。

KeyDownが発生しない場合の切り分けにも役立ちます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine(e.KeyData);
}

Visual Studioの出力ウィンドウで確認したい場合は、Console.WriteLineではなくDebug.WriteLineを使うこともあります。

C#
System.Diagnostics.Debug.WriteLine(e.KeyData);

8-7. ゲームや操作画面でキー入力を扱う例

ゲームや操作画面では、キーを押している間だけ移動する処理が必要になることがあります。

その場合は、KeyDownKeyUpを組み合わせて、押下中のキーを管理します。

C#
private bool leftPressed;
private bool rightPressed;
private bool upPressed;
private bool downPressed;

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left:
leftPressed = true;
break;

case Keys.Right:
rightPressed = true;
break;

case Keys.Up:
upPressed = true;
break;

case Keys.Down:
downPressed = true;
break;
}
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left:
leftPressed = false;
break;

case Keys.Right:
rightPressed = false;
break;

case Keys.Up:
upPressed = false;
break;

case Keys.Down:
downPressed = false;
break;
}
}

private void timer1_Tick(object sender, EventArgs e)
{
int move = 5;

if (leftPressed) label1.Left -= move;
if (rightPressed) label1.Left += move;
if (upPressed) label1.Top -= move;
if (downPressed) label1.Top += move;
}

この方法では、キーイベントが発生した瞬間だけでなく、タイマー処理によって押下状態に応じた連続移動ができます。

長押しによるKeyDownの連続発火に直接頼るより、操作が安定しやすくなります。

9. KeyDownイベント実装時の注意点

9-1. KeyDownで文字入力チェックを行うときの注意

KeyDownで文字入力チェックを行うことはできますが、文字そのものの検証には注意が必要です。

KeyDownは「どのキーが押されたか」を扱うイベントです。そのため、入力された文字を正確に扱うイベントではありません。

たとえば、Aキーを押したとき、KeyDownではKeys.Aとして判定されます。Shiftキーを押しているかどうかは分かりますが、最終的に入力される文字が大文字なのか小文字なのか、日本語入力中なのかまでは単純に判定できません。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
{
MessageBox.Show("Aキーが押されました");
}
}

これは「Aというキーが押された」ことの判定であり、「aという文字が入力された」ことの判定ではありません。

文字入力を制限したい場合は、KeyPressTextChangedValidatingなども検討しましょう。

9-2. IME入力や日本語入力との関係

日本語入力では、キーを押した時点で最終的な文字が決まるとは限りません。

たとえば、「k」「a」と入力して「か」に変換されたり、スペースキーで漢字変換したりします。この場合、KeyDownで個々のキーを判定しても、最終的にTextBoxに入力される文字とは一致しません。

そのため、日本語入力を含むTextBoxの入力制限をKeyDownだけで行うのは難しいです。

たとえば、次のような処理は注意が必要です。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
e.SuppressKeyPress = true;
}
}

このような単純な判定では、テンキー、Backspace、Delete、カーソル移動、IME入力、貼り付けなどを正しく扱えない場合があります。

日本語入力を含めて検証したい場合は、入力が確定した後の文字列を確認する方法が現実的です。

C#
private void textBox1_TextChanged(object sender, EventArgs e)
{
// 入力後のTextを検証する
}

厳密な入力チェックは、キーイベントだけでなく、入力後の値に対して行うことをおすすめします。

9-3. TextBoxの入力制御にはKeyPressやTextChangedも検討する

TextBoxに数字だけ入力させたい場合、KeyDownよりもKeyPressのほうが書きやすいことがあります。

C#
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
{
e.Handled = true;
}
}

ただし、KeyPressでも貼り付けやIME入力を完全には防げない場合があります。

入力後に値を補正したい場合は、TextChangedを使います。

C#
private void textBox1_TextChanged(object sender, EventArgs e)
{
string onlyDigits = new string(textBox1.Text.Where(char.IsDigit).ToArray());

if (textBox1.Text != onlyDigits)
{
int selectionStart = textBox1.SelectionStart;
textBox1.Text = onlyDigits;
textBox1.SelectionStart = Math.Min(selectionStart, textBox1.Text.Length);
}
}

業務アプリでは、入力途中に厳しく制限しすぎると使いにくくなる場合があります。最終的な値の妥当性を確認するなら、Validatingイベントや保存前のチェックも有効です。

C#
private void textBox1_Validating(object sender, CancelEventArgs e)
{
if (!int.TryParse(textBox1.Text, out _))
{
MessageBox.Show("数値を入力してください");
e.Cancel = true;
}
}

9-4. e.HandledとSuppressKeyPressの違い

KeyDownイベントでは、e.Handlede.SuppressKeyPressを使うことがあります。

e.Handledは、そのイベントを処理済みとして扱うためのプロパティです。

C#
e.Handled = true;

一方、e.SuppressKeyPressは、キー入力に関連する後続処理を抑制します。

C#
e.SuppressKeyPress = true;

たとえば、TextBoxでEnterキーを押したときに検索処理を実行し、ビープ音や改行を防ぎたい場合は、SuppressKeyPressを使うことが多いです。

C#
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Search();
e.SuppressKeyPress = true;
}
}

SuppressKeyPresstrueにすると、通常はHandledtrueとして扱われます。

キー入力そのものを抑えたい場合は、e.Handledだけでなくe.SuppressKeyPressを使うと期待どおりに動きやすいです。

ただし、すべての場面でむやみに設定すると、通常の入力やショートカットが動かなくなることがあります。必要なキーにだけ設定しましょう。

9-5. 同じ処理が複数回実行される場合の対策

KeyDownで同じ処理が複数回実行される場合、主な原因は次の2つです。

イベントハンドラを複数回登録している
キー長押しによってKeyDownが連続発生している

イベントハンドラを複数回登録している例です。

C#
this.KeyDown += Form1_KeyDown;
this.KeyDown += Form1_KeyDown;

この場合、1回キーを押しただけで同じ処理が2回実行されます。

動的にイベント登録する場合は、重複登録に注意しましょう。必要に応じて、登録前に解除する方法もあります。

C#
this.KeyDown -= Form1_KeyDown;
this.KeyDown += Form1_KeyDown;

また、キーを長押しすると、OSのキーリピートによってKeyDownが連続して発生します。

一度だけ実行したい処理では、フラグを使って制御します。

C#
private bool isProcessing = false;

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space && !isProcessing)
{
isProcessing = true;
MessageBox.Show("Spaceキー処理");
}
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
isProcessing = false;
}
}

9-6. 長押しによる連続発火への対応

KeyDownは、キーを長押しすると連続して発生することがあります。

文字入力では自然な動きですが、ショートカットキーや実行処理では問題になる場合があります。

たとえば、Spaceキーで処理を開始するコードがあるとします。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
StartAction();
}
}

この場合、Spaceキーを長押しするとStartActionが何度も呼ばれる可能性があります。

一度だけ実行したい場合は、押下状態を管理します。

C#
private bool spacePressed = false;

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space && !spacePressed)
{
spacePressed = true;
StartAction();
}
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
spacePressed = false;
}
}

一方、ゲームの移動処理のように、押している間ずっと動かしたい場合は、KeyDownの連続発火に頼るより、KeyDownでフラグを立て、タイマーで移動する方法が安定します。

9-7. 可読性の高いキー判定コードの書き方

キー判定が増えてくると、if文が複雑になりがちです。

悪い例です。

C#
if (e.Control && e.Shift && e.KeyCode == Keys.S || e.Control && e.KeyCode == Keys.F12)
{
SaveFile();
}

条件が増えると、後から読んだときに分かりにくくなります。

キーごとに処理を分けたり、メソッド化したりすると可読性が上がります。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (IsSaveShortcut(e))
{
SaveFile();
e.SuppressKeyPress = true;
return;
}

if (e.KeyCode == Keys.Escape)
{
Close();
return;
}
}

private bool IsSaveShortcut(KeyEventArgs e)
{
return e.Control && e.KeyCode == Keys.S;
}

switch文を使うのも有効です。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control)
{
HandleControlShortcut(e);
return;
}

switch (e.KeyCode)
{
case Keys.Enter:
Search();
break;

case Keys.Escape:
Close();
break;
}
}

private void HandleControlShortcut(KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.S:
SaveFile();
e.SuppressKeyPress = true;
break;

case Keys.O:
OpenFile();
e.SuppressKeyPress = true;
break;
}
}

キー判定は、後から機能追加されやすい部分です。処理が増えてきたら、早めにメソッド分割しておくと保守しやすくなります。

10. KeyDownイベントに関するよくある質問

10-1. KeyDownイベントはどのタイミングで発生しますか?

KeyDownイベントは、キーボードのキーが押されたときに発生します。

キーを押した瞬間に発生し、キーを離したときにはKeyUpが発生します。文字として入力可能なキーであれば、一般的にはKeyDownの後にKeyPressが発生します。

KeyDown → KeyPress → KeyUp

ただし、矢印キー、Ctrlキー、Shiftキー、Altキー、ファンクションキーなどではKeyPressが発生しない場合があります。

特殊キーやショートカットキーを判定したい場合は、KeyDownを使うのが基本です。

10-2. KeyDownで入力文字を取得できますか?

KeyDownで取得できるのは、基本的には「押されたキー」です。

たとえば、Aキーを押した場合は、e.KeyCode == Keys.Aとして判定できます。

C#
if (e.KeyCode == Keys.A)
{
MessageBox.Show("Aキー");
}

しかし、これは入力された文字そのものではありません。

大文字・小文字、IME入力、日本語変換、キーボード配列などを考えると、KeyDownだけで正確な入力文字を扱うのは難しいです。

入力文字を扱いたい場合は、KeyPresse.KeyCharや、TextBoxのTextChangedで入力後の文字列を確認する方法を検討しましょう。

10-3. KeyDownとKeyPressはどちらを使うべきですか?

目的によって使い分けます。

キー操作やショートカットキーを扱いたい場合は、KeyDownを使います。

Enterキー
Escキー
Ctrl+S
矢印キー
F1キー
ShiftやCtrlとの組み合わせ

文字入力を扱いたい場合は、KeyPressを使います。

数字だけ入力させる
特定の文字を禁止する
入力された文字を判定する

ただし、TextBoxの入力制限では、KeyPressだけでなくTextChangedValidatingも検討するべきです。貼り付けやIME入力を考慮すると、最終的な文字列を検証する処理が必要になることがあります。

10-4. フォームでKeyDownが発生しないのはなぜですか?

フォームでKeyDownが発生しない主な原因は、フォームではなく子コントロールにフォーカスがあるためです。

たとえば、TextBoxにフォーカスがあると、キー入力はTextBoxに送られます。そのため、フォームのKeyDownが期待どおりに発生しないことがあります。

フォーム全体でキー入力を受け取りたい場合は、KeyPreviewtrueにします。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
}

それでも発生しない場合は、イベントハンドラが登録されているか、対象キーがコントロール側で処理されていないかを確認しましょう。

10-5. 矢印キーやTabキーを取得するにはどうすればいいですか?

矢印キーやTabキーは、フォーカス移動やコントロール操作に使われるため、通常のKeyDownでは取得しにくいことがあります。

矢印キーを取得したい場合は、まずKeyPreviewを試します。

C#
this.KeyPreview = true;

それでも反応しない場合は、PreviewKeyDownで入力キーとして扱います。

C#
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Left ||
e.KeyCode == Keys.Right ||
e.KeyCode == Keys.Up ||
e.KeyCode == Keys.Down)
{
e.IsInputKey = true;
}
}

Tabキーやショートカットキーを処理したい場合は、ProcessCmdKeyも有効です。

C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Tab)
{
MessageBox.Show("Tabキー");
return true;
}

return base.ProcessCmdKey(ref msg, keyData);
}

ただし、Tabキーは通常のフォーカス移動に使われる重要なキーなので、独自処理に使う場合は操作性に注意しましょう。

10-6. TextBox入力中にフォームのKeyDownを発生させるには?

TextBox入力中でもフォーム側でKeyDownを発生させたい場合は、フォームのKeyPreviewtrueにします。

C#
public Form1()
{
InitializeComponent();

this.KeyPreview = true;
}

そのうえで、フォームのKeyDownイベントに処理を書きます。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
e.SuppressKeyPress = true;
}
}

これにより、TextBoxにフォーカスがあっても、フォーム側でCtrl+Sなどのショートカットキーを処理しやすくなります。

ただし、TextBoxが内部で処理するキーや、Tabキー、矢印キーなどは、KeyPreviewだけでは取得できないことがあります。その場合は、TextBox側のイベントやProcessCmdKeyを検討します。

10-7. WPFでも同じようにKeyDownを使えますか?

WPFでもKeyDownイベントは使えます。

ただし、WinFormsとはキー判定の書き方が少し違います。

WinFormsでは次のように書きます。

C#
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enter");
}

WPFでは次のように書きます。

C#
if (e.Key == Key.Enter)
{
MessageBox.Show("Enter");
}

WPFでは、Keys.EnterではなくKey.Enterを使います。

また、WPFにはPreviewKeyDownがあります。親要素で先にキーイベントを受け取りたい場合や、子要素で処理される前にキーを判定したい場合に便利です。

C#
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.Close();
}
}

Ctrlキーなどの修飾キーは、Keyboard.Modifiersで判定できます。

C#
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.S)
{
SaveFile();
}

WPFではイベントの伝わり方がWinFormsと異なるため、KeyDownで反応しない場合はPreviewKeyDownAddHandlerの利用も検討しましょう。

まとめ

C#のKeyDownイベントは、キーボードのキーが押されたときに発生するイベントです。

Enterキー、Escキー、矢印キー、ファンクションキー、Ctrl+Sのようなショートカットキーを扱うときに非常によく使われます。

基本的なキー判定は、WinFormsではe.KeyCodeを使います。

C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Enterキーが押されました");
}
}

CtrlやShiftなどの修飾キーを含む判定では、e.Controle.Shifte.Altを使うと分かりやすく書けます。

C#
if (e.Control && e.KeyCode == Keys.S)
{
SaveFile();
}

フォーム全体でキー入力を受け取りたい場合は、KeyPreviewtrueにします。

C#
this.KeyPreview = true;

ただし、矢印キーやTabキーのような特殊キーは、コントロール側で処理されることがあります。その場合は、PreviewKeyDownIsInputKeyProcessCmdKeyなどを使い分けましょう。

KeyDownKeyPressKeyUpの違いも重要です。

KeyDown:キーが押されたとき
KeyPress:文字が入力されたとき
KeyUp:キーが離されたとき

キー操作やショートカットキーならKeyDown、文字入力の判定ならKeyPress、押している状態を管理したいならKeyDownKeyUpの組み合わせが向いています。

KeyDownが発生しない場合は、フォーカス、KeyPreview、イベント登録、コントロール側のキー処理を順番に確認すると原因を見つけやすくなります。

C#で使いやすいWindowsアプリを作るうえで、KeyDownイベントは欠かせない基本機能です。キー判定の基本と発生しない原因への対処法を押さえておけば、フォーム全体のショートカットや入力操作をスムーズに実装できるようになります。