目录

一、 杨老师是个热情的人

二、 开始喽

三、 还需要些解释什么吗

四、 OK了吗

五、最终代码

一、杨老师是个热情的人

我们的项目开源有一段时间了,我一直以为我有一个很不错的胸怀把自己花了很多精力做出来的项目贡献出来了,我以为同学们会很开心,会像“一个饥饿的人看到面包”一样的扑到了我的项目代码上面快乐的研究起来,但是事实上我们的群里面却异常的冷清。我想应该是大家都还在研究代码来不及说话或者是不爱说话吧,直到今天杨老师给我打电话,说了一些情况,似乎是说大家还不太懂数据库等等,我才知道是我错了,生活往往是这样:你没想到的太多了……。杨老师是我最佩服的老师,他是一个充满热情的人。电话里面一直邀请我再和同学们去讲个课帮助大家看懂这些代码,可以让这个项目学习继续下去,这样也不枉费当初的一番好的初衷。但是我想,我可能不是一个会讲课的人,再去讲课也太正规了一点,我跟杨老师商量一个折中的办法:利用大家已经加入的QQ群,我尝试着写几个教程,跟大家一起从零开始介绍一些我的项目里面可能会用到的知识,但愿这样可以帮到大家。

我不确定我能不能坚持下去(可能这也是我不敢去和同学们讲课的原因之一吧。人有时是没办法太了解自己的),但是不管怎么样,大家一起努力,今天就从第一个开始吧。

在Microsoft Visual Studio 2012(以下简称VS)里面,有许多的控件可供日常窗体设计的需求。但是很多时候,我们需要的控件却找不到,比如:画一条直线。当然,我们也不能太苛求Microsoft,毕竟他们已经做得够好。更何况有时我们还会忘记支付他们软件使用费……。

在多年前(你们可能还不是小学生的时候,估计应该是人了)我有一个项目做得很好,我以为业主会很满意,事实上他们也真的很满意。只有在最后我要交货,他们要付款(这个是我当时最看重的一个环节)之前,老板很轻描淡写的说了一句,那么复杂的界面(真的很复杂吗,如下图。但是伦家是付款的资本方,顾客就是上帝呀,以后你们会知道的),你就不能画几条线分割一下吗?

wps6AE3.tmp

我想也对,可是回去后我在工具栏里面却怎么也找不到可以画一条线的控件,多简单的一件事情,Microsoft竟然不能帮我,我曾经无比崇拜的Bill,你到哪里去了……。后来我用Ps做了二条线解决了问题(那时候我还不会自定义控件,错怪了Bill),但是这个事情让我在很长的一段时间里面感到惭愧,认为自己不是一个合格的程序员。当然,今天我做到了,就像下面这个图所示:

wps6AF3.tmp

二、开始喽

好吧,言归正传。要做这件事(做一个可以画一条线的控件),当然第一个事情是打开VS(如果你有安装的话),新建一个项目(这个应该不难):

wps6AF4.tmp

第二步:在新建的这个项目里面,右单击”LineControlsTest”这个项目,在出现的菜单里面找到“添加”,然后选择“用户控件”。添加一个用户自定义控件,我发现我还是不会讲,用图片来说明吧(有图有真相)

wps6B05.tmp

wps6B15.tmp

第三步:做完上面这几个步骤之后,你们应该可以看到这个界面:

wps6B16.tmp在这个界面下按下“F7”这个按键(好吧,我开始讨厌自己这么啰嗦了),进入代码编辑界面。如下图:

wps6B17.tmp

把控件的继承类从原来的“UserControl”改为“Control”,这样做的目的是不要VS自动帮我们做的界面,这个界面我们要自己做。但是这样做会出现错误,按一下F5运行项目时会报错,如下:

wps6B18.tmp

解决办法是把“ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;”这句代码删除掉就可以了,因为这句代码是针对“UserControl”类的,我们现在从Control类继承,不需要这句代码。(还有一种做法不会报错,就是新建一个类,而不是新建一个用户控件,然后让这个类继承System.Windows.Forms.Control类,但是这种做法要在Using里面增加几个引用,有兴趣的童鞋可以试试,效果是一样的)

第四步:开始写代码了,这个是最重要的,也是本次这个项目的唯一需要我们动脑的一个环节。好吧,下面剩下的事情变得简单了,只需要把下面的代码Copy到类里面就完成了。建议你们亲自动手在VS里面打入这些代码,会有新发现哦。

protected override void OnPaint(PaintEventArgs e)

        {

            Pen pen = new Pen(SystemColors.ControlText);//定义一个画笔           

            this.Height = 18;//设置本控件的高度

            pen.Width = 1;//设置画笔的宽度

            e.Graphics.DrawLine(pen, 0, this.Height - pen.Width, this.Width - 1, this.Height - pen.Width); //在控件的底部画一条线

            Brush br =new SolidBrush(SystemColors.MenuHighlight); //定义一个刷子

            SizeF sizeF = e.Graphics.MeasureString(Text, this.Font); //根据控件设置的字体和控件的文字(Text)属性,获取控件文字在本控件中的大小

            e.Graphics.DrawString(Text, this.Font, br, (this.Width-sizeF.Width)/2,1);//根据上面这行代码获取的文件大小,在控件的中间写上控件的文字

            br.Dispose();//笔刷用完了就要清楚掉,避免占用内存

            base.OnPaint(e);//这句不能少,这个是调用基础类(Control)的OnPaint代码

        }

