Windowsフォーム上のテキストボックスにプレースホルダテキストを表示させる

#プレースホルダ
![sample.png](https://qiita-image-store.s3.amazonaws.com/0/100206/6a290caa-79c0-ba2d-fa6e-49f7c0d8394e.png)

フォーム上の文字列入力項目に、入力例や入力時の注意をあらかじめ表示しておく手段のひとつとして「プレースホルダ」や「プロンプト」などと呼ばれる要素があります（ご存知の方も多いと思いますが HTML5 フォームの [input](http://www.htmq.com/html5/input.shtml) 要素には [placeholder](http://www.htmq.com/html5/input_placeholder.shtml) 属性があります）
.NET Framework の Windows フォームで用意される標準テキストボックスには無い機能なので、拡張して搭載してみます。

#サンプルコード
```C#
using System;

namespace WindowsFormsApplication1 // <- 組み込む名前空間で
{
    public class TextBoxEx : System.Windows.Forms.TextBox
    {
        private string _placeholder = string.Empty;

        // （プロパティ）
        public string Placeholder
        {
            get { return _placeholder; }
            set
            {
                _placeholder = value;
                Invalidate();
            }
        }

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 15) //  WM_PAINT == 15
            {
                if (this.Enabled && !this.ReadOnly && !this.Focused && (_placeholder != null) && (_placeholder.Length > 0) && (this.TextLength == 0))
                {
                    using (var g = this.CreateGraphics())
                    {
                        // 描画を一旦消してしまう
                        g.FillRectangle(new System.Drawing.SolidBrush(this.BackColor), this.ClientRectangle);

                        // プレースホルダのテキスト色を、前景色と背景色の中間として文字列を描画する
                        var placeholderTextColor = System.Drawing.Color.FromArgb((this.ForeColor.A >> 1 + this.BackColor.A >> 1), (this.ForeColor.R >> 1 + this.BackColor.R >> 1), ((this.ForeColor.G >> 1 + this.BackColor.G) >> 1), (this.ForeColor.B >> 1 + this.BackColor.B >> 1));
                        g.DrawString(_placeholder, this.Font, new System.Drawing.SolidBrush(placeholderTextColor), 1, 1);
                    }
                }
            }
        }
    }
}
```
![sam.png](https://qiita-image-store.s3.amazonaws.com/0/100206/f120cf51-4c7d-b074-2951-167349508ace.png)

上記コード（クラス）を組み込んで一旦ビルドすると、Visual Studio で任意のフォームを作成・編集するときに、ツールボックスへ 「TextBoxEx」コントロールが追加されます。これを標準の TextBox などと同じくフォーム上に張り付けて、Placeholder プロパティにプレースホルダテキストをセットしてやります。フォームの [InitializeComponent](https://msdn.microsoft.com/ja-jp/library/system.windows.markup.icomponentconnector.initializecomponent.aspx) メソッド内を編集したりして、標準の [System.Windows.Forms.TextBox](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.textbox.aspx) を拡張対応させる方法でももちろんよいでしょう。

標準のテキストボックスコントロールを派生させて（サブクラス化して）、ウインドウプロシージャ (WndProc) の WM_PAINT　処理を拡張しています。標準の描画処理を行ったあと、プレースホルダを表示する条件（ここでは、[Enabled](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.enabled.aspx)=true, [ReadOnly](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.textboxbase.readonly.aspx)=false, ...）に該当していたら、一旦消してプレースホルダ文字列を描画させています。
わざわざ一旦消すことは無駄であろうになぜそうするか...標準の挙動とまったく同じくさせるコードを書くことが困難（しかも OS の仕様変更など将来にわたって）だからです（時間と気力があれば [TextBoxBase](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.textboxbase.aspx) クラスから派生させて独自コントロールを作ることも選択肢でしょうけれども、かけるコストと効果を比べると私はイヤ）
ちなみに [System.Windows.Forms.TextBox](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.textbox.aspx) コントロールでオーナードローをしたい（[OnPaint](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.onpaint.aspx) や [OnPaintBackground](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.onpaintbackground.aspx) をオーバーライドして処理をしたい）場合は、[Control.SetStyle](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.setstyle.aspx) メソッドをコンストラクタなどで呼び出し、[ControlStyles.UserPaint](https://msdn.microsoft.com/ja-jp/library/system.windows.forms.controlstyles.aspx) スタイルを有効にする必要があります。

#プレースホルダテキストを使用する功罪
ご参考までに「プレースホルダテキストはユーザーの[短期記憶](https://u-site.jp/alertbox/20091207_short-term-memory)に負荷をかける」ためプレースホルダを使うべきではない、との主張もあります。

- [入力フォームのプレースホルダを使ってはいけない](https://u-site.jp/alertbox/form-design-placeholders) - ニールセン博士のAlertbox : [U-Site](https://u-site.jp/)さん