Skip Navigation Linksホーム > ライブラリ > ボタン > ボタンを独自の形状で描画する

ボタンを独自の形状で描画する

言語フィルタ:

概要

ボタンを独自の形状で描画する方法を紹介します。

ボタンを独自の形状で描画すると次のようになります。

sample1

対象コントロール

  • System.Windows.Forms.Button

解説

コントロールの詳細を描画する」で紹介している方法でボタンを好きな形状で描画することが出来ます。しかし、角を丸くしようとした場合に角の隅の部分を透明に描画することが出来ません。

OnPaint メソッドで基本クラスの OnPaint メソッドを呼び出している場合は、基本クラスによってボタンが描画されてしまうため、それが見えてしまいます。基本クラスの OnPaint メソッドを呼ばないようにすると、今度は透明に描画した部分が黒く塗りつぶされてしまいます。これはボタンの背景に何も描画されていないためです。つまり背景を描画出来れば、ボタンを好きな形状で描画出来るようになります。

ボタンは ControlStyles.Opaque スタイルが適用されているため、背景は描画されません。ControlStyles.Opaque スタイルを未適用状態にするには、コンストラクタで SetStyle メソッドを使用して ControlStyles.Opaque スタイルを False に設定します。これで背景が描画されるようになり OnPaintBackground メソッドが呼び出されるようになります。

展開されたイメージ ControlStyles.Opaque スタイルを未適用状態にする - Visual Basic
コピーイメージ コードのコピー
Public Sub New()
    Me.SetStyle(ControlStyles.Opaque, False)
End Sub
展開されたイメージ ControlStyles.Opaque スタイルを未適用状態にする - C#
コピーイメージ コードのコピー
public OwnerDrawButton() // コンストラクタ
{
    this.SetStyle(ControlStyles.Opaque, false);
}

これで OnPaint メソッドで透明に描画した部分に背景が表示されるようになります。楕円形でも角丸の長方形でも、後は好きな形状でボタンを描画するだけです。

ソースコード

楕円の形状をした Button コントロールを作成するサンプルを紹介します。

ボタンの上にマウスがある時や、ボタンが押されている時にボタンの色を変化させるために、マウスの状態を示す MouseState 列挙体を追加しています。ボタンのテキストは必ずコントロールの中央に表示されるようにしてあります。

ボタンの形状の外の部分を透明に描画したいため、コンストラクタで基本クラスの BackColor プロパティに透明色を設定しています。ボタンの色を変更するには、基本クラスの BackColor プロパティを隠蔽し再定義した BackColor プロパティで設定出来るようにしています。

展開されたイメージ Visual Basic
コピーイメージ コードのコピー
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms

