WinForm ImageButton

介绍

ImageButton是一个简单的。net类,用于web悬停按钮的WinForm版本,它支持“空闲”图像、悬停和“关闭”图像。在。net中设计定制ui时,它非常有用。

背景

如果您要修改源代码,了解. net中重写、继承、隐藏、图像和属性的一般知识将非常有用。你需要知道如何使用图像编辑器来创建按钮,或者使用网页上的按钮生成器。

使用的代码

图像中的文字

在开始使用代码之前,您需要创建一组图像—普通图像、悬停图像和向下图像—所有这些都是可选的,但是将它们组合在一起可以产生最佳效果。

你会创建这样的东西:

正常:ExampleButton.png徘徊:ExampleButtonHover.png: ExampleButtonDown.png

现在从bin/Release中添加ImageButton.dll作为项目的引用,并将ImageButton控件拖放到窗体中。窗体上将有一个类似于picturebox的控件。

更改属性窗口中的NormalImage、HoverImage和DownImage属性以匹配已创建的图像集。

图像中没有文字

你可以创建一个没有文本的图像集,这样你就可以使用VS设计器设置文本。这意味着您不需要为每个按钮创建单独的图像集。

注意:文本中没有自动换行功能,你必须自己给文本添加换行符(VB中的vbCrLf或c#中的\n)。

首先,创建一个内部没有按钮文本的图像集;是这样的:

正常:ExampleButtonA.png徘徊:ExampleButtonHoverA.png: ExampleButtonDownA.png

然后,像以前一样,添加ImageButton控件并设置图像属性,但这次,将Text属性设置为您想要显示的文本,将Font属性设置为您想要使用的字体。

它是如何工作的

为了创建控件,我创建了ImageButton类,覆盖了PictureBox控件并实现了IButtonControl。实现IButtonControl将允许ImageButton被用作表单上的任何其他按钮,作为默认按钮或取消按钮。

鼠标的方法

概念很简单——我们创建一个图像并将其显示在屏幕上。如果用户悬停在图像上,我们将图像交换为悬停图像,如果用户按住鼠标,我们将图像更改为按下(“向下”)的图像。

因此,为此存在以下方法覆盖:

#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
        private bool hover = false;
        private bool down = false;
        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);
        }

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

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

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

我们来复习一下:

需要

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);
}

当按钮在窗体上移动时,OnMouseMove被调用。我们避免使用OnMouseHover,因为在调用该方法之前会有延迟。

我们将悬停布尔值设置为true,以便其他方法知道鼠标何时悬停在控件上。然后我们检查鼠标按钮是否按下。如果是,我们将按钮图像设置为DownImage,如果该图像不是null,并且该图像还不是已关闭的图像。

如果鼠标没有被按下,我们检查是否存在悬停图像-如果存在,我们将图像设置为悬停图像,否则,我们将其设置为正常图像。

在最后一行,我们调用Picturebox版本的OnMouseMove。

OnMouseLeave

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

这需要一点解释。:)如果鼠标已经离开了控件的边界,我们设置鼠标是否在按钮上徘徊的开关为false,并将图像设置为“正常”图像。我们让图片框从那里接手工作。

OnMouseDown

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

如果鼠标按下了控件,我们将焦点转移到图像按钮(这不是默认行为,所以我们必须实现它),并将down布尔值设置为true。如果设计器设置了已关闭的图像,则将图像更改为已关闭的图像。然后我们调用picturebox自己的OnMouseDown拷贝。

OnMouseUp

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

鼠标按钮不再被按下,因此我们设置为false。如果我们悬停在控件上并松开,那么我们将图像设置为悬停图像,如果它不是什么,否则我们将它设置为“正常图像”。之后,我们告诉PictureBox继续处理OnMouseUp。

空值

正如你可能已经注意到的,在我们改变图像之前,我们检查悬停和向下的图像是否为空,但是在处理NormalImage时,我们不这样做。这是为了防止当用户没有悬停或者在用户没有指定正常图像的情况下点击控件时,悬停/关闭图像粘贴。在演示应用程序中,示例F演示了这一点。

文本

根据控件类的需要,PictureBox控件具有文本和字体属性,但是它们没有实现,并且隐藏在属性窗口中。我们可以改变这个,使文本呈现:

[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;
    }
}
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);
}

覆盖

PictureBox从属性窗口隐藏文本和字体属性。为了解决这个问题,我们为文本和字体属性创建了“虚拟”覆盖,这只是简单地设置基类属性,但是为它们分配Browsable和DesignerSerializationVisibility属性,这将告诉设计人员“注意”这些属性。

绘画

因为我们继承的PictureBox控件不呈现文本,所以我们必须在OnPaint中添加代码来在按钮上绘制文本。首先,我们调用OnPaint的PictureBox基类方法,它处理图像的绘制和其他所有事情。在此之后,我们将通过测量选定字体时文本的大小,并将其与ImageButton的大小进行比较,从而在中间绘制的图像的顶部绘制文本,以找到开始绘制文本的位置。

OnTextChanged

当控件的文本发生更改时,必须重新绘制该控件。因此,我们重写OnTextChanged方法,并在其中添加对Refresh方法(从PictureBox继承)的调用,刷新方法将重新绘制按钮。

隐藏属性

有一些属性对ImageButton没有对PictureBox那么有用,我们想在属性窗口中隐藏它们。为此,我们使用与文本和字体属性相反的代码:

[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; } }

描述变化

SizeMode和BorderStyle属性提到了PictureBox而不是ImageButton。为了解决这个问题,我们简单地使用属性和“虚拟”属性来改变属性的描述,代码如下:

[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; } }

IButtonControl

我们还必须实现IButtonControl。这是简单的做,所有我们必须做的是实现方法,如:

private bool isDefault = false;

        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);
        }

键盘的方法

我们必须实现键盘事件,以便用户可以使用空格键和回车键来“点击”按钮,就像任何其他窗口窗体上的任何其他按钮一样。

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);
}

简单地说,如果发送到控件的消息是键向上或键向下事件,我们就会捕获它。如果是,我们检查它是什么键。如果是enter键,我们只需调用一个click事件。如果是空格键,我们在按下按钮时按住它,直到:

    用户放开空格,在这种情况下我们执行单击,或者用户按Escape、Tab或控件失去焦点,在这种情况下我们不调用单击事件

如果不是空格键,也不是回车键,我们让PictureBox基类方法处理消息。

演示应用程序

ZIP中包含了一个演示应用程序。这里有一个截图:

imagebuttondemo.png

享受吧!

本文转载于:http://www.diyabc.com/frontweb/news14600.html

posted @ 2020-08-12 02:22  Dincat  阅读(201)  评论(0编辑  收藏  举报