原文地址: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 / - drawStringSize.Width / , base.Image.Height / - drawStringSize.Height / );
else
drawPoint = new PointF(base.Width / - drawStringSize.Width / , base.Height / - drawStringSize.Height / );
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.演示程序

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

  1. C#winform自定义控件模拟设计时界面鼠标移动和调节大小、选中效果

    要想玩转Winform自定义控件需要对GDI+非常熟悉,对常用的控件有一些了解,好选择合适的基类控件来简化. 要点说明及代码 1)定义接口: using System; using System.Wi ...

  2. Winform自定义控件实例

    本文转自http://www.cnblogs.com/hahacjh/archive/2010/04/29/1724125.html 写在前面: .Net已经成为许多软件公司的选择,而.Net自定义W ...

  3. (六十九)c#Winform自定义控件-垂直滚动条

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  4. (七十九)c#Winform自定义控件-导航菜单

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  5. WPF中关于自定义控件的滚动条鼠标停留在内容上鼠标滚轮滚动无效的问题

    问题起因:在一个用户控件里放置了1个TreeView垂直顺序放置. 当用户控件中的内容超过面板大小时,滚动条会自动出现 ,但是只有当鼠标指示在右边滚动条的那一条位置时,才支持鼠标滚轴滚动. 点在控件内 ...

  6. winform 自定义控件:半透明Loading控件

    winform  自定义控件:半透明Loading控件 by wgscd date:2015-05-05 效果: using System;using System.Drawing;using Sys ...

  7. c#Winform自定义控件-目录

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  8. (二)c#Winform自定义控件-按钮

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  9. (三)c#Winform自定义控件-有图标的按钮

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

随机推荐

  1. 小菜鸟之HTML第二课

    JavaScript 运行在浏览器上的一种基于对象和事件的驱动的脚本语言 基于对象(windows – document location histroy 便于调用对象属性和方法 事件驱动 键盘和鼠标 ...

  2. JavaScript引用类型简单记录

    Object Array instanceof Function 引用类型 - Object {} 等价于 new Object() 我们经常使⽤用对象来承载可选参数,⽽而⽤用 命名的形式参数来传递必 ...

  3. kafka安装使用配置1.2

    进入cd /usr/local/flume/conf/ vi kafka.conf 配置 agent.sources=s1 agent.channels=c1 agent.sinks=k1 agent ...

  4. C++中用ODBC和ADO方式连接SQL数据库

    https://wenku.baidu.com/view/f01e4e762f3f5727a5e9856a561252d380eb2033.html

  5. Jupyter修改默认文件保存路径

    一.Jupyter安装 前提需要已经安装好python环境~ 接着,Python3x版本安装路径下执行pip命令安装 pip3 install Jupyter 看网速,安装完后会显示安装成功一段话即可 ...

  6. Open-falcon监控

    https://book.open-falcon.org/zh_0_2/ 本文档记录了CentOS7.4下open-falcon-v2监控系统的部署流程,以及一些需要注意的地方. 环境准备 安装Red ...

  7. Go语言GOMAXPROCS(调整并发的运行性能)

    在 Go语言程序运行时(runtime)实现了一个小型的任务调度器.这套调度器的工作原理类似于操作系统调度线程,Go 程序调度器可以高效地将 CPU 资源分配给每一个任务.传统逻辑中,开发者需要维护线 ...

  8. .Net面试题四

    1.C#编译成的dll存放在哪个目录?C#程序文件的后缀名是什么?.csproj后缀名是什么文件? 2.请写出C#中常用文件操作类.数据库操作类.网络请求类.每项至少写出三个 3.请定义一个只读属性: ...

  9. L1-025. 正整数A+B 简单复习一下,。

    本题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000].稍微有点麻烦的是,输入并不保证是两个正整数. 输入格式: 输入在一行给出A和B,其间以空格分开.问题是A和B不一定是满 ...

  10. mybatis抛出异常(java.sql.SQLException: Incorrect string value: '\xF0\x9F\x92\x94' for column 'name' at row 1)

    文章参考 https://blog.csdn.net/junsure2012/article/details/42171035 https://www.cnblogs.com/WangYunShuai ...