第五步:解决方案里面右单击项目名称,在出现的菜单里面点击“生成”。

wps6B29.tmp

第六步:双击解决方案里面的Form1,在出现的Form1窗体界面上面,可以看到我们设计的控件出现在工具栏里面了,拖动我们自己的控件(PxyLine),到窗体上面,可以看到我们做得控件实现了我们开始设计的功能。

wps6B2A.tmp

太简单了,但不是所有的项目都这么简单的。

三、还需要些解释什么吗

我想我已经讲得很清楚了,以我这样的文字功底,这个已经是我能说的最清楚的事情了,更何况我的代码里面每一句都有注释(这个在平常是不可能的)。如果一定还要讲的,就是这个override和OnPaint,不过也许你们可以找一下帮助文档。微软官方是这样解释override的:要扩展或修改继承的方法、属性、索引器或事件的抽象实现或虚实现,必须使用 override 修饰符。至于OnPaint微软讲得更简单:引发 Paint 事件。当然,通过这个项目,也许你会有所体会。

四、OK了吗

如果只是满足基本功能,这样是OK了,但是如果是我公司的人交付给我这样的代码,我是远不能满意的(我不算是苛刻的老板,但是这样的代码真的太烂了,居然是我写的:-()。

比如:文字为什么是居中显示的,如果我需要靠左显示呢?能不能做一个属性,我设置一下就可以改变显示方式呢?

比如:控件高度怎么写死在里面呀,如果我需要修改控件高度,是不是一定要到源代码里面去修改并且重新编译呀?这也太次了吧

比如:如果我要求画的线粗一些呢?

比如:文件的颜色和线条的颜色呢?

还有,再比如……,你们也想象一下 :)

好吧再加点料,针对上面说的几个比如,我再做一个补充,其他几个交给你们去实现。我希望你们实现完了也和我一样,把代码分享出来让大家也乐一乐(也许我可以对你们的代码再提一下修改意见)。

第一步:为了把控件的线条粗细做成可以调整,我们定义一个私有变量penwidth,并且默认值为1,代码如下:

private int penwidth = 1;

第二步:封装:选中penwidth(鼠标放置在penwidth这几个字母里面),右单击出现如下菜单(最简单的方法是在此时按下Ctrl+R,再按下E):

wps6B3B.tmp

VS会自动生成如下代码:

public int Penwidth

        {

get { return penwidth; }

set { penwidth = value; }

        }     

再在上做一些注释(这个是我比较骄傲的一个好习惯,不知道你们有没有),成了这样的

///

/// 设置画笔宽度

///

public int Penwidth

        {

get { return penwidth; }

set { penwidth = value; }

        }     

对了,还要加上这个[Browsable(true), Category("自定义"), Description("设置控件画笔宽度")],成了这样的:

///

/// 设置画笔宽度

///

[Browsable(true), Category("自定义"), Description("设置控件画笔宽度")]

public int Penwidth

        {

get { return penwidth; }

set { penwidth = value; }

        }    

好吧,我已经看到有同学举手了。

“[Browsable(true), Category("自定义"), Description("设置控件画笔宽度")]”是什么,看一下这个:

wps6B3C.tmp

上面这段代码添加之后,我们点击Form1上面的pxyLine控件,在该控件的属性窗体里面就会出现一个“自定义”的目录,里面就是我们刚才定义的控件属性“penWidth”,在这里,用户可以自由设置控件的Penwidth属性。其他的详细解释找百度。

第三步,修改Onpaint代码。

把原来的这句代码:pen.Width = 1;改成:pen.Width = Penwidth;

好了,这回是真正的OK了,“完整”代码贴在下面。其它几个“比如”就靠你们了。


五、最终代码

public PxyLine()

        {

            InitializeComponent();

        }

        private int penwidth = 1;

        ///

        /// 设置画笔宽度

        ///

        [Browsable(true), Category("自定义"), Description("设置控件画笔宽度")]

        public int Penwidth

        {

            get { return penwidth; }

            set { penwidth = value; }

        }     

        protected override void OnPaint(PaintEventArgs e)

        {

            Pen pen = new Pen(SystemColors.ControlText);//定义一个画笔           

            this.Height = 18;//设置本控件的高度

            //pen.Width = 1;//设置画笔的宽度

            pen.Width = Penwidth;//使用控件的属性,这样使用者可以通过修改属性来控制这个控件的行为,而不是修改源代码后再编译

            e.Graphics.DrawLine(pen, 0, this.Height - pen.Width, this.Width - 1, this.Height - pen.Width); //在控件的底部画一条线

            Brush br =new SolidBrush(SystemColors.MenuHighlight); //定义一个刷子

            SizeF sizeF = e.Graphics.MeasureString(Text, this.Font); //根据控件设置的字体和控件的文字(Text)属性,获取控件文字在本控件中的大小

            e.Graphics.DrawString(Text, this.Font, br, (this.Width-sizeF.Width)/2,1);//根据上面这行代码获取的文件大小,在控件的中间写上控件的文字

            br.Dispose();//笔刷用完了就要清除掉,避免占用内存

            base.OnPaint(e);//这句不能少,这个是调用基础类(Control)的OnPaint代码

        }

源代码下载