投稿日 2015/09/22 Visual Studio 2015 対応
C# で Windows.Forms アプリケーションを作成するとき、標準コントロールだけでも十分高機能なアプリを構築できますが、ユーザコントロールやカスタムコントロールを使うと、コントロールを組み合わせた独自コントロールや独自のスタイルや機能を持つコントロールを作成できます。
ユーザコントロールは、標準のコントロールを組み合わせて作ったコントロールで、ちょうどパネルの上にコントロールを配置して使うようなイメージです。ただし、このパネルは Panel でなく UserControl から派生させます。
これはプロジェクトを選択して追加メニューからユーザコントロールを選択します。すると、ユーザコントロールのひな型がプロジェクトに追加されます。
FileSelector はテキストボックス、ボタンおよびファイル選択コモンダイアログを組み合わせたユーザコントロールです。デザイン画面は下のようになっています。コントロールのサイズを変更したとき、デザインが崩れないようにテキストボックスの Anchor プロパティを Left,Top,Right に、ボタンの Anchor プロパティを Top,Right にしています。
| 種別 | 宣言 | 説明 |
|---|---|---|
| プロパティ | public bool UseSaveAsDialog | OpenFileDialog または SaveFileDialog を使うかの指定。False なら OpenFileDialog、True なら SaveFileDialog を使用する。 |
| プロパティ | public string DialogFilter | OpenFileDialog または SaveFileDialog の Filter プロパティ |
| プロパティ | public string FileName | 現在のファイル名(表示されているもの) |
| プロパティ | public string InitialDirectory | OpenFileDialog または SaveFileDialog の 初期ディレクトリ |
| イベント | public event EventHandler FileNameChanged | コモンダイアログによりファイル名が変更されたとき発生するイベント |
次がこのコントロールのコードです。
using System;
using System.Windows.Forms;
namespace OhhLibrary
{
/// <summary>
/// ファイル選択ユーザーコントロール
/// </summary>
public partial class FileSelector : UserControl
{
private TextBox textBox1;
private OpenFileDialog openFileDialog1;
private SaveFileDialog saveFileDialog1;
private Button button1;
/// <summary>
/// ファイル名が変更されたとき発生するイベント
/// (コモンダイアログで変更されたときのみ)
/// </summary>
public event EventHandler FileNameChanged;
/// <summary>
/// OpenFileDialog または SaveFileDialog を使うかの指定。
/// False なら OpenFileDialog、True なら SaveFileDialog を使用する。
/// </summary>
public bool UseSaveAsDialog { get; set; } = false;
/// <summary>
/// OpenFileDialog または SaveFileDialog の Filter プロパティ
/// </summary>
public string DialogFilter { get; set; } = "All(*.*)|*.*";
/// <summary>
/// 現在のファイル名
/// </summary>
public string FileName
{
get
{ return textBox1.Text; }
set
{ textBox1.Text = value; }
}
/// <summary>
/// OpenFileDialog または SaveFileDialog の 初期ディレクトリ
/// </summary>
public string InitialDirectory { get; set; }
/// <summary>
/// コンストラクタ
/// </summary>
public FileSelector()
{
InitializeComponent();
}
/// <summary>
/// 参照ボタンがクリックされたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
if (UseSaveAsDialog)
{
// ファイル保存のとき
if (!String.IsNullOrEmpty(InitialDirectory))
saveFileDialog1.InitialDirectory = this.InitialDirectory;
saveFileDialog1.Filter = this.DialogFilter;
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
textBox1.Text = saveFileDialog1.FileName;
FileNameChanged(sender, e);
}
}
else
{
// ファイル選択のとき
if (!String.IsNullOrEmpty(InitialDirectory))
openFileDialog1.InitialDirectory = this.InitialDirectory;
openFileDialog1.Filter = this.DialogFilter;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
textBox1.Text = openFileDialog1.FileName;
FileNameChanged(sender, e);
}
}
}
/// <summary>
/// コンポーネントを初期化する。
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)
(((System.Windows.Forms.AnchorStyles.Top |
System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(0, 4);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(224, 19);
this.textBox1.TabIndex = 0;
//
// button1
//
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Top |
System.Windows.Forms.AnchorStyles.Right)));
this.button1.Location = new System.Drawing.Point(229, 4);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(21, 19);
this.button1.TabIndex = 1;
this.button1.Text = "...";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// FileSelector
//
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Name = "FileSelector";
this.Size = new System.Drawing.Size(262, 27);
this.ResumeLayout(false);
this.PerformLayout();
}
}
}ダイアログボックスにはたいてい OK と Cancel ボタンがあります。このコントロールはそれらを組み合わせたものです。下の画像はそのデザイン画面です。
| 種別 | 宣言 | 説明 |
|---|---|---|
| イベント | public event EventHandler OkClick | OK ボタンがクリックされたときに発生する。 |
| イベント | public event EventHandler CancelClick | Cancel ボタンがクリックされたときに発生する。 |
次のコードはこのコントロールの使用例です。OK と Cancel をクリックしたときのハンドラを定義します。
/// <summary>
/// OK ボタンをクリックしたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void okCancel1_OkClick(object sender, EventArgs e)
{
if (MessageBox.Show("終了しますか?", "確認", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk) == DialogResult.OK)
Close();
}
/// <summary>
/// Cancel ボタンをクリックしたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void okCancel1_CancelClick(object sender, EventArgs e)
{
Close();
}using System;
using System.Windows.Forms;
namespace OhhLibrary
{
public partial class OKCancel : UserControl
{
private Button btnOK;
private Button btnCancel;
/// <summary>
/// OK がクリックされたときのイベント
/// </summary>
public event EventHandler OkClick;
/// <summary>
/// Cancel がクリックされたときのイベント
/// </summary>
public event EventHandler CancelClick;
/// <summary>
/// コンストラクタ
/// </summary>
public OKCancel()
{
InitializeComponent();
}
/// <summary>
/// コンポーネントを初期化する。
/// </summary>
private void InitializeComponent()
{
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnOK
//
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(0, 3);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(73, 24);
this.btnOK.TabIndex = 0;
this.btnOK.Text = "&OK";
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(79, 3);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(73, 24);
this.btnCancel.TabIndex = 0;
this.btnCancel.Text = "&Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// OKCancel
//
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOK);
this.Name = "OKCancel";
this.Size = new System.Drawing.Size(153, 30);
this.ResumeLayout(false);
}
/// <summary>
/// OK ボタンがクリックされたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOK_Click(object sender, EventArgs e)
{
OkClick(sender, e);
}
/// <summary>
/// Cancel ボタンがクリックされたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnCancel_Click(object sender, EventArgs e)
{
CancelClick(sender, e);
}
}
}このコントロールは NumericUpDown と TextBox を組み合わせたユーザコントロールです。下の画像はそのデザイン画面です。コントロールのサイズが変わった時、デザインが崩れないようにテキストボックスの Anchor プロパティは Left,Top,Right を、NumericUpDown の Anchor プロパティは Top,Right を設定しています。
| 種別 | 宣言 | 説明 |
|---|---|---|
| プロパティ | public int MinValue | NumericUpDown の最小値です。 |
| プロパティ | public int MaxValue | NumericUpDown の最大値です。 |
| プロパティ | public int Value | NumericUpDown の現在の値です。 |
| プロパティ | public HorizontalAlignment TextAlign | テキストボックスの文字列の整列を設定、読み取りできます。 |
using System;
using System.Windows.Forms;
namespace OhhLibrary
{
/// <summary>
/// スピナ付きのテキストボックス
/// </summary>
public partial class SpinTextBox : UserControl
{
/// <summary>
/// 最小値
/// </summary>
public int MinValue
{
get
{
return (int)numericUpDown1.Minimum;
}
set
{
numericUpDown1.Minimum = value;
}
}
/// <summary>
/// 最大値
/// </summary>
public int MaxValue
{
get
{
return (int)numericUpDown1.Maximum;
}
set
{
numericUpDown1.Maximum = value;
}
}
/// <summary>
/// numericUpDown1 の値
/// </summary>
public int Value
{
get
{
return (int)numericUpDown1.Value;
}
set
{
numericUpDown1.Value = value;
}
}
/// <summary>
/// テキストボックスの文字列整列
/// </summary>
public HorizontalAlignment TextAlign
{
get
{
return textBox1.TextAlign;
}
set
{
textBox1.TextAlign = value;
}
}
/// <summary>
/// コンストラクタ
/// </summary>
public SpinTextBox()
{
InitializeComponent();
}
/// <summary>
/// numericUpDown1 の値が変化したとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
textBox1.Text = numericUpDown1.Value.ToString();
}
}
}カスタムコントロールは既存のコントロールを元に作成します。ユーザコントロールは UserControl クラスから派生させましたが、カスタムコントロールは既存のコントロール、例えば TextBox や Button などから派生させて作ります。
Visual Studio でプロジェクトを選択し、新しい項目の追加を実行します。そして、一覧から「カスタムコントロール」を選びます。デザイン画面が開くのでツールボックスから派生元のコントロールをドラッグ&ドロップします。
下の画像はデザイン画面の例です。この例では、テキストボックスをデザイン画面に貼り付けています。
このサンプルは Explorer からオブジェクトをドラッグ&ドロップ可能なテキストボックスです。外観は、普通のテキストボックスと同じですが、Explorer からオブジェクトをドラッグオーバーするとカーソルがコピーカーソルに変化して、ドロップ可能になります。
特別なプロパティ、メソッド、イベントはありません。使用法も普通のテキストボックスと同じです。
using System.Windows.Forms;
namespace OhhLibrary
{
public partial class TextBoxDd : TextBox
{
/// <summary>
/// コンストラクタ
/// </summary>
public TextBoxDd()
{
InitializeComponent();
AllowDrop = true;
}
/// <summary>
/// 描画が必要なとき
/// </summary>
/// <param name="pe"></param>
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
/// <summary>
/// ドラッグ開始
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnDragEnter(DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Copy;
}
}
/// <summary>
/// ドラッグオーバー
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnDragOver(DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Copy;
}
}
/// <summary>
/// ドロップしたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnDragDrop(DragEventArgs e)
{
object data;
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Explorerからファイルをドロップ
data = e.Data.GetData(DataFormats.FileDrop);
string[] paths = (string[])data;
if (paths.Length > 0)
this.Text = paths[0];
}
}
}
}このサンプルは、フォーカスを失うときに内容を自動的にチェックするテキストボックスです。チェックは内容が空であることと外部から与えたデリゲートでチェックする2種類が可能です。
| 種別 | 宣言 | 説明 |
|---|---|---|
| プロパティ | public bool NullCheck | 値がTrue の場合、テキストボックスが空かどうかをチェックします。False なら何もしません。 |
| プロパティ | public bool Invalid | チェック結果を保持しています。True なら不良、False なら良です。 |
| プロパティ | public ErrorProvider ErrorSign | チェック結果が不良の場合、テキストボックスの横に赤いマークを表示するかどうかを指定します。ErrorProvider が設定されていれば表示をします。 |
| プロパティ | public Func<string, bool> Validator | 値をチェックするメソッドを指定します。Func は System.Func デリゲートです。文字列(テキストボックスの Text プロパティの内容) を受け取り、不良なら True、良なら False を返す関数のデリゲートを指定します。 |
下に使用例を示します。
private void Form1_Load(object sender, EventArgs e)
{
textBoxVal1.NullCheck = true;
textBoxVal1.ErrorSign = errorProvider1;
// Validator イベントハンドラを設定する。
textBoxVal1.Validator = (s) => {
int n;
bool result = ! Int32.TryParse(s, out n);
return result;
};
}
// textBoxVal1 が Validated されたとき
private void textBoxVal1_Validated(object sender, EventArgs e)
{
if (textBoxVal1.Invalid)
{
MessageBox.Show("textBox1Val1の内容が不正です。", "エラー");
}
}using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace OhhLibrary
{
/// <summary>
/// データチェック付きのテキストボックス
/// </summary>
public partial class TextBoxVal : TextBox
{
/// <summary>
/// テキストボックスが空かどうかをチェックするかどうか。
/// </summary>
public bool NullCheck { get; set; }
/// <summary>
/// チェック結果が不良なら真になる。
/// </summary>
public bool Invalid { get; set; }
/// <summary>
/// ErrorProvider
/// </summary>
public ErrorProvider ErrorSign { get; set; }
/// <summary>
/// 値を評価するハンドラ
/// </summary>
public Func<string, bool> Validator;
/// <summary>
/// コンストラクタ
/// </summary>
public TextBoxVal()
{
InitializeComponent();
}
/// <summary>
/// 描画が必要なとき
/// </summary>
/// <param name="pe"></param>
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
/// <summary>
/// フォーカスを失うとき
/// </summary>
/// <param name="e"></param>
protected override void OnValidating(CancelEventArgs e)
{
// base.OnValidating(e);
// Invalid を初期化
Invalid = false;
// 内容が NULL かどうかをチェック
if (NullCheck && String.IsNullOrEmpty(this.Text))
{
// NULL なら Invalid を True にしてもどる。
Invalid = true;
// ErrorProvider がセットされていたらエラー表示にする。
if (ErrorSign != null)
{
ErrorSign.SetError(this, "内容が空欄です。");
}
return;
}
// そうでない場合はテキストの内容を評価する。
if (Validator == null)
return;
Invalid = Validator(this.Text);
if (ErrorSign != null)
{
if (Invalid)
{
ErrorSign.SetError(this, "内容が不正です。");
}
else
{
ErrorSign.Clear();
}
}
}
}
}このサンプルは Button から派生させたコントロールで、OnPaint イベントハンドラをオーバーライドすることにより、独自の外観を持つようにしています。(下の画像)
このボタンはクリックすると色が赤に変化するだけで、機能的には Button と同じです。ただし、PenWidth というプロパティのみ独自に持っています。これは、枠の幅を決めています。
using System.Drawing;
using System.Windows.Forms;
namespace OhhLibrary
{
public partial class ButtonCR : Button
{
public int PenWidth { get; set; }
private Color orgColor;
private bool flagFirst = true;
/// <summary>
/// コンストラクタ
/// </summary>
public ButtonCR()
{
InitializeComponent();
// 変数初期化
PenWidth = 6;
orgColor = this.BackColor;
}
/// <summary>
/// 描画が必要なとき
/// </summary>
/// <param name="pe"></param>
protected override void OnPaint(PaintEventArgs pe)
{
var g = pe.Graphics;
var borderPen = new Pen(this.ForeColor, PenWidth);
var backBrush = new SolidBrush(this.BackColor);
borderPen.LineJoin = System.Drawing.Drawing2D.LineJoin.Bevel;
g.FillRectangle(backBrush, new Rectangle(new Point(0, 0), this.Size));
g.DrawRectangle(borderPen, new Rectangle(new Point(0,0), this.Size));
}
/// <summary>
/// 左マウスボタンが押されたとき
/// </summary>
/// <param name="mevent"></param>
protected override void OnMouseDown(MouseEventArgs mevent)
{
base.OnMouseDown(mevent);
if (flagFirst)
{
orgColor = this.BackColor;
flagFirst = false;
}
this.BackColor = Color.Red;
this.ForeColor = Color.Black;
}
/// <summary>
/// 左マウスボタンが離されたとき
/// </summary>
/// <param name="mevent"></param>
protected override void OnMouseUp(MouseEventArgs mevent)
{
base.OnMouseUp(mevent);
this.BackColor = orgColor;
this.ForeColor = Color.Black;
}
}
}