• 【Winform-自定义控件】ImageButton 支持鼠标正常、悬停、按下更改图片,支持文本


    原文地址:https://www.codeproject.com/Articles/29010/WinForm-ImageButton

    自定义winfrom图片按钮:支持鼠标正常、悬停、按下更改图片,支持文本。

    首先,创建没有按钮文本的图片,这样的:

    正常:      悬停:       按下:

    添加ImageButton控件并设置图像属性,然后设置文本Text,设置字体Font。

    1.创建ImageButton类,重写PictureBox控件,实现IButtonControl接口。

    实现IButtonControl接口,将允许ImageButton按钮与窗体上的任何其他按钮一起使用,作为默认按钮或取消按钮。

     public class ImageButton : PictureBox, IButtonControl

    2.鼠标方法

    思路很简单。就是创建一个图片显示在屏幕上,如果用户将鼠标停留在图片上,就会切换图片到HoverImage,

    鼠标按下就会切换图片到DownImage。

      2.1 设置属性

            private bool hover = false;
            private bool down = false;
    
            #region HoverImage
            private Image m_HoverImage;
            [Category("Appearance")]
            [Description("Image to show when the button is hovered over.")]
            public Image HoverImage
            {
                get { return m_HoverImage; }
                set { m_HoverImage = value; if (hover) Image = value; }
            }
            #endregion
    
            #region DownImage
            private Image m_DownImage;
            [Category("Appearance")]
            [Description("Image to show when the button is depressed.")]
            public Image DownImage
            {
                get { return m_DownImage; }
                set { m_DownImage = value; if (down) Image = value; }
            }
            #endregion
    
            #region NormalImage
            private Image m_NormalImage;
            [Category("Appearance")]
            [Description("Image to show when the button is not in any other state.")]
            public Image NormalImage
            {
                get { return m_NormalImage; }
                set { m_NormalImage = value; if (!(hover || down)) Image = value; }
            }
            #endregion

      2.2 重写OnMouseMove事件

    当鼠标移动到ImageButton上时调用OnMouseMove。避免使用OnMouseHover,因为这个方法会延迟调用。

    设置hover=true,表示鼠标悬停在ImageButton上,

    然后判断down的值,down=true时,设置按钮图片为DownImage;down=false时,设置按钮图片为HorverImage。

            protected override void OnMouseMove(MouseEventArgs e)
            {
                hover = true;
                if (down)
                {
                    if ((m_DownImage != null) && (Image != m_DownImage))
                        Image = m_DownImage;
                }
                else
                    if (m_HoverImage != null)
                        Image = m_HoverImage;
                    else
                        Image = m_NormalImage;
                base.OnMouseMove(e);
            }

       2.3 重写OnMouseLeave事件

     如果鼠标已经离开ImageButton的边界,设置hover=false。然后切换图片为NormalImage。

            protected override void OnMouseLeave(EventArgs e)
            {
                hover = false;
                Image = m_NormalImage;
                base.OnMouseLeave(e);
            }

       2.4 重写OnMouseDown事件

    如果鼠标按下控件,我们将焦点转移到ImageButton上(这不是默认行为,需要实现),

    然后设置down为true,并切换图片为DownImage。

            protected override void OnMouseDown(MouseEventArgs e)
            {
                base.Focus();
                down = true;
                if (m_DownImage != null)
                    Image = m_DownImage;
                base.OnMouseDown(e);
            }

      2.5 重写OnMouseUp事件

    当鼠标不再被按下,设置down为false。

    如果鼠标悬停在ImageButton上,切换图片为HoverImage。

    如果鼠标离开ImageButton,切换图片为NormalImage。

            protected override void OnMouseUp(MouseEventArgs e)
            {
                down = false;
                if (hover)
                {
                    if (m_HoverImage != null)
                        Image = m_HoverImage;
                }
                else
                    Image = m_NormalImage;
                base.OnMouseUp(e);
            }

    3.空值

    你应该注意到,我们在切换图片之前会检查HoverImage和DowmImage是否为空,而NormalImage不用检查,这是为什么呢?

    这是为了防止当NormalImage没有指定图片,而HoverImage和DowmImage有图片时,显示图片出来。

    4.文本

    PictureBox控件继承了Control类,而Control类具有Text和Font属性,

    所以Text和Font属性没有实现,而是隐藏在属性里。

    我们可以改变这一点,使文本呈现:

      4.1 重写Text属性和Font属性

            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            [Category("Appearance")]
            [Description("The text associated with the control.")]
            public override string Text
            {
                get
                {
                    return base.Text;
                }
                set
                {
                    base.Text = value;
                }
            }
    
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            [Category("Appearance")]
            [Description("The font used to display text in the control.")]
            public override Font Font
            {
                get
                {
                    return base.Font;
                }
                set
                {
                    base.Font = value;
                }
            }

      4.2 重写方法:绘制文本

        OnPaint

    由于ImageButton继承的PictureBox控件不会呈现文本,我们必须添加代码来绘制按钮上的文本。

    OnPain可以处理图像的绘制和其他。然后根据Text的大小,与ImageButton大小比较,找到开始绘制文本的位置。

        OnTextChanged

    当控件文本改变时,我们必须重新绘制控件。

    因此重写OnTextChanged方法,并调用Refresh方法,该方法重新绘制了该按钮(Refresh方法继承自PictureBox)。

            protected override void OnPaint(PaintEventArgs pe)
            {
                base.OnPaint(pe);
                if ((!string.IsNullOrEmpty(Text)) && (pe != null) && (base.Font != null))
                {
                    SolidBrush drawBrush = new SolidBrush(base.ForeColor);
                    SizeF drawStringSize = pe.Graphics.MeasureString(base.Text, base.Font);
                    PointF drawPoint;
                    if (base.Image != null)
                        drawPoint = new PointF(base.Image.Width / 2 - drawStringSize.Width / 2, base.Image.Height / 2 - drawStringSize.Height / 2);
                    else
                        drawPoint = new PointF(base.Width / 2 - drawStringSize.Width / 2, base.Height / 2 - drawStringSize.Height / 2);
                    pe.Graphics.DrawString(base.Text, base.Font, drawBrush, drawPoint);
                }
            }
    
            protected override void OnTextChanged(EventArgs e)
            {
                Refresh();
                base.OnTextChanged(e);
            }

     5.隐藏属性

    对于一些继承了PictureBox,而对ImageButton不是很有用的属性,我们希望它们从Property窗口隐藏起来。

    为了做到这一点,我们使用了与Text和Font属性相反的处理。

            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new Image Image { get { return base.Image; } set { base.Image = value; } }
    
            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } }
    
            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } }
    
            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new String ImageLocation { get { return base.ImageLocation; } set { base.ImageLocation = value; } }
    
            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new Image ErrorImage { get { return base.ErrorImage; } set { base.ErrorImage = value; } }
    
            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new Image InitialImage { get { return base.InitialImage; } set { base.InitialImage = value; } }
    
            [Browsable(false)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public new bool WaitOnLoad { get { return base.WaitOnLoad; } set { base.WaitOnLoad = value; } }

     6.说明变更

    SizeMode和BorderStyle属性描述提到的是pictureBox,而不是ImageButton。下面的代码会改变属性的Description。

            [Description("Controls how the ImageButton will handle image placement and control sizing.")]
            public new PictureBoxSizeMode SizeMode { get { return base.SizeMode; } set { base.SizeMode = value; } }
    
            [Description("Controls what type of border the ImageButton should have.")]
            public new BorderStyle BorderStyle { get { return base.BorderStyle; } set { base.BorderStyle = value; } }

     7.实现IButtonControl

    我们也要实现IButtonControl,很简单,我们所要做的就是实现这些方法:

            private bool isDefault = false;
    
            private DialogResult m_DialogResult;
            public DialogResult DialogResult
            {
                get
                {
                    return m_DialogResult;
                }
                set
                {
                    m_DialogResult = value;
                }
            }
    
            public void NotifyDefault(bool value)
            {
                isDefault = value;
            }
    
            public void PerformClick()
            {
                base.OnClick(EventArgs.Empty);
            }

     8.键盘方法

    我们必须实现键盘事件,以便用户可以使用空格键和输入键“点击”按钮。

    如果它是一个key up或key down事件,我们会将消息发送到控件。如果是,我们检查它是什么键。如果是输入键,我们只需调用点击事件。

    如果是空格键,则按住按钮直到:

    1. 用户放开空格键,在这种情况下,我们执行点击或 
    2. 用户按Escape,Tab或控件丢失焦点,在这种情况下,我们不调用点击事件

    如果不是空格键,而不是输入键,我们让PictureBox基类方法处理消息。

    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private bool holdingSpace = false;
    public override bool PreProcessMessage(ref Message msg)
    {
        if (msg.Msg == WM_KEYUP)
        {
            if (holdingSpace)
            {
                if ((int)msg.WParam == (int)Keys.Space)
                {
                    OnMouseUp(null);
                    PerformClick();
                }
                else if ((int)msg.WParam == (int)Keys.Escape
                    || (int)msg.WParam == (int)Keys.Tab)
                {
                    holdingSpace = false;
                    OnMouseUp(null);
                }
            }
            return true;
        }
        else if (msg.Msg == WM_KEYDOWN)
        {
            if ((int)msg.WParam == (int)Keys.Space)
            {
                holdingSpace = true;
                OnMouseDown(null);
            }
            else if ((int)msg.WParam == (int)Keys.Enter)
            {
                PerformClick();
            }
            return true;
        }
        else
            return base.PreProcessMessage(ref msg);
    }
    protected override void OnLostFocus(EventArgs e)
    {
        holdingSpace = false;
        OnMouseUp(null);
        base.OnLostFocus(e);
    }

    9.演示程序

  • 相关阅读:
    Python学习-day18 Web框架
    Python学习-day17 jQuery method and demo
    centos 7环境下安装rabbitmq
    linux java7升级到java8
    idea推送项目到github
    HttpServletRequest.getInputStream()多次读取问题
    spring boot 自动装配的原理
    mysql 的INNODB引擎和MYISAM引擎的区别、索引相关
    事务的传播性和隔离级别
    Atomic 的实现原理
  • 原文地址:https://www.cnblogs.com/Sukie-s-home/p/6782896.html
Copyright © 2020-2023  润新知