【原创】自己动手写控件----XSmartNote控件

一、前面的话

在上一篇博文自己动手写工具----XSmartNote [Beta 3.0]中,用到了若干个自定义控件,其中包含用于显示Note内容的简单的Label扩展控件,用于展示标签内容的label扩展控件,还有包含自定义事件的含checkbox的控件。自定义控件的好处就是其灵活程度很高,不但可以扩展控件的外观,还可以扩展控件的事件,甚至从底层拦截Windows消息进行处理,这也是我喜欢自己写控件的原因。至于自定义控件的几种形式在这里就不说了,有兴趣的小伙伴可以百度一下,下面来看看这些控件的实现过程。

二、控件效果

这部分控件除了上面的TextBoxEx,其他主要是基于Label进行扩展的,有的包含事件,有的包含动态效果,还有的控件只是用于简单地显示信息。

三、控件实现

1、 LabelEx控件

LabelEx控件既包含简单的动态效果,也有自己的事件,首先新建用户控件,继承需要扩展的控件,这里继承的是Label控件,这样就可以使用Label的所有的成员。完善构造函数,并绑定label的LabelEx_TextChanged事件,绑定事件是为了当文字长度发生变化时,LabelEx能够自动适应。然后重写OnPaint方法,绘制自己需要的外观,注意base.OnPaint(e)这句,如果不加上这句代码,控件上的文本是不会显示的。具体看代码:

 1 public partial class LabelEx : Label
 2 {
 3
 4     #region PARAMS
 5     private const int WIDTH = 0x0226;//550
 6     private const int HEIGHT = 0x002F;//47
 7     #endregion
 8
 9     #region CONSTRUCTOR
10     public LabelEx()
11        : base()
12     {
13         this.Font = new Font(new FontFamily("微软雅黑"), 10.0f);
14         InitializeComponent();
15         this.AutoSize = false;
16         //设置内边距
17         this.Padding = new Padding(5);
18         //设置外边距
19         this.Margin = new Padding(5);
20         this.Width = WIDTH;
21         this.Height = HEIGHT;
22         this.TextChanged += LabelEx_TextChanged;
23     }
24     #endregion
25
26
27     #region Property
28     public Color NormalBorderColor { get; set; }
29     public Color HighLightBorderColor { get; set; }
30     #endregion
31
32     #region EVENTS
33     private void LabelEx_TextChanged(object sender, EventArgs e)
34     {
35         //文字变化后改变一下当前的大小
36         System.Drawing.Size ps = GetPreferredSize(this.Size);
37         if (ps.Height < HEIGHT)
38         {
39             ps.Height = HEIGHT;
40         }
41         this.Size = new System.Drawing.Size(this.Width, ps.Height);
42     }
43     #endregion
44
45     #region OVERRIDE
46     protected override void OnPaint(PaintEventArgs e)
47     {
48         base.OnPaint(e);//解决了文字不显示的问题
49         Graphics g = e.Graphics;
50         int x = this.Width;
51         int y = this.Height;
52         Point leftTop = new Point(0, 0);
53         Point rightTop = new Point(x - 1, 0);
54         Point leftBottom = new Point(0, y - 1);
55         Point rightBottom = new Point(x - 1, y - 1);
56         //绘制四个边缘,避免被背景色填充
57         g.DrawLine(new Pen(Color.White), leftTop, rightTop);
58         g.DrawLine(new Pen(Color.White), leftBottom, rightBottom);
59         g.DrawLine(new Pen(Color.White), leftTop, leftBottom);
60         g.DrawLine(new Pen(Color.White), rightTop, rightBottom);
61         //画上边缘
62         for (int i = 0; i < x - 1; i += 3)
63         {
64             g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(i, 0, 2, 1));
65         }
66
67         //画下边缘
68         for (int m = 0; m < x - 1; m += 3)
69         {
70             g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(m, y - 1, 2, 1));
71         }
72
73         //画左边缘
74         for (int i = 0; i < y - 1; i += 3)
75         {
76             g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(0, i, 1, 2));
77         }
78
79         //画右边缘
80         for (int i = 0; i < y - 1; i += 3)
81         {
82             g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(x - 1, i, 1, 2));
83         }
84     }
85
86     public override bool AutoSize
87     {
88         get
89         {
90             return false;
91         }
92     }
93     #endregion
94 }

2、LabelExS控件

