読者です 読者をやめる 読者になる 読者になる

真実の楽譜(フルスコア)

プログラム関係の忘備録になるはず

ReadOnlyの時にチカチカしないNumericUpDownを作る

NumericUpDown

GUIで数値を上下に変化させて指定することができる、
NumericUpDownというFormコントロールがあります。
NumericUpDown クラス (System.Windows.Forms)

アプリケーションでユーザーに数値を入力してもらう際に、
ボタンを押せば一定間隔で指定ができるので、
開発側としても変な入力がされにくい便利なコントロール。

ReadOnly時のチカチカがいらないよね

f:id:s_sikisya:20140310185141p:plain
ユーザが直接値を指定して変更させないために、
ReadOnlyのプロパティが用意されていますが、
ReadOnly時でもフォーカスに入るとカーソルがチカチカと点滅。
これじゃユーザが入力できると勘違いする恐れがあります。

チカチカを消してしまうには

調べてみるとこのチカチカするヤツ、
caret(キャレット)って名前らしく、
これを消し去るにはWindows APIが必要とのこと。

参考
[C#] 独自に作成したウィンドウコントロールのコンポーネントにキャレットを表示する

チカチカしないNumericUpDownを作る

今回はSystem.Windows.Forms.NumericUpDownクラスを継承して、
ReadOnly時にキャレットを表示させないコントロールを実装してみました。

NumericUpDown継承クラスの作成

NumericUpDownクラスを継承した以下のクラスを作成します。

NumericUpDownNoBlinking.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;

namespace NumericUpDownNoBlinking
{
  public partial class NumericUpDownNoBlinking : NumericUpDown
  {

    [DllImport("user32.dll")]
    static extern bool HideCaret(IntPtr hWnd);

    //キャレット表示の有無を指定
    private bool caretVisible = true;

    //ReadOnlyメソッドを上書き
    public new bool ReadOnly
    {
      get
      {
        return base.ReadOnly;
      }
      set
      {
        base.ReadOnly = value;

        //キャレットの有無をReadOnly属性に応じて変更
        caretVisible = !value;

        //ReadOnly有効時、NumericUpDownのTextBoxコントロールにカーソルを変化させない。
        foreach (Control cntr in this.Controls)
        {
          if (cntr is TextBox)
          {
            if (caretVisible)
            {
              cntr.Cursor = Cursors.IBeam;
            }
            else
            {
              cntr.Cursor = Cursors.Arrow;
            }
          }
        }
      }
    }

    public NumericUpDownNoBlinking()
    {
      InitializeComponent();
    }

    public NumericUpDownNoBlinking(IContainer container)
    {
      container.Add(this);

      InitializeComponent();
    }

    //フォーカス取得時にキャレットを消去
    protected override void OnGotFocus(EventArgs e)
    {
      base.OnGotFocus(e);

      if (!caretVisible)
      {
        //NumericUpDownのTextBoxコントロールのキャレットを非表示にする
        foreach (Control cntr in this.Controls)
        {
          if (cntr is TextBox)
          {
            HideCaret(cntr.Handle);
          }
        }
      }
    }
  }
}

解説

Windwos APIのHideCaret関数を使えるように、
宣言しておきます。

[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);

コントロールのOnGotFocusメソッドをオーバーライドし、
フォーカス取得時にControlsプロパティに格納されている
TextBoxのHandleをHideCaret関数の引数に渡せばOK。

protected override void OnGotFocus(EventArgs e)
{
  base.OnGotFocus(e);

  if (!caretVisible)
  {
    //NumericUpDownのTextBoxコントロールのキャレットを非表示にする
    foreach (Control cntr in this.Controls)
    {
      if (cntr is TextBox)
      {
        HideCaret(cntr.Handle);
      }
    }
  }
}

NumericUpDownのHandleではなくTextBoxを指定することがポイント!

また上記プログラムでは、
テキストボックス上にマウスカーソルが移動しても、
マウスポインタの形状を矢印のままにしています。
こちらもControlsプロパティに格納されているTextBoxに対して、
プロパティを変更することで対処しています。

参考
ネコキス: NumericUpDown のキャレットを消す
[C#] 独自に作成したウィンドウコントロールのコンポーネントにキャレットを表示する