C# Winform 实现Ajax效果自定义按钮
Posted 天道酬勤,商道酬信。
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# Winform 实现Ajax效果自定义按钮相关的知识,希望对你有一定的参考价值。
技术看点
- WinForm自定义控件的使用
- 自定义控件gif动画的播放
需求及效果
又来一波 C# GDI自定义控件show 。这个控件已经使用几年了,最近找出来重构一下。原来是没有边框的,那么导致导航的功能不是很突出。本来想加个效果:在执行单击时显示Loading动画,在执行完单击事件后恢复原样。这就是网页里见到的局部刷新,Ajax常用的场景。需求来自几年前一个智能储物柜项目,人机界面有个美工设计好的效果图,为了省事和通用,需要一个透明的按钮来实现导航的任务。就是控件只是设计时可见,运行时不可见。
关键点说明
1)、GraphicsPath实现矩形的圆角羽化处理
using (GraphicsPath path = new GraphicsPath()) { #region 羽化,圆角处理 path.StartFigure(); path.AddArc(new Rectangle(new Point(rect.X, rect.Y), new Size(2 * Radius, 2 * Radius)), 180, 90); path.AddLine(new Point(rect.X + Radius, rect.Y), new Point(rect.Right - Radius, rect.Y)); path.AddArc(new Rectangle(new Point(rect.Right - 2 * Radius, rect.Y), new Size(2 * Radius, 2 * Radius)), 270, 90); path.AddLine(new Point(rect.Right, rect.Y + Radius), new Point(rect.Right, rect.Bottom - Radius)); path.AddArc(new Rectangle(new Point(rect.Right - 2 * Radius, rect.Bottom - 2 * Radius), new Size(2 * Radius, 2 * Radius)), 0, 90); path.AddLine(new Point(rect.Right - Radius, rect.Bottom), new Point(rect.X + Radius, rect.Bottom)); path.AddArc(new Rectangle(new Point(rect.X, rect.Bottom - 2 * Radius), new Size(2 * Radius, 2 * Radius)), 90, 90); path.AddLine(new Point(rect.X, rect.Bottom - Radius), new Point(rect.X, rect.Y + Radius)); path.CloseFigure(); #endregion
要点就是画几段弧线和矩形连接起来。透明就是用了Color.FromArgb加上透明度,然后填充GraphicsPath形成透明区域。
g.FillPath(new SolidBrush(Color.FromArgb(153, BackColor)), path);
2)、单窗体应用如何模块化
窗体只有一个,但操作界面好多个,由于是无人值守的应用。那么老是切换窗体操作是非常不方便的。工作区域是一个容器Panel,把每个操作界面定义成一个Panel作为只容器。
public partial class DepositBizPanel : UserControl { private BackgroundStyle backgroundStyle = BackgroundStyle.Green; /// <summary> /// 主题风格 /// </summary> public BackgroundStyle BackgroundStyle { get { return backgroundStyle; } set { backgroundStyle = value; switch (value) { case GreenlandExpressBox.BackgroundStyle.Blue: BackgroundImage = Properties.Resources.jbblue; break; case GreenlandExpressBox.BackgroundStyle.Orange: BackgroundImage = Properties.Resources.jborange; break; case GreenlandExpressBox.BackgroundStyle.Green: BackgroundImage = Properties.Resources.jbgreen; break; } Invalidate(); } } public Panel ParentPanel { get; set; } public Bitmap QR_Barcode { get { return (Bitmap)pbxBarcode.Image; } set { pbxBarcode.Image = value; } } public DialogResult PanelDiagResult { get; set; } public DepositBizPanel(Panel parent, Bitmap barcode, BackgroundStyle style) { InitializeComponent(); DoubleBuffered = true; ParentPanel = parent; QR_Barcode = barcode; BackgroundStyle = style; } private void btnback_Click(object sender, EventArgs e) { foreach (Control panel in ParentPanel.Controls) { if (panel is DepositBizPanel) { ParentPanel.Controls.Remove(panel); PanelDiagResult = DialogResult.Cancel; break; } } } private void btnprocessnext_Click(object sender, EventArgs e) { foreach (Control panel in ParentPanel.Controls) { if (panel is DepositBizPanel) { ParentPanel.Controls.Remove(panel); PanelDiagResult = DialogResult.OK; break; } } } }
3)、控件播放gif动画
private void BeginAnimate() { if (m_AnimateImage == null) return; if (ImageAnimator.CanAnimate(m_AnimateImage)) { //当gif动画每隔一定时间后,都会变换一帧,那么就会触发一事件, //该方法就是将当前image每变换一帧时,都会调用当前这个委托所关联的方法。 ImageAnimator.Animate(m_AnimateImage, m_evthdlAnimator); } } private void StopAnimate() { if (m_AnimateImage == null) return; try { if (ImageAnimator.CanAnimate(m_AnimateImage)) { ImageAnimator.StopAnimate(m_AnimateImage, m_evthdlAnimator); } } finally { m_IsExecuted = false; } } private void UpdateImage() { if (m_AnimateImage == null) return; if (ImageAnimator.CanAnimate(m_AnimateImage)) { //获得当前gif动画的下一步需要渲染的帧,当下一步任何对当前gif动画的操作都是对该帧进行操作) ImageAnimator.UpdateFrames(m_AnimateImage); } } private void OnImageAnimate(Object sender, EventArgs e) { Invalidate(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); string s1 = @"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"; byte[] buffer = Convert.FromBase64String(s1); MemoryStream ms = new MemoryStream(buffer); var srcImg = Image.FromStream(ms); m_AnimateImage = srcImg; }
OnLoad执行的操作是从base64字符串里反序列化图片,就是效果图中的Loading的gif图片。这里遇到一个问题:在关闭了MemoryStream之后,会出现“gdi+ 中发生一般性错误”,于是改为不关闭了,控件销毁之后占用的内存就会释放吧。这是一点隐忧,如果有好的办法,希望留言告知。
透明按钮自定义控件全部代码
第一版自定义按钮:
/// <summary> /// Cool透明自定义按钮 /// </summary> public partial class CoolTransparentButton : UserControl { private Size iconSize = new Size(32, 32); public Size IconSize { get { return iconSize; } set { iconSize = value; Invalidate(); } } private string _ButtonText; public string ButtonText { get { return _ButtonText; } set { _ButtonText = value; Invalidate(); } } protected Image _IconImage; public Image IconImage { get { return _IconImage; } set { _IconImage = value; Invalidate(); } } private bool _FocseActived = false; private Color _BorderColor = Color.White; public Color BorderColor { get { return _BorderColor; } set { _BorderColor = value; Invalidate(); } } private int _Radius = 12; public int Radius { get { return _Radius; } set { _Radius = value; Invalidate(); } } private bool ifDrawBorderWhenLostFocse = true; /// <summary> /// 失去焦点是否画边框 /// </summary> public bool IfDrawBorderWhenLostFocse { get { return ifDrawBorderWhenLostFocse; } set { ifDrawBorderWhenLostFocse = value; Invalidate(); } } /// <summary> /// 是否处于激活状态(焦点) /// </summary> public bool FocseActived { get { return _FocseActived; } set { _FocseActived = value; Invalidate(); } } public CoolTransparentButton() { DoubleBuffered = true; BackColor = Color.Transparent; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); SetStyle(ControlStyles.Opaque, false); UpdateStyles(); } protected override void OnPaint(PaintEventArgs e) { var rect = ClientRectangle; rect.Inflate(-1, -1); Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.HighQuality; using (GraphicsPath path = new GraphicsPath()) { #region 羽化,圆角处理 path.StartFigure(); path.AddArc(new Rectangle(new Point(rect.X, rect.Y), new Size(2 * Radius, 2 * Radius)), 180, 90); path.AddLine(new Point(rect.X + Radius, rect.Y), new Point(rect.Right - Radius, rect.Y)); path.AddArc(new Rectangle(new Point(rect.Right - 2 * Radius, rect.Y), new Size(2 * Radius, 2 * Radius)), 270, 90); path.AddLine(new Point(rect.Right, rect.Y + Radius), new Point(rect.Right, rect.Bottom - Radius)); path.AddArc(new Rectangle(new Point(rect.Right - 2 * Radius, rect.Bottom - 2 * Radius), new Size(2 * Radius, 2 * Radius)), 0, 90); path.AddLine(new Point(rect.Right - Radius, rect.Bottom), new Point(rect.X + Radius, rect.Bottom)); path.AddArc(new Rectangle(new Point(rect.X, rect.Bottom - 2 * Radius), new Size(2 * Radius, 2 * Radius)), 90, 90); path.AddLine(new Point(rect.X, rect.Bottom - Radius), new Point(rect.X, rect.Y + Radius)); path.CloseFigure(); #endregion if (!FocseActived) { if (ifDrawBorderWhenLostFocse) g.DrawPath(new Pen(Color.Gray, 1), path); g.FillPath(new SolidBrush(Color.FromArgb(66, BackColor)), path); } else { g.DrawPath(new Pen(BorderColor, 1), path); rect.Inflate(-1, -1); g.FillPath(new SolidBrush(Color.FromArgb(153, BackColor)), path); } #region 画文本 g.SmoothingMode = SmoothingMode.AntiAlias; if (IconImage != null) { Rectangle rc = new Rectangle((Width - 32) / 2, 16, IconSize.Width, IconSize.Height); g.DrawImage(IconImage, rc); } if (!string.IsNullOrEmpty(ButtonText)) { using (StringFormat f = new StringFormat()) { Rectangle rectTxt = new Rectangle(0, (Height - 18) / 2, Width, 36); f.Alignment = StringAlignment.Center;// 水平居中对齐 f.LineAlignment = StringAlignment.Center; // 垂直居中对齐 f.FormatFlags = StringFormatFlags.NoWrap;// 设置为单行文本 SolidBrush fb = new SolidBrush(this.ForeColor); // 绘制文本 e.Graphics.DrawString(ButtonText, new Font("微软雅黑", 16F, FontStyle.Bold), fb, rectTxt, f); } } #endregion } } protected override void OnMouseHover(EventArgs e) { FocseActived = true; } protected override void OnMouseLeave(EventArgs e) { FocseActived = false; } protected override void OnEnter(EventArgs e) { FocseActived = true; } protected override void OnLeave(EventArgs e) { FocseActived = false; } }
第二版自定义按钮:
/// <summary> /// 自定义透明自定义按钮,模仿实现了网页元素的Ajax效果 /// </summary> public partial class AjaxTransparentButton : UserControl { private Size iconSize = new Size(32, 32); public Size IconSize { get { return iconSize; } set { iconSize = value; Invalidate(); } } private string _ButtonText; public string ButtonText { get { return _ButtonText; } set { _ButtonText = value; Invalidate(); } } protected Image _IconImage; public Image IconImage { get { return _IconImage; } set { _IconImage = value; Invalidate(); } } private bool _FocseActived = false; private Color _BorderColor = Color.White; public Color BorderColor { get { return _BorderColor; } set { _BorderColor = value; Invalidate(); } } private int _Radius = 12; public int Radius { get { return _Radius; } set { _Radius = value; Invalidate(); } } private bool ifDrawBorderWhenLostFocse = true; /// <summary> /// 失去焦点是否画边框 /// </summary> public bool IfDrawBorderWhenLostFocse { get { return ifDrawBorderWhenLostFocse; } set { ifDrawBorderWhenLostFocse = value; Invalidate(); } } /// <summary> /// 是否处于激活状态(焦点) /// </summary> public bool FocseActived { get { return _FocseActived; } set { _FocseActived = value; Invalidate(); } } private Image m_AnimateImage = null; private EventHandler m_evthdlAnimator = null; private bool m_IsExecuted = false; public AjaxTransparentButton() { BackColor = Color.Transparent; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true); SetStyle(ControlStyles.Opaque, false); UpdateStyles(); m_evthdlAnimator = new EventHandler(OnImageAnimate); } protected override void OnPaint(PaintEventArgs e) { var rect = ClientRectangle; rect.Inflate(-1, -1); Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.HighQuality; using (GraphicsPath path = new GraphicsPath()) { #region 羽化,圆角处理 path.StartFigure(); path.AddArc(new Rectangle(new Point(rect.X, rect.Y), new Size(2 * Radius, 2 * Radius)), 180, 90); path.AddLine(new Point(rect.X + Radius, rect.Y), new Point(rect.Right - Radius, rect.Y)); path.AddArc(new Rectangle(new Point(rect.Right - 2 * Radius, rect.Y), new Size(2 * Radius, 2 * Radius)), 270, 90); path.AddLine(new Point(rect.Right, rect.Y + Radius), new Point(rect.Right, rect.Bottom - Radius)); path.AddArc(new Rectangle(new Point(rect.Right - 2 * Radius, rect.Bottom - 2 * Radius), new Size(2 * Radius, 2 * Radius)), 0, 90); path.AddLine(new Point(rect.Right - Radius, rect.Bottom), new Point(rect.X + Radius, rect.Bottom)); path.AddArc(new Rectangle(new Point(rect.X, rect.Bottom - 2 * Radius), new Size(2 * Radius, 2 * Radius)), 90, 90); path.AddLine(new Point(rect.X, rect.Bottom - Radius), new Point(rect.X, rect.Y + Radius)); path.CloseFigure(); #endregion if (!FocseActived) { if (ifDrawBorderWhenLostFocse) g.DrawPath(new Pen(Color.Gray, 1), path); g.FillPath(new SolidBrush(Color.FromArgb(66, BackColor)), path); } else { g.DrawPath(new Pen(BorderColor, 1), path); rect.Inflate(-1, -1); g.FillPath(new SolidBrush(Color.FromArgb(153, BackColor)), path); } #region 画文本 g.SmoothingMode = SmoothingMode.AntiAlias; if (IconImage != null) { Rectangle rc = new Rectangle((Width - 32) / 2, 16, IconSize.Width, IconSize.Height); g.DrawImage(IconImage, rc); } if (!string.IsNullOrEmpty(ButtonText)) { using (StringFormat f = new StringFormat()) { Rectangle rectTxt = new Rectangle(0, (Height - 18) / 2, Width, 36); f.Alignment = StringAlignment.Center;// 水平居中对齐 f.LineAlignment = StringAlignment.Center; // 垂直居中对齐 f.FormatFlags = StringFormatFlags.NoWrap;// 设置为单行文本 SolidBrush fb = new SolidBrush(this.ForeColor); // 绘制文本 e.Graphics.DrawString(ButtonText, new Font("微软雅黑", 16F, FontStyle.Bold), fb, rectTxt, f); } } if (m_AnimateImage != null) { Rectangle rectGif = new Rectangle((Width - 24) / 2, (Height - 16) / 2 - 8, 32, 32); if (m_IsExecuted) { UpdateImage(); e.Graphics.DrawImage(m_AnimateImage, rectGif); } else { e.Graphics.FillRectangle(new SolidBrush(Color.Transparent), rectGif); } } #endregion } } protected override void OnMouseHover(EventArgs e) { FocseActived = true; } protected override void OnMouseLeave(EventArgs e) { FocseActived = false; } protected override void OnEnter(EventArgs e) { FocseActived = true; } protected override void OnLeave(EventArgs e) { FocseActived = false; } private void BeginAnimate() { if (m_AnimateImage == null) return; if (ImageAnimator.CanAnimate(m_AnimateImage)) { //当gif动画每隔一定时间后,都会变换一帧,那么就会触发一事件, //该方法就是将当前image每变换一帧时,都会调用当前这个委托所关联的方法。 ImageAnimator.Animate(m_AnimateImage, m_evthdlAnimator); } } private void StopAnimate() { if (m_AnimateImage == null) return; try { if (ImageAnimator.CanAnimate(m_AnimateImage)) { ImageAnimator.StopAnimate(m_AnimateImage, m_evthdlAnimator); } } finally { m_IsExecuted = false; } } private void UpdateImage() { if (m_AnimateImage == null) return; if (ImageAnimator.CanAnimate(m_AnimateImage)) { //获得当前gif动画的下一步需要渲染的帧,当下一步任何对当前gif动画的操作都是对该帧进行操作) ImageAnimator.UpdateFrames(m_AnimateImage); } } private void OnImageAnimate(Object sender, EventArgs e) { Invalidate(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); string s1 = @"R0lGODlhIAAgALMAAP///7Ozs/v7+9bW1uHh4fLy8rq6uoGBgTQ0NAEBARsbG8TExJeXl/39/VRUVAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAAACwAAAAAIAAgAAAE5xDISSlLrOrNp0pKNRCdFhx以上是关于C# Winform 实现Ajax效果自定义按钮的主要内容,如果未能解决你的问题,请参考以下文章
[原创]c# wpf自定义 任意颜色阴影特效融合winform的探索之路
WinForm(C#)自定义控件之——RoundButton(圆形按钮)