【原创】自己动手写控件----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)这句,如果不加上这句代码,控件上的文本是不会显示的。具体看代码:
public partial class LabelEx : Label
{ #region PARAMS
private const int WIDTH = 0x0226;//
private const int HEIGHT = 0x002F;//
#endregion #region CONSTRUCTOR
public LabelEx()
: base()
{
this.Font = new Font(new FontFamily("微软雅黑"), 10.0f);
InitializeComponent();
this.AutoSize = false;
//设置内边距
this.Padding = new Padding();
//设置外边距
this.Margin = new Padding();
this.Width = WIDTH;
this.Height = HEIGHT;
this.TextChanged += LabelEx_TextChanged;
}
#endregion #region Property
public Color NormalBorderColor { get; set; }
public Color HighLightBorderColor { get; set; }
#endregion #region EVENTS
private void LabelEx_TextChanged(object sender, EventArgs e)
{
//文字变化后改变一下当前的大小
System.Drawing.Size ps = GetPreferredSize(this.Size);
if (ps.Height < HEIGHT)
{
ps.Height = HEIGHT;
}
this.Size = new System.Drawing.Size(this.Width, ps.Height);
}
#endregion #region OVERRIDE
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);//解决了文字不显示的问题
Graphics g = e.Graphics;
int x = this.Width;
int y = this.Height;
Point leftTop = new Point(, );
Point rightTop = new Point(x - , );
Point leftBottom = new Point(, y - );
Point rightBottom = new Point(x - , y - );
//绘制四个边缘,避免被背景色填充
g.DrawLine(new Pen(Color.White), leftTop, rightTop);
g.DrawLine(new Pen(Color.White), leftBottom, rightBottom);
g.DrawLine(new Pen(Color.White), leftTop, leftBottom);
g.DrawLine(new Pen(Color.White), rightTop, rightBottom);
//画上边缘
for (int i = ; i < x - ; i += )
{
g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(i, , , ));
} //画下边缘
for (int m = ; m < x - ; m += )
{
g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(m, y - , , ));
} //画左边缘
for (int i = ; i < y - ; i += )
{
g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(, i, , ));
} //画右边缘
for (int i = ; i < y - ; i += )
{
g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(x - , i, , ));
}
} public override bool AutoSize
{
get
{
return false;
}
}
#endregion
}
2、LabelExS控件
上面的LabelEx控件只是简单地处理了一下Label控件,而LabelExS控件继承自LabelEx控件,并添加了一些简单的状态控制。这里的重绘方法,和上面的控件大概是一致的,只是稍微做出了修改。不同状态下的绘制代码:
/// <summary>
/// 激活态
/// </summary>
/// <param name="g"></param>
private void DrawHighLightBorder(Graphics g)
{
int x = this.Width;
int y = this.Height;
//画上边缘
for (int i = ; i < x - ; i++)
{
g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(i, , , ));
} //画下边缘
for (int m = ; m < x - ; m++)
{
g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(m, y - , , ));
} //画左边缘
for (int i = ; i < y - ; i++)
{
g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(, i, , ));
} //画右边缘
for (int i = ; i < y - ; i++)
{
g.FillRectangle(new SolidBrush(base.HighLightBorderColor), new Rectangle(x - , i, , ));
}
} /// <summary>
/// 常规态
/// </summary>
/// <param name="g"></param>
private void DrawNormalBorder(Graphics g)
{
int x = this.Width;
int y = this.Height;
//画上边缘
for (int i = ; i < x - ; i++)
{
g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(i, , , ));
} //画下边缘
for (int m = ; m < x - ; m++)
{
g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(m, y - , , ));
} //画左边缘
for (int i = ; i < y - ; i++)
{
g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(, i, , ));
} //画右边缘
for (int i = ; i < y - ; i++)
{
g.FillRectangle(new SolidBrush(base.NormalBorderColor), new Rectangle(x - , i, , ));
}
}
根据状态进行绘制:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
switch (State)
{
case :
DrawNormalBorder(g);
break;
case :
DrawHighLightBorder(g);
break;
default:
DrawNormalBorder(g);
break;
}
}
鼠标事件控制,进入或离开的时候,强制区域无效并更新状态,这样OnPaint函数会根据状态进行重新绘制:
/// <summary>
/// 鼠标进入事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LabelExSolidBorder_MouseEnter(object sender, EventArgs e)
{
State = ;
base.ForeColor = Color.Orange;
base.HighLightBorderColor = Color.Orange;// ★可配置
this.Invalidate();
} /// <summary>
/// 鼠标离开事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LabelExSolidBorder_MouseLeave(object sender, EventArgs e)
{
State = ;
base.ForeColor = Color.Brown;
base.NormalBorderColor = Color.White;// ★可配置
this.Invalidate();
}
为控件绑定Click事件,执行自定义操作:
/// <summary>
/// 观察者模式,鼠标单击后,执行自定义操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LabelExSolidBorder_Click(object sender, EventArgs e)
{
MainForm mf = (MainForm)this.TopLevelControl;
ShowNote += mf.SetText;//绑定事件
SetSelectedNode += mf.SetSelectedNode; if (ShowNote != null)
{
ShowNote();//自定义操作
}
}
3、 RectangleLabel控件
这个控件和第一个控件差不太多,主要的区别就是外围路径的绘制,由矩形变成了圆角矩形,做成了类似标签的效果,在这里仅仅贴出绘制的代码,大概思路就是绘制两条线段,并在合适的位置再绘制两个半圆形并组合在一起就可以了:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Rectangle rect = new Rectangle(new Point(, ), new Size(, ));
Rectangle rect2 = new Rectangle(new Point(, ), new Size(, ));
Rectangle rect3 = new Rectangle(new Point(, ), new Size(, ));
Point[] ps = { new Point(, ), new Point(, ) };
Point[] pq = { new Point(, ), new Point(, ) };
float start = 90f;
float end = 180f;
float start2 = 270f;
float end2 = 180f;
Pen p = new Pen(this.BorderColor, this.BorderWidth);
SolidBrush sl = new SolidBrush(this.InnerColor);
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawArc(p, rect, start, end);
g.DrawArc(p, rect3, start2, end2);
g.DrawLine(p, ps[], ps[]);
g.DrawLine(p, pq[], pq[]);
}
}
4、LabelWithCheck控件
相对来说,这个控件由Label和ChecBox组成,是这些控件中是最复杂的,因为它本身包含了事件信息和对应的逻辑,就不仅仅是绘制那么简单了。该控件直接继承自UserControl,灵活性也比较高。
添加了若干属性:
[Category("XHB.Controls")]
[Browsable(true)]
[Description("设置或获取LabelText宽度")]
public int LabelWidth
{
get
{
return labelWidth;
}
set
{
labelWidth = value + ck.Width + ;
this.Refresh();
}
}
[Category("XHB.Controls")]
[Browsable(true)]
[Description("指示LabelText是否被选中")]
[DefaultValue(false)]
public bool LabelChecked
{
get
{
return ck.Checked;
}
set
{
ck.Checked = value;
}
}
代码中的属性上方包含一些控件特性,用于在设计时进行浏览或操作:
Category : 指示控件的分组信息;
Browsable : 指示控件是否可浏览;
Description : 控件的描述信息;
DefaultValue : 控件的默认值;
自定义事件,LabelWithCheck控件的自定义事件继承自EventArgs类:
/// <summary>
/// 事件参数包含的信息
/// </summary>
public class LabelWithCheckEventArgs :EventArgs
{
private string _LabelText;
private Guid _Id;
public LabelWithCheckEventArgs(Guid id,string labelText)
{
this._LabelText = labelText;
this._Id = id;
}
public string LabelText
{
get
{
return _LabelText;
}
set
{
_LabelText = value;
}
}
public Guid Id
{
get
{
return _Id;
}
set
{
_Id = value;
}
}
}
默认情况下,单击控件的非CheckBox所在区域,CheckBox不会变化,下面实现单击整个控件都会改变CheckBox的选中状态,只要加一丢丢代码就好:
/// <summary>
/// 单击Label也可以触发CheckBox的选中事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lb_Click(object sender, EventArgs e)
{
ck.Checked = ck.Checked ? false : true;
}
四、总结
自定义控件的开发,其实是有规律可循的,无非就是绘制、状态、逻辑的有序组合,无论是简单的控件还是精美复杂的控件,开发的思路都是一样的,掌握了方法,万变不离其宗。这里列出的控件都是入门级的,是适合初学者的,如果文中有表述失误的地方,请提出来,不仅提高自己,更能避免引人误入歧途。具体的代码还是参考我的GitHub上的小工具。注:本文同步发布到我的简书。
作者:悠扬的牧笛
博客地址:http://www.cnblogs.com/xhb-bky-blog/p/5523611.html
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。
【原创】自己动手写控件----XSmartNote控件的更多相关文章
- 【原创】自己动手写工具----XSmartNote [Beta 3.0]
一.前面的话 在动笔之前,一直很纠结到底要不要继续完成这个工具,因为上次给它码代码还是一年多之前的事情,参考自己动手写工具----XSmartNote [Beta 2.0],这篇博文里,很多园友提出了 ...
- 【原创】自己动手写工具----XSmartNote [Beta 2.0]
一.前面的话 在上一篇自己动手写工具----XSmartNote中,我简单介绍了这个小玩意儿的大致界面和要实现的功能,看了一下园子里的评论,评价褒贬不一,有人说“现在那么多云笔记的工具”,“极简版ev ...
- appium+python:自己写的一个滑动控件的方式
#调用方式roll_ele("ID","ele_id","7","up",3)#将控件分为7格,从底部倒数第二格向上滑动 ...
- UWP入门(一) -- 先写几个简单控件简单熟悉下(别看这个)
原文:UWP入门(一) -- 先写几个简单控件简单熟悉下(别看这个) 1. MainPage.xmal <Grid Background="{ThemeResource Applica ...
- 【原创】自己动手写工具----签到器[Beta 2.0]
一.前面的话 上一篇中基本实现了简单的签到任务,但是不够灵活.在上一篇自己动手写工具----签到器的结尾中,我设想了几个新增功能来提高工具的灵活程度,下面把新增功能点列出来看看: (1)新增其他的进程 ...
- ASP.NET关于Login控件使用,LoginView 控件,CreateUserWizard 控件
原文:ASP.NET关于Login控件使用,LoginView 控件,CreateUserWizard 控件 Login控件它是属于Membership服务的一部分,必须配置Membership提供程 ...
- 背水一战 Windows 10 (77) - 控件(控件基类): ContentControl, UserControl, Page
[源码下载] 背水一战 Windows 10 (77) - 控件(控件基类): ContentControl, UserControl, Page 作者:webabcd 介绍背水一战 Windows ...
- 在DELPHI中动态创建控件以及控件的事件
在DELPHI中我们经常要动态的创建控件以及控件的事件.例如,我们可能想根据程序需要动态的创建一些Tshape组件来创建某个图形,并使得在鼠标移动上去之后可以完成某些操作.这一般需要需要三步: 生成一 ...
- WPF进阶技巧和实战03-控件(1-控件及内容控件)
所有控件都继承自System.Windows.Controls.Control类,这个类添加一些基本结构: 设置控件内容对齐方式 (HorizontalContentAlignment,Vertica ...
随机推荐
- HTML5应用程序缓存Application Cache
什么是Application Cache HTML5引入了应用程序缓存技术,意味着web应用可进行缓存,并在没有网络的情况下使用,通过创建cache manifest文件,可以轻松的创建离线应用. A ...
- Web报表工具FineReport填报界面键盘操作
对于一张填报数据较多的报表,需要用户频繁地操作鼠标.而FineReport填报界面除去按钮类型的控件,其余可以完全使用键盘而不需要用鼠标操作,对于用户而言,这将极大的节省信息录入的时间. 这里我们对填 ...
- android版扫扫图书应用源码
书,是人类的灵魂. 扫扫图书是一个让你懂得如何去选择一本书的APP. 这里你可以扫描条形码查询图书, 你也可以关键字搜索,遇到合乎你口味的书, 你还可以看看别人的读书笔记,不同角度去体会. 注:请允许 ...
- SVN为什么比Git更好
首先我表明一个根本的立场,我个人更喜欢用Git,但是,这仅仅是一个个人偏好.当我们需要将一种技术方案带给整个团队的时候,并不是由我们的个人偏好作为主要决定因素,而应该充分去权衡利弊,选择对团队,对公司 ...
- vs2010中如何设置Visual Assist方便地使用现成的代码编辑器风格
风格setting可以在下面网站上获取: http://studiostyl.es/ 在VS2010+VA直接使用会有2个显著的问题: 1,有些符号颜色太深,与黑色背景几乎融为一体: 2,光标落入大小 ...
- 【转】你所不知道的HTML <head/> 头标签
HTML的头部内容特别多,有针对SEO的头部信息,也有针对移动设备的头部信息.而且各个浏览器内核以及各个国内浏览器厂商都有些自己的标签元素,有很多差异性.移动端的工作已经越来越成为前端工作的重要内容, ...
- ASP.NET MVC的客户端验证:jQuery验证在Model验证中的实现
在简单了解了Unobtrusive JavaScript形式的验证在jQuery中的编程方式之后,我们来介绍ASP.NET MVC是如何利用它实现客户端验证的.服务端验证最终实现在相应的ModelVa ...
- Long类型的数据转换时间格式方法
function getDate(date) { //得到日期对象 var d=new Date(date); //得到年月日 var year =d.getFullYear(); ); var da ...
- JSON学习笔记一 —— 一些与移动端交互产生JSON数据的方法
/** * 测试的返回JSon方法,正式的不会用 * @author MrHandler * @param reqCode * @param joinStr * ...
- C#简单的对象交互
在对象的世界里,一切皆为对象;对象与对象相互独立,互不干涉,但在一定外力的作用下对象开始共同努力 对象交互的实例 电视机大家都有吧,依照万物皆对象的思维模式来看,电视机可以是一个类,然后电视机有一些基 ...