【学習】WinFormsでUserControlを使ってみよう(複数コントロールを1つにまとめる)

広告

これまでの学習では、

  • フォームに1つずつコントロールを配置する
  • Button、TextBox、Label などを個別に使う

という形でプログラムを動かしてきました。

しかし、同じ組み合わせ(例:ラベル+テキストボックス+クリアボタン)を複数箇所で使いたい場面があります。

複数コントロールを1つにまとめて再利用する

という仕組みです。

今回は UserControl(ユーザーコントロール) を使って、「タイトル付きテキストボックス+クリアボタン」を1つの部品として作るアプリを作ります。

今日作るもの

「クリア可能なテキストボックス」 を UserControl として作成し、フォームに2つ配置します。

  •  と  の2つの入力欄
  • 各欄に「↻」ボタンがあり、押すとテキストがクリアされる
  • 両方入力すると「Hello 姓 名」と表示される

ソリューションとプロジェクトを作る

Visual Studio で新しいプロジェクトを作ります。

  • テンプレート: Windows Forms アプリ (.NET Framework)
  • ソリューション名: UserControlSample
  • プロジェクト名: UserControlDemo

作成すると Form1 が表示されます。


UserControl を追加する

  1. ソリューションエクスプローラーで プロジェクト名 を右クリック
  2. 追加 → ユーザー コントロール(Windows フォーム) を選択
  3. 名前を ClearableTextBox に設定して 追加 をクリック
  4. デザイナーが開き、白いデザイン画面が表示される

UserControl にコントロールを配置する

ClearableTextBox のデザイン画面に、次のコントロールを配置します。

コントロール名前プロパティ
LabellblTitleLocation: 3, 5
TextBoxtxtValueLocation: 3, 23、Size: 148, 23、Anchor: Top, Left, Right
ButtonbtnClearLocation: 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つドラッグして配置します。

コントロール名前LocationTitle
ClearableTextBoxctlFirstName12, 12First Name
ClearableTextBoxctlLastName12, 71Last Name
LabellblFullName12, 252(空欄)

ctlFirstName と ctlLastName の Title プロパティは、プロパティウィンドウで「First Name」「Last Name」に設定します。


イベントを登録する

  1. ctlFirstName をダブルクリック → ctlFirstName_TextChanged が作成される
  2. ctlLastName をダブルクリック → ctlLastName_TextChanged が作成される
  3. フォームのタイトルバーをダブルクリック → 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 は部品化・再利用の基本です。入門シリーズの締めとして押さえておくと、実務アプリやチーム開発に進みやすくなります。

訪問数 2 回, 今日の訪問数 2回

広告