透かし文字(WaterMark)表示機能つきComboBox

(2016.02.01追記)TextBox版は以下の記事を参照ください。
透かし文字表示機能つきTextBox(WinForms) - hnx8 開発室

HNXgrepでも使用している「未入力のときに入力を促す内容の透かし文字」を表示するComboBoxのソースを紹介してみます。

未入力の場合は、太線囲みのようにグレーなど特定の色で表示されるよう、ComboBoxのクラスを拡張し、以下2つのプロパティを追加しています。

  1. WatermarkText:未入力時に表示するテキスト内容
  2. WatermarkColor:未入力時テキストの表示色

他は、通常のComboBoxと使い方はまったく一緒です。

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

/// <summary>
/// 改良版ComboBox
/// <para>・ウォーターマーク機能追加</para>
/// </summary>
class WatermarkComboBox : ComboBox
{
    // ウォーターマーク表示関連変数
    private bool _watermark;       //ウォーターマーク表示中はtrue
    private string _watermarkText; //ウォーターマーク表示内容text
    private Color _watermarkColor; //ウォーターマーク表示時の前景色
    private Color _foreColor;      //通常表示時の前景色
    private bool _isDropDownList;  //ドロップダウンリストでウォーターマーク表示中か

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public WatermarkComboBox()
    {
        _watermark = false;
        _watermarkText = "";
        _watermarkColor = SystemColors.GrayText;
        _foreColor = SystemColors.WindowText;
        _isDropDownList = false;
    }

    /// <summary>
    /// テキストが空の場合に表示する文字列を取得・設定します。
    /// </summary>
    [Category("表示")]
    [DefaultValue("")]
    [Description("テキストが空の場合に表示する文字列です。")]
    [RefreshProperties(RefreshProperties.Repaint)]
    public string WatermarkText
    {
        get { return this._watermarkText; }
        set
        {
            this._watermarkText = value;
            trySetWatermark(); //ウォーターマーク表示を試みる
        }
    }

    /// <summary>
    /// テキストが空の場合に表示する文字列の色を取得・設定します。
    /// </summary>
    [Category("表示")]
    [DefaultValue(typeof(Color), "GrayText")]
    [Description("テキストが空の場合に表示する文字列の色です。")]
    public Color WatermarkColor
    {
        get { return this._watermarkColor; }
        set
        {
            this._watermarkColor = value;
            trySetWatermark(); //ウォーターマーク表示を試みる
        }
    }

    public override Color ForeColor
    {
        get
        {   //描画時にForeColorプロパティどおりの色で描画しているみたいなので、ForeColorどおりの色を返す
            //ただしデザインモードでは通常色どおりに返す
            return (DesignMode) ? this._foreColor : base.ForeColor;
        }
        set
        {
            this._foreColor = value;
            if (_watermark == false)
            {   //ウォーターマーク表示中でなければ、ただちに色適用
                base.ForeColor = value;
            }
        }
    }

    [RefreshProperties(RefreshProperties.Repaint)]
    public override string Text
    {
        get { return (_watermark) ? string.Empty : base.Text; }
        set
        {
            if (string.IsNullOrEmpty(value))
            {   //空文字列が指定された
                trySetWatermark(); //ウォーターマーク表示を試みる
            }
            else if (_watermark)
            {   //適切な文字列が設定された。ウォーターマーク表示設定解除
                resetWatermark(value);
            }
            else
            {   //テキスト上書き
                base.Text = value;
            }
        }
    }

    /// <summary>
    /// ウォーターマーク表示設定を試みる
    /// </summary>
    /// <returns></returns>
    private bool trySetWatermark()
    {
        if (string.IsNullOrEmpty(_watermarkText) == false && string.IsNullOrEmpty(base.Text) && base.SelectedIndex < 0 && base.Focused == false)
        {
            //DropDownListは、ウォーターマーク表示のためDropDownにスタイル変更
            _isDropDownList = (base.DropDownStyle == ComboBoxStyle.DropDownList && DesignMode == false);
            if (_isDropDownList) { base.DropDownStyle = ComboBoxStyle.DropDown; }
            //ウォーターマーク設定
            base.ForeColor = WatermarkColor;
            base.Text = _watermarkText;
            _watermark = true;
            return true;
        }
        return false;
    }

    /// <summary>
    /// ウォーターマーク表示設定を解除する
    /// </summary>
    /// <param name="value"></param>
    private void resetWatermark(string value)
    {
        if (_isDropDownList)
        {
            base.DropDownStyle = ComboBoxStyle.DropDownList;
            _isDropDownList = false;
        }
        if (value != null) { base.Text = value; }
        base.ForeColor = _foreColor;
        _watermark = false;
    }

    //イベント処理対応
    protected override void OnEnter(EventArgs e)
    {
        if (_watermark)
        {   //ウォーターマーク表示解除
            resetWatermark("");
        }
        base.OnEnter(e);
    }
    protected override void OnLeave(EventArgs e)
    {
        base.OnLeave(e);
        trySetWatermark();
    }
    protected override void OnSelectedIndexChanged(EventArgs e)
    {
        if (SelectedIndex >= 0)
        {   //ウォーターマーク表示解除
            resetWatermark(null);
        }
        base.OnSelectedIndexChanged(e);
    }

    //本来は、TextChanged,ForeColorChangedイベントもオーバーライドして抑止するのが望ましい
}

※参考にしたページ
コンボボックスに透かし文字を表示する(※WindowsVista以降のみ)
http://youryella.wankuma.com/Library/Extensions/ComboBox/Watermark.aspx
テキストボックスに透かし文字を表示する (実装)
http://youryella.wankuma.com/Library/Extensions/TextBox/Watermark.aspx

WindowsXP対応」のウォーターマーク機能つきComboBoxがほしかったので、上記ページを参考にして作ってみました。
もっともXPのサポート期限はあと2年弱なので、いまさら需要なんかないとは思いますが・・・・。