上面的LabelEx控件只是简单地处理了一下Label控件,而LabelExS控件继承自LabelEx控件,并添加了一些简单的状态控制。这里的重绘方法,和上面的控件大概是一致的,只是稍微做出了修改。不同状态下的绘制代码:

 1 /// <summary>
 2 /// 激活态
 3 /// </summary>
 4 /// <param name="g"></param>
 5 private void DrawHighLightBorder(Graphics g)
 6 {
 7     int x = this.Width;
 8     int y = this.Height;
 9     //画上边缘
10     for (int i = 0; i < x - 1; i++)
11     {
12         g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(i, 0, 1, 1));
13     }
14
15     //画下边缘
16     for (int m = 0; m < x - 1; m++)
17     {
18         g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(m, y - 1, 1, 1));
19     }
20
21     //画左边缘
22     for (int i = 0; i < y - 1; i++)
23     {
24         g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(0, i, 1, 1));
25     }
26
27     //画右边缘
28     for (int i = 0; i < y - 1; i++)
29     {
30         g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(x - 1, i, 1, 1));
31     }
32 }
33
34 /// <summary>
35 /// 常规态
36 /// </summary>
37 /// <param name="g"></param>
38 private void DrawNormalBorder(Graphics g)
39 {
40     int x = this.Width;
41     int y = this.Height;
42     //画上边缘
43     for (int i = 0; i < x - 1; i++)
44     {
45         g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(i, 0, 1, 1));
46     }
47
48     //画下边缘
49     for (int m = 0; m < x - 1; m++)
50     {
51         g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(m, y - 1, 1, 1));
52     }
53
54     //画左边缘
55     for (int i = 0; i < y - 1; i++)
56     {
57         g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(0, i, 1, 1));
58     }
59
60     //画右边缘
61     for (int i = 0; i < y - 1; i++)
62     {
63         g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(x - 1, i, 1, 1));
64     }
65 }

根据状态进行绘制:

 1 protected override void OnPaint(PaintEventArgs e)
 2 {
 3     base.OnPaint(e);
 4     switch (State)
 5     {
 6         case 0:
 7             DrawNormalBorder(g);
 8             break;
 9         case 1:
10             DrawHighLightBorder(g);
11             break;
12         default:
13             DrawNormalBorder(g);
14             break;
15     }
16 }

鼠标事件控制,进入或离开的时候,强制区域无效并更新状态,这样OnPaint函数会根据状态进行重新绘制:

 1 /// <summary>
 2 /// 鼠标进入事件
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void LabelExSolidBorder_MouseEnter(object sender, EventArgs e)
 7 {
 8     State = 1;
 9     base.ForeColor = Color.Orange;
10     base.HighLightBorderColor = Color.Orange;// ★可配置
11     this.Invalidate();
12 }
13
14 /// <summary>
15 /// 鼠标离开事件
16 /// </summary>
17 /// <param name="sender"></param>
18 /// <param name="e"></param>
19 private void LabelExSolidBorder_MouseLeave(object sender, EventArgs e)
20 {
21     State = 0;
22     base.ForeColor = Color.Brown;
23     base.NormalBorderColor = Color.White;// ★可配置
24     this.Invalidate();
25 }

为控件绑定Click事件,执行自定义操作:

 1 /// <summary>
 2 /// 观察者模式,鼠标单击后,执行自定义操作
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void LabelExSolidBorder_Click(object sender, EventArgs e)
 7 {
 8     MainForm mf = (MainForm)this.TopLevelControl;
 9     ShowNote += mf.SetText;//绑定事件
10     SetSelectedNode += mf.SetSelectedNode;
11
12     if (ShowNote != null)
13     {
14         ShowNote();//自定义操作
15     }
16 }

3、 RectangleLabel控件

