ラジオボタンの「ホット状態」を描画する

WindowsXPのビジュアルスタイルが有効なGUIでは、「ホット(Hot)状態」と呼ばれる、マウスがコントロール上をポイントしている際に、強調表示するエフェクトが描画される。

これを知った当初はああ、また実装するコードが増えるじゃないか、なんて面倒な機能を追加してくれたもんだと嘆いたのだが、先日のエントリカスタムコントロール(その3)で紹介した、RadioButtonRendererクラス等のビジュアルスタイルに対応した描画クラスは、この"ホット状態"を容易に描画することができる。

protected override void OnPaint(PaintEventArgs e)
{
    string caption = "ホット状態に対応";
    Size textSize = TextRenderer.MeasureText(caption, this.Font);
    RadioButtonState buttonState = RadioButtonState.UncheckedNormal;
    if (this.Enabled)
    {
        if (ボタンは選択状態か?)
        {
            if (マウスカーソルがボタンの矩形上をポイントしているか?)
            {
                buttonState = RadioButtonState.CheckedHot;
            }
            else
            {
                buttonState = RadioButtonState.CheckedNormal;
            }
        }
        else
        {
            if (マウスカーソルがボタンの矩形上をポイントしているか?)
            {
                buttonState = RadioButtonState.UncheckedHot;
            }
        }
    }
    else
    {
        if (ボタンは選択状態か?)
        {
            buttonState = RadioButtonState.CheckedDisabled;
        }
        else
        {
            buttonState = RadioButtonState.UncheckedDisabled;
        }
    }
    RadioButtonRenderer.DrawRadioButton(e.Graphics, new Point(0, 0), new Rectangle(10, 0, textSize .Width, textSize .Height), caption, this.Font, TextFormatFlags.PreserveGraphicsTranslateTransform, true, buttonState);
}

RadioButtonStateには、ホット状態を示す列挙メンバ、UncheckedHotやCheckedHot等が用意されており、これを適切に描画時に指定するだけだ。
サンプルではRadioButtonStateを決定する条件を日本語で書いたが、System.Windows.Forms.Controlを継承したクラスであれば、protected定義されているOnMouseMoveメソッドやOnMouseLeave中で、上記条件を満たしているかを判定するのは簡単に書けるだろう。メソッド中で条件を満たしたと判定できたら、Invalidateメソッド等で再描画を促せばよい。

private int mouseEnter = -1;

protected override void OnMouseMove(MouseEventArgs e)
{
    Point mouseLoc = e.Location;
 
    this.mouseEnter = this.HitTest(mouseLoc);
    if ( this.mouseEnter != -1)
    {
        this.Invalidate();
    }
    base.OnMouseMove(e);
}
protected override void OnMouseLeave(EventArgs e)
{
    this.mouseEnter = -1;
    this.Invalidate();
    base.OnMouseLeave(e);
}
private int HitTest(Point loc)
{
    //ヒットしたボタンの座標などを返すヒットテスト(内容は省略)
    if (found)
    {
        return i;
    }   
    else
    {
        return -1;
    }
}

なお、この手法はCheckBoxRenderer等のビジュアルスタイルをサポートしたレンダラクラスでは共通にサポートされている機能なので、同様に描画処理を記述できるだろう。