【学習】WinFormsでUserControlを使ってみよう(複数コントロールを1つにまとめる)
これまでの学習では、
- フォームに1つずつコントロールを配置する
- Button、TextBox、Label などを個別に使う
という形でプログラムを動かしてきました。
しかし、同じ組み合わせ(例:ラベル+テキストボックス+クリアボタン)を複数箇所で使いたい場面があります。
複数コントロールを1つにまとめて再利用する
という仕組みです。
今回は UserControl(ユーザーコントロール) を使って、「タイトル付きテキストボックス+クリアボタン」を1つの部品として作るアプリを作ります。
今日作るもの
「クリア可能なテキストボックス」 を UserControl として作成し、フォームに2つ配置します。
- 姓 と 名 の2つの入力欄
- 各欄に「↻」ボタンがあり、押すとテキストがクリアされる
- 両方入力すると「Hello 姓 名」と表示される
ソリューションとプロジェクトを作る
Visual Studio で新しいプロジェクトを作ります。
- テンプレート: Windows Forms アプリ (.NET Framework)
- ソリューション名: UserControlSample
- プロジェクト名: UserControlDemo
作成すると Form1 が表示されます。
UserControl を追加する
- ソリューションエクスプローラーで プロジェクト名 を右クリック
- 追加 → ユーザー コントロール(Windows フォーム) を選択
- 名前を ClearableTextBox に設定して 追加 をクリック
- デザイナーが開き、白いデザイン画面が表示される
UserControl にコントロールを配置する
ClearableTextBox のデザイン画面に、次のコントロールを配置します。
| コントロール | 名前 | プロパティ |
|---|---|---|
| Label | lblTitle | Location: 3, 5 |
| TextBox | txtValue | Location: 3, 23、Size: 148, 23、Anchor: Top, Left, Right |
| Button | btnClear | Location: 157, 23、Size: 31, 23、Text: ↻、Anchor: Top, Right |
UserControl の Size を 191, 53 に、MinimumSize を 84, 53 に設定します。
lblTitle の Text は空欄のままで構いません(フォーム側で Title プロパティで設定します)。
UserControl のコードを書く
F7 キーを押してコードエディタを開き、次のコードを追加します。
プロパティの公開
フォームから使うために、Title と Text プロパティを公開します。
public string Title
{
get => lblTitle.Text;
set => lblTitle.Text = value;
}
public new string Text
{
get => txtValue.Text;
set => txtValue.Text = value;
}
UserControl は Control を継承しており、Control にはすでに Text プロパティがあります。
// Control クラス(基底クラス)には既に Text がある
public virtual string Text { get; set; }
ClearableTextBox で、内部の txtValue.Text を外に出すために同じ名前の Text を定義すると、基底クラスの Text を隠すことになります。
その「意図的に隠している」ことをコンパイラに伝えるために new を付けます。
new を付けない場合
new を付けないと、次のような警告が出ます。
CS0108: 'ClearableTextBox.Text’ は継承されたメンバー 'Control.Text’ を非表示にしています。
意図的に非表示にする場合は、new キーワードを使用してください。
同じく new が必要なイベント
TextChanged イベントも、基底クラス Control に既に定義されているため、同様に new が必要です。
public new event EventHandler TextChanged
{
add => txtValue.TextChanged += value;
remove => txtValue.TextChanged -= value;
}
まとめ
| キーワード | 役割 |
|---|---|
new | 継承されたメンバーを意図的に隠すことを示す |
override | 仮想メンバーを上書きする(Text は virtual だが、内部の TextBox に委譲したいため override は使わない) |
override ではなく new を使うのは、基底の Text を上書きするのではなく、「この UserControl の Text は内部の TextBox の Text を指す」という別の意味を持たせたいためです。
イベントの転送
テキストが変わったことをフォームに伝えるため、TextChanged イベントを転送します。
public new event EventHandler TextChanged
{
add => txtValue.TextChanged += value;
remove => txtValue.TextChanged -= value;
}
クリアボタンの処理
btnClear をダブルクリックして Click イベントを作成し、次のコードを書きます。
private void btnClear_Click(object sender, EventArgs e) =>
Text = "";
ビルドしてツールボックスに表示する
ビルド → ソリューションのビルド を実行します。エラーがなければ、ツールボックスの下部に ClearableTextBox が表示されます。
フォームに UserControl を配置する
Form1 のデザイナーを開き、ツールボックスから ClearableTextBox を2つドラッグして配置します。
| コントロール | 名前 | Location | Title |
|---|---|---|---|
| ClearableTextBox | ctlFirstName | 12, 12 | First Name |
| ClearableTextBox | ctlLastName | 12, 71 | Last Name |
| Label | lblFullName | 12, 252 | (空欄) |
ctlFirstName と ctlLastName の Title プロパティは、プロパティウィンドウで「First Name」「Last Name」に設定します。
イベントを登録する
- ctlFirstName をダブルクリック →
ctlFirstName_TextChangedが作成される - ctlLastName をダブルクリック →
ctlLastName_TextChangedが作成される - フォームのタイトルバーをダブルクリック →
Form1_Loadが作成される
完成コード
ClearableTextBox.cs(UserControl 側)
using System;
using System.Windows.Forms;
namespace UserControlDemo
{
public partial class ClearableTextBox : UserControl
{
public ClearableTextBox()
{
InitializeComponent();
}
public string Title
{
get => lblTitle.Text;
set => lblTitle.Text = value;
}
public new string Text
{
get => txtValue.Text;
set => txtValue.Text = value;
}
public new event EventHandler TextChanged
{
add => txtValue.TextChanged += value;
remove => txtValue.TextChanged -= value;
}
private void btnClear_Click(object sender, EventArgs e) =>
Text = "";
}
}
Form1.cs(フォーム側)
using System;
using System.Windows.Forms;
namespace UserControlDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void UpdateNameLabel()
{
if (string.IsNullOrWhiteSpace(ctlFirstName.Text) || string.IsNullOrWhiteSpace(ctlLastName.Text))
lblFullName.Text = "姓と名の両方を入力してください。";
else
lblFullName.Text = $"Hello {ctlFirstName.Text} {ctlLastName.Text}";
}
private void ctlFirstName_TextChanged(object sender, EventArgs e) =>
UpdateNameLabel();
private void ctlLastName_TextChanged(object sender, EventArgs e) =>
UpdateNameLabel();
private void Form1_Load(object sender, EventArgs e) =>
UpdateNameLabel();
}
}
プログラムの流れ
姓 or 名のテキストが変わる
↓
TextChanged が発生
↓
UpdateNameLabel() が呼ばれる
↓
lblFullName.Text を更新
重要ポイント
UserControl は「複数コントロールを1つにまとめた部品」 です。
- プロパティの公開 … フォームから内部コントロールにアクセスできるようにする(Title、Text など)
- イベントの転送 … 内部コントロールのイベントをフォームに伝える(TextChanged など)
- 論理動作 … 内部でボタンクリック時の処理を実装する(クリアなど)
- ビルド するとツールボックスに表示され、他のフォームでも再利用できる
- チーム開発では「Aさんが UserControl、Bさんがフォーム」と役割分担しやすい
発展:電話帳アプリで使う
WinFormsで作る電話帳アプリ(Listで管理する版) では、1件分の表示を Label や TextBox で作っています。これを UserControl にまとめると、1件分のレイアウトを1つの部品として再利用できます。
発展:チーム開発で役割分担する
WinFormsで2人開発を体験する では、フォームとリストを分担して作ります。UserControl を導入すると、「Aさんが ClearableTextBox を作る」「Bさんがフォームに配置する」という役割分担がしやすくなります。
発展アイデア
UserControl を使うと次のようなものも作れます。
- 住所入力欄(郵便番号+住所+電話番号を1つにまとめる)
- 検索ボックス(TextBox+検索ボタン)
- 数値入力欄(Label+NumericUpDown+単位表示)
- 日付選択欄(Label+DateTimePicker)
UserControl は部品化・再利用の基本です。入門シリーズの締めとして押さえておくと、実務アプリやチーム開発に進みやすくなります。




ディスカッション
コメント一覧
まだ、コメントがありません