Namespace Extentions

    Public Class OwnerDrawButton
        Inherits System.Windows.Forms.Button

        Private Enum MouseState As Integer
            Leave = 0
            Enter = 1
            Down = 2
        End Enum

        Private drawMouseState As MouseState

        Public Sub New()
            Me.SetStyle(ControlStyles.Opaque, False)
            MyBase.BackColor = Color.Transparent
        End Sub

        Protected Overrides Sub OnPaint(ByVal pevent As System.Windows.Forms.PaintEventArgs)
            pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias

            Dim rect As New Rectangle(0, 0, Me.ClientRectangle.Width - 1, Me.ClientRectangle.Height - 1)

            ' ボタンの形状を描画
            Using sb As New SolidBrush(Me.BackColor)
                pevent.Graphics.FillEllipse(sb, rect)
            End Using

            ' マウスの状態によりボタンの前景色を変更
            Select Case Me.drawMouseState
                Case MouseState.Enter
                    Using sb As New SolidBrush(Color.FromArgb(30, 255, 255, 255))
                        pevent.Graphics.FillEllipse(sb, rect)
                    End Using
                Case MouseState.Down
                    Using sb As New SolidBrush(Color.FromArgb(30, 0, 0, 0))
                        pevent.Graphics.FillEllipse(sb, rect)
                    End Using
            End Select

            ' フォーカス枠を描画
            If Me.Focused Then
                Using p As New Pen(Color.Black)
                    p.DashStyle = DashStyle.Dot

                    Dim r As Rectangle = rect
                    r.Inflate(-2, -2)
                    pevent.Graphics.DrawEllipse(p, r)
                End Using
            End If

            ' 文字列を描画
            Using sf As New StringFormat
                sf.Alignment = StringAlignment.Center
                sf.LineAlignment = StringAlignment.Center
                Using sb As New SolidBrush(Me.ForeColor)
                    pevent.Graphics.DrawString(Me.Text, Me.Font, sb, Me.ClientRectangle, sf)
                End Using
            End Using
        End Sub

        Protected Overrides Sub OnEnter(ByVal e As System.EventArgs)
            MyBase.OnEnter(e)
            Me.Invalidate()
        End Sub

        Protected Overrides Sub OnLeave(ByVal e As System.EventArgs)
            MyBase.OnLeave(e)
            Me.Invalidate()
        End Sub

        Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
            MyBase.OnMouseEnter(e)
            Me.drawMouseState = MouseState.Enter
            Me.Invalidate()
        End Sub

        Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
            MyBase.OnMouseLeave(e)
            Me.drawMouseState = MouseState.Leave
            Me.Invalidate()
        End Sub

        Protected Overrides Sub OnMouseDown(ByVal mevent As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseDown(mevent)
            Me.drawMouseState = MouseState.Down
            Me.Invalidate()
        End Sub

        Protected Overrides Sub OnMouseUp(ByVal mevent As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseUp(mevent)
            If Me.ClientRectangle.Contains(mevent.Location) Then
                Me.drawMouseState = MouseState.Enter
            Else
                Me.drawMouseState = MouseState.Leave
            End If
            Me.Invalidate()
        End Sub

#Region " BackColor "

        Private _BackColor As Color
        Public Shadows Property BackColor() As Color
            Get
                If Me._BackColor <> Color.Empty Then
                    Return Me._BackColor
                End If
                If Me.Parent IsNot Nothing Then
                    Return Me.Parent.BackColor
                End If

                Return Control.DefaultBackColor
            End Get
            Set(ByVal value As Color)
                Me._BackColor = value
                Me.Invalidate()
            End Set
        End Property

        Public Overrides Sub ResetBackColor()
            Me.BackColor = Color.Empty
        End Sub

        Private Function ShouldSerializeBackColor() As Boolean
            Return Me._BackColor <> Color.Empty
        End Function

#End Region

    End Class

End Namespace
展開されたイメージ C#
コピーイメージ コードのコピー
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Extentions
{
    class OwnerDrawButton : System.Windows.Forms.Button
    {
        private enum MouseState : int
        {
            Leave = 0,
            Enter = 1,
            Down = 2,
        }

        private MouseState drawMouseState;

        public OwnerDrawButton()
        {
            this.SetStyle(ControlStyles.Opaque, false);
            base.BackColor = Color.Transparent;
        }

        protected override void OnPaint(PaintEventArgs pevent)
        {
            pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            Rectangle rect = new Rectangle(0, 0, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1);

            // ボタンの形状を描画
            using (SolidBrush sb = new SolidBrush(this.BackColor))
            {
                pevent.Graphics.FillEllipse(sb, rect);
            }

            // マウスの状態によりボタンの前景色を変更
            switch (this.drawMouseState)
            {
                case MouseState.Enter:
                    using (SolidBrush sb = new SolidBrush(Color.FromArgb(30, 255, 255, 255)))
                    {
                        pevent.Graphics.FillEllipse(sb, rect);
                    }
                    break;
                case MouseState.Down:
                    using (SolidBrush sb = new SolidBrush(Color.FromArgb(30, 0, 0, 0)))
                    {
                        pevent.Graphics.FillEllipse(sb, rect);
                    }
                    break;
            }

            // フォーカス枠を描画
            if (this.Focused)
            {
                using (Pen p = new Pen(Color.Black))
                {
                    p.DashStyle = DashStyle.Dot;

                    Rectangle r = rect;
                    r.Inflate(-2, -2);
                    pevent.Graphics.DrawEllipse(p, r);
                }
            }

            // 文字列を描画
            using (StringFormat sf = new StringFormat())
            {
                sf.Alignment = StringAlignment.Center;
                sf.LineAlignment = StringAlignment.Center;
                using (SolidBrush sb = new SolidBrush(this.ForeColor))
                {
                    pevent.Graphics.DrawString(this.Text, this.Font, sb, this.ClientRectangle, sf);
                }
            }
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            this.Invalidate();
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            this.Invalidate();
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            this.drawMouseState = MouseState.Enter;
            this.Invalidate();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            this.drawMouseState = MouseState.Leave;
            this.Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs mevent)
        {
            base.OnMouseDown(mevent);
            this.drawMouseState = MouseState.Down;
            this.Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs mevent)
        {
            base.OnMouseUp(mevent);
            if (this.ClientRectangle.Contains(mevent.Location))
            {
                this.drawMouseState = MouseState.Enter;
            }
            else
            {
                this.drawMouseState = MouseState.Leave;
            }
            this.Invalidate();
        }

        #region BackColor

        private Color _BackColor;
        public new Color BackColor
        {
            get
            {
                if (this._BackColor != Color.Empty)
                {
                    return this._BackColor;
                }

                if (this.Parent != null)
                {
                    return this.Parent.BackColor;
                }

                return Control.DefaultBackColor;
            }
            set
            {
                this._BackColor = value;
                this.Invalidate();
            }
        }

        public override void ResetBackColor()
        {
            this.BackColor = Color.Empty;
        }

        private Boolean ShouldSerializeBackColor()
        {
            return this._BackColor != Color.Empty;
        }

        #endregion
    }
}