这个控件和第一个控件差不太多,主要的区别就是外围路径的绘制,由矩形变成了圆角矩形,做成了类似标签的效果,在这里仅仅贴出绘制的代码,大概思路就是绘制两条线段,并在合适的位置再绘制两个半圆形并组合在一起就可以了:

 1 protected override void OnPaint(PaintEventArgs e)
 2 {
 3     base.OnPaint(e);
 4     Graphics g = e.Graphics;
 5     Rectangle rect = new Rectangle(new Point(0, 0), new Size(20, 20));
 6     Rectangle rect2 = new Rectangle(new Point(20, 10), new Size(40, 20));
 7     Rectangle rect3 = new Rectangle(new Point(60, 0), new Size(20, 20));
 8     Point[] ps = { new Point(11, 0), new Point(69, 0) };
 9     Point[] pq = { new Point(11, 20), new Point(69, 20) };
10     float start = 90f;
11     float end = 180f;
12     float start2 = 270f;
13     float end2 = 180f;
14     Pen p = new Pen(this.BorderColor, this.BorderWidth);
15     SolidBrush sl = new SolidBrush(this.InnerColor);
16     {
17         g.SmoothingMode = SmoothingMode.HighQuality;
18         g.DrawArc(p, rect, start, end);
19         g.DrawArc(p, rect3, start2, end2);
20         g.DrawLine(p, ps[0], ps[1]);
21         g.DrawLine(p, pq[0], pq[1]);
22     }
23 }

4、LabelWithCheck控件

相对来说,这个控件由Label和ChecBox组成,是这些控件中是最复杂的,因为它本身包含了事件信息和对应的逻辑,就不仅仅是绘制那么简单了。该控件直接继承自UserControl,灵活性也比较高。

添加了若干属性:

 1 [Category("XHB.Controls")]
 2 [Browsable(true)]
 3 [Description("设置或获取LabelText宽度")]
 4 public int LabelWidth
 5 {
 6     get
 7     {
 8         return labelWidth;
 9     }
10     set
11     {
12         labelWidth = value + ck.Width + 13;
13         this.Refresh();
14     }
15 }
16 [Category("XHB.Controls")]
17 [Browsable(true)]
18 [Description("指示LabelText是否被选中")]
19 [DefaultValue(false)]
20 public bool LabelChecked
21 {
22     get
23     {
24         return ck.Checked;
25     }
26     set
27     {
28         ck.Checked = value;
29     }
30 }

代码中的属性上方包含一些控件特性,用于在设计时进行浏览或操作:

Category : 指示控件的分组信息;
Browsable : 指示控件是否可浏览;
Description : 控件的描述信息;
DefaultValue : 控件的默认值;

自定义事件,LabelWithCheck控件的自定义事件继承自EventArgs类:

 1 /// <summary>
 2 /// 事件参数包含的信息
 3 /// </summary>
 4 public class LabelWithCheckEventArgs :EventArgs
 5 {
 6     private string _LabelText;
 7     private Guid _Id;
 8     public LabelWithCheckEventArgs(Guid id,string labelText)
 9     {
10         this._LabelText = labelText;
11         this._Id = id;
12     }
13     public string LabelText
14     {
15         get
16         {
17             return _LabelText;
18         }
19         set
20         {
21             _LabelText = value;
22         }
23     }
24     public Guid Id
25     {
26         get
27         {
28             return _Id;
29         }
30         set
31         {
32             _Id = value;
33         }
34     }
35 }

默认情况下,单击控件的非CheckBox所在区域,CheckBox不会变化,下面实现单击整个控件都会改变CheckBox的选中状态,只要加一丢丢代码就好:

1 /// <summary>
2 /// 单击Label也可以触发CheckBox的选中事件
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 private void lb_Click(object sender, EventArgs e)
7 {
8     ck.Checked = ck.Checked ? false : true;
9 }

四、总结

自定义控件的开发,其实是有规律可循的,无非就是绘制、状态、逻辑的有序组合,无论是简单的控件还是精美复杂的控件,开发的思路都是一样的,掌握了方法,万变不离其宗。这里列出的控件都是入门级的,是适合初学者的,如果文中有表述失误的地方,请提出来,不仅提高自己,更能避免引人误入歧途。具体的代码还是参考我的GitHub上的小工具。注:本文同步发布到我的简书

作者:悠扬的牧笛

博客地址:http://www.cnblogs.com/xhb-bky-blog/p/5523611.html

声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

时间: 2024-08-02 17:34:58

【原创】自己动手写控件----XSmartNote控件的相关文章

【原创】自己动手写工具----XSmartNote [Beta 3.0]

一.前面的话 在动笔之前,一直很纠结到底要不要继续完成这个工具,因为上次给它码代码还是一年多之前的事情,参考自己动手写工具----XSmartNote [Beta 2.0],这篇博文里,很多园友提出了宝贵的意见.最后决定吸收园友的建议把这个工具重构一下.首先给它换了个名 称,XSmartNote.因为各种自身的原因,并没有完成这个工具的基本功能,只是实现了基本的框架.最近有点时间就搞了一下,权当做打下基础.本来想用B/S结构来重新搞一下,毕竟园子里有很多大牛都是专注于ASP.NET MVC,但仔

appium+python:自己写的一个滑动控件的方式

#调用方式roll_ele("ID","ele_id","7","up",3)#将控件分为7格,从底部倒数第二格向上滑动3格#滑动控件的方法def roll_ele(id,ele_id,size,direct,rollsize=1): """ 参数id为定位方式,eled_id为具体控件的id或xpath等,size为需要把该控件分成几格,direct为滑动方向,up或down(上或下),roll

【原创】自己动手写工具----随手记(最终版)

一.前面的话 在上一篇自己动手写工具----随手记中,我简单介绍了这个小玩意儿的大致界面和要实现的功能,看了一下园子里的评论,评价褒贬不一,有人说“现在那么多云笔记的工具”,“极简版evernote”,我想说的是,别人的工具再好用,终究不是自己写的,其实写这个的目的,一方面是锻炼自己的技术能力,在coding的时候,或多或少会遇到一些问题,在解决这些问题的过程中,技术能力就会有所提升:另一方面,写这个东西还有自己个人原因,可以随时记录一些繁杂的知识点,通过给这些知识点打上标签,可以实现归类,那有

【原创】自己动手写工具----签到器(升级版)

一.前面的话 上一篇中基本实现了简单的签到任务,但是不够灵活.在上一篇自己动手写工具----签到器的结尾中,我设想了几个新增功能来提高工具的灵活程度,下面把新增功能点列出来看看: (1)新增其他的进程任务: (2)任务按照进程进行分类:用IE就是执行IE的任务,与其他(如资源管理器等)无关: (3)每执行完一个任务关闭任务窗口: (4)对签到性质的任务进行”已签到“过滤: (5)实现执行的任务列表自定义: (6)另外实现任务的可视化: 还是先来看看效果图: 二.签到器的改进 在上一个版本中,(自

背水一战 Windows 10 (77) - 控件(控件基类): ContentControl, UserControl, Page

原文:背水一战 Windows 10 (77) - 控件(控件基类): ContentControl, UserControl, Page [源码下载] 作者:webabcd 介绍背水一战 Windows 10 之 控件(控件基类 - ContentControl, UserControl, Page) ContentPresenter ContentControl UserControl Page 示例1.演示 ContentPresenter 的基础知识Controls/BaseContro

【原创】连“霍金”都想学习的“人工智能”---【自己动手写神经网络】小白入门连载开始了(1)

欢迎关注[自己动手写神经网络]的博客连载!!! 第1章 神经网络简介 神经网络这个词,相信大家都不陌生.就在你打开本书,并试图了解神经网络时,你已经在使用一个世界上最复杂的神经网络——你的大脑,一个由大约1000亿个神经元(每个单元拥有约1万个连接)构成的复杂系统.但人的大脑太过复杂,以至于科学家们到目前为止仍然无法准确解释大脑的工作原理和方式.但有幸的是,生物神经网络的最最基本的元素已经能够被识别,而这就构成了本书想为你介绍的人工神经网络(Artificial Neural Network).

Android培训准备资料之UI一些相似控件和控件一些相似属性之间的区别

这一篇博客主要收集五大布局中的一些相似控件和控件一些相似属性之间的区别 ImageView ImageButton Button 三者有啥区别? (1)Button继承自TextView,ImageView继承自View,ImageButton继承自ImageView                                              (2)Button支持android:text属性,而ImageButton和ImageView不支持,但是ImageView和ImageB

zw版【转发&#183;台湾nvp系列Delphi例程】Delphi 使用 HALCON库件COM控件数据格式转换

zw版[转发·台湾nvp系列Delphi例程]Delphi 使用 HALCON库件COM控件数据格式转换 Delphi 使用 HALCON库件COM控件数据格式转换,与IHObjectX接口有关 var rg0,rg1 : HRegionX;begin rg0 := CoHRegionX.Create; rg0.GenRectangle1(10,10,150,150); rg1 := CoHRegionX.Create; rg1.GenRectangle1(200,200,250,250); r

Silverlight中获取控件中子控件

如题:,直接来看代码: /// <summary> /// 查找并返回第一个 相同 name的子元素 /// </summary> /// <typeparam name="T">需要查找 的子控件 类型</typeparam> /// <param name="obj">需要查找其下面子控件的 控件 类型</param> /// <param name="childName&q