C#开发案例精粹

978-7-115-63274-6
作者: 明日科技
译者:
编辑: 赵祥妮
分类: C#

图书目录:

详情

本书紧密围绕软件开发人员在编程中遇到的实际问题和开发中应该掌握的技术,以实例的形式,全面介绍应用C#进行软件开发的技术和技巧。本书共16章,包括窗体与界面设计,控件应用,图形技术,多媒体技术,文件系统,操作系统与Windows相关应用,数据库技术,SQL查询相关技术,LINQ查询技术,打印技术,图表技术,网络开发技术,加密、安全与软件注册,C#操作硬件,人工智能应用,游戏开发。 本书所有实例的源代码都经过精心调试,在Windows 7、Windows 10等操作系统下测试通过,均能够正常运行。

图书摘要

版权信息

书名:C# 开发案例精粹

ISBN:978-7-115-63274-6

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

版  权

著    明日科技

责任编辑 赵祥妮

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

内容提要

本书紧密围绕软件开发人员在编程中遇到的实际问题和开发中应该掌握的技术,以实例的形式,全面介绍应用C#进行软件开发的技术和技巧。本书共16章,包括窗体与界面设计,控件应用,图形技术,多媒体技术,文件系统,操作系统与Windows相关应用,数据库技术,SQL查询相关技术,LINQ查询技术,打印技术,图表技术,网络开发技术,加密、安全与软件注册,C#操作硬件,人工智能应用,游戏开发。

本书所有实例的源代码都经过精心调试,在Windows 7、Windows 10等操作系统下测试通过,均能够正常运行。

本书适合软件开发人员阅读,也适合大、中专院校师生阅读。

前  言

软件开发从来不是一件容易的事,即使是非常有经验的开发人员,也经常会遇到一些技术难题。要成为一名合格的程序员,必须不断吸取和借鉴其他开发人员的成功经验。阅读别人设计的程序,从中提取编程思想的精华,这是学习程序设计最好的方法之一。

作者有幸参加过十几个项目的开发工作,对编程有深刻的体会。编程是一项复杂的创造性工作,它需要开发人员掌握各方面的知识并积累丰富的开发经验。程序开发中的某个问题可能会占用团队几天甚至十几天的时间,但是如果开发人员遇到过类似的问题,也许几分钟就可以解决。这就是编程经验的重要性,也是许多企业用人时选择有软件开发经验者的主要原因。这也是本书成书的主要原因之一!

本书内容

本书精选146个典型实例,所选实例覆盖C#软件开发中的热点问题和关键问题。全书按实际应用进行分类,可以使读者在短时间内掌握更多的实用技术,快速提高软件开发水平。所选实例均来源于实际项目,有的实例来源于作者的开发实践,有的实例来源于公司的开发项目,还有的实例来源于明日科技读者群的提问。通过对这些实例进行详细分析和讲解,可以让读者迅速掌握程序设计的开发技巧,短时间内提高软件开发的综合水平。

在实例讲解上,全书采用统一的编排方式,每个实例都包括“实例说明”“技术要点”“实现过程”“举一反三”这4个部分。在“实例说明”中,以图文结合的方式给出实例的功能说明及运行结果。在“技术要点”中给出实例的重点、难点技术和相关编程技巧。在“实现过程”中介绍该实例的设计过程和主要代码。在“举一反三”中给出相关实例的扩展应用。

本书特色

所有实例都以解决读者在编程中遇到的实际问题和开发中应该掌握的技术为中心,每个实例都可以独立解决某一方面的问题:有的可以解决工作中的难题,有的可以提高工作效率,还有的可以提升工作价值。

所选实例具有极强的扩展性,能够给读者以启发,使读者能举一反三进行相关操作,达到非常实用的效果。

所选实例具有广泛的代表性。

本书的约定

书中涉及数据库的实例,在实例对应文件夹中均提供了用于保存数据库文件的文件夹。

因篇幅限制,本书实例只给出了关键代码,其他代码参见源代码。

本书的源代码和案例拓展获取方式:关注微信公众号“明日IT部落”,回复“C#开发案例精粹”,根据引导即可下载。

本书的服务

本书由明日科技有限公司组织编写,参与编写的人员有王小科、王国辉、张鑫、周佳星、赛奎春、高春艳、杨丽等。由于C#涉及范围比较广泛,书中疏漏之处在所难免,敬请广大读者批评指正。

为便于读者和本书作者沟通,我们将通过明日科技有限公司服务网站全面为读者提供网上服务和支持。读者在使用本书的过程中遇到的问题,我们承诺在5个工作日内给您提供及时答复。

服务网站:www.mingrisoft.com。

企业客服QQ:4006751066。

服务QQ群:162973740。

企业客服电话:0431-84978981/84978982。

本书编写组

2023年10月

资源与支持

提交勘误

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区(https://www.epubit.com/),按书名搜索,进入本书页面,单击“发表勘误”,输入错误信息,单击“提交勘误”按钮即可(见下图)。本书的作者和编辑会对您提交的错误信息进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

与我们联系

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们。

如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

关于异步社区和异步图书

异步社区”(www.epubit.com)是由人民邮电出版社创办的IT专业图书社区,于2015年8月上线运营,致力于优质内容的出版和分享,为读者提供高品质的学习内容,为作译者提供专业的出版服务,实现作者与读者在线交流互动,以及传统出版与数字出版的融合发展。

异步图书”是异步社区策划出版的精品IT图书的品牌,依托于人民邮电出版社在计算机图书领域40余年的发展与积淀。异步图书面向IT行业以及各行业使用IT技术的用户。

第1章 窗体与界面设计

实例001 带历史信息的菜单

实例说明

在开发图纸管理软件时,要求在菜单上记录用户最近打开的档案或图纸文件,以方便下次使用。如图1.1所示,单击“文件”菜单下的“打开”菜单项,可打开需要查阅的图纸文件。下次运行该软件时,上次打开的图纸文件的文件名已经被记录到“文件”菜单的历史菜单项中,选择该菜单项,即可打开相应的图纸文件。

图1.1 带历史信息的菜单

技术要点

将在菜单中最近打开的文件的路径保存到事先建立的INI文件中,软件启动时读取INI文件中的数据建立数组菜单,即可实现显示带历史信息的菜单的功能。

意:要建立一个带历史信息的菜单,必须首先添加一个MenuStrip控件,并将主窗体的IsMdiContainer属性设为true。

实现过程

01 新建一个项目,将其命名为MenuHistory,默认窗体为Form1。

02 向Form1窗体添加MenuStrip控件,同时向Form1窗体添加OpenFileDialog控件。创建一个“文件”主菜单,在其下面创建“打开”“关闭所有”“退出”等菜单项。

03 主要代码。

将打开的文件的路径写入INI文件的实现代码如下:

01   private void 打开ToolStripMenuItem_Click(object sender, EventArgs e)
02   {
03       openFileDialog1.FileName = "";          //设定打开文件对话框的初始内容为空
04       this.openFileDialog1.ShowDialog();      //显示打开文件对话框
05       //定义一个以一种特定编码向流中写入数据的对象
06       StreamWriter s = new StreamWriter(address + "\\Menu.ini", true); 
07       s.WriteLine(openFileDialog1.FileName);  //写入INI文件
08       s.Flush();           //清理当前编写器的所有缓冲区,并使所有缓冲数据写入基础流
09       s.Close();                              //关闭当前的StreamWriter对象和基础流
10       ShowWindows(openFileDialog1.FileName);  //调用自定义方法ShowWindows
11   }

读取INI文件并将信息加入菜单的实现代码如下:

01   private void Form1_Load(object sender, EventArgs e)
02   {
03       //定义一个以一种特定编码从字节流中读取字符的对象
04       StreamReader sr = new StreamReader(address + "\\Menu.ini"); 
05       //定义一个int型变量i并为其赋值
06       int i = this.文件ToolStripMenuItem.DropDownItems.Count - 2; 
07       while (sr.Peek() >= 0)                  //读取INI文件
08       {
09           //定义一个ToolStripMenuItem对象
10           ToolStripMenuItem menuitem = new ToolStripMenuItem(sr.ReadLine());  
11           //向菜单中添加内容
12           this.文件ToolStripMenuItem.DropDownItems.Insert(i, menuitem); 
13           i++;                         //int型变量i递增
14           //为菜单中的菜单项生成处理程序
15           menuitem.Click += new EventHandler(menuitem_Click); 
16               }
17               sr.Close();                    //关闭当前的StreamReader对象和基础流
18           }

举一反三

根据本实例,读者可以开发以下程序。

记录用户操作菜单日志的程序。在用户单击菜单项时,把用户、菜单命令和菜单对应功能写入保存菜单日志的INI文件。如果需要查看日志,只需打开INI文件。

通过数据库保存菜单历史信息的程序。

实例002 带下拉菜单的工具栏

实例说明

本实例实现一个带下拉菜单的工具栏,效果如图1.2所示。

图1.2 带下拉菜单的工具栏

技术要点

带下拉菜单的工具栏在其他开发环境中实现比较复杂,但.NET已经提供了这个功能,只需将工具栏按钮的类型设置为DropDownButton即可。

实现过程

01 新建一个项目,将其命名为DropDownTool,默认窗体为Form1。

02 向Form1窗体中添加ToolStrip控件用来设计工具栏,并为工具栏添加相应的按钮,在按钮的下拉选项中选择DropDownButton类型。

03 为工具栏DropDownButton类型的按钮设置相应的下拉菜单,就可以轻松实现带下拉菜单的工具栏。

举一反三

根据本实例,读者可以实现以下功能。

制作一个带快捷菜单的工具栏。

制作一个带复选框的工具栏。

实例003 在状态栏中加入图标

实例说明

本实例实现在状态栏中加入图标,这样程序的主界面将更有特色。实例运行结果如图1.3所示。

技术要点

在状态栏中加入图标在.NET中非常容易实现,只需将对应状态栏面板的Image属性设置为要显示的图片即可。

图1.3 在状态栏中加入图标

实现过程

01 新建一个项目,将其命名为ImageProgressBar,默认窗体为Form1。

02 向Form1窗体中添加StatusStrip控件用来设计状态栏,并为状态栏添加相应的按钮,设置按钮的Image属性为要显示的图片。

举一反三

根据本实例,读者可以实现以下功能。

将其他控件放置在状态栏中,例如进度条。

将其他控件放置在状态栏中,例如复选框。

实例004 带导航菜单的主界面

实例说明

在窗体中,菜单栏是不可缺少的重要组成部分。本实例使用其他控件来制作一个模拟菜单栏。运行本实例后,单击窗体上面的按钮,将会在按钮的下面显示一个下拉列表,如图1.4所示。

图1.4 带导航菜单的主界面

技术要点

该实例主要使用Button控件和ListView控件制作导航菜单界面。在对ListView控件添加菜单信息时,必须在前面写入添加语句,例如ListView.Items.Add,否则添加的菜单信息将替换前一条信息。单击相应的按钮时,应首先对ListView控件进行清空,否则在ListView控件中将显示上一次的菜单信息。

实现过程

01 新建一个项目,将其命名为Navigation,默认窗体为Form1。

02 在Form1窗体上添加MenuStrip控件,用来设计菜单栏;添加ToolStrip控件,用来设计工具栏;添加SplitContainer控件、ImageList控件、3个Button控件和ListView控件,用来设计左侧的导航栏。

03 分别为MenuStrip控件、ToolStrip控件添加子项,将3个Button控件和ListView控件加入SplitContainer1.panel的左侧部分。

04 主要代码。

加载窗体时,设置左侧导航栏内容的实现代码:

01   private void Form1_Load(object sender, EventArgs e)
02   {
03       listView1.Clear();//清空listView1中的原有内容
04       listView1.LargeImageList = imageList1;//设置当前项以大图标形式显示时用到的图像
05       //向listView1中添加项"设置上下班时间"
06       listView1.Items.Add("设置上下班时间", "设置上下班时间", 0);   
07       //向listView1中添加项"是否启用短信提醒"
08       listView1.Items.Add("是否启用短信提醒", "是否启用短信提醒", 1); 
09       listView1.Items.Add("设置密码", "设置密码", 2);//向listView1中添加项"设置密码"
10   }

添加“打开”按钮的ListView控件显示内容的实现代码如下:

01   private void button2_Click_1(object sender, EventArgs e)
02   {
03       listView1.Dock = DockStyle.None; //设置listView1的绑定属性为未绑定
04       button2.Dock = DockStyle.Top; //设置button2的绑定属性为上端绑定
05       button1.SendToBack();//将button1控件设置为最底层显示
06       button1.Dock = DockStyle.Top; //设置button1的绑定属性为上端绑定
07       button3.Dock = DockStyle.Bottom; //设置button3的绑定属性为底端绑定
08       listView1.Dock = DockStyle.Bottom;//设置listView1的绑定属性为底端绑定
09       listView1.Clear();//清空listView1中的原有内容
10       //向listView1中添加项"近期工作记录"
11       listView1.Items.Add("近期工作记录", "近期工作记录", 3); 
12       //向listView1中添加项"近期工作计划"
13       listView1.Items.Add("近期工作计划", "近期工作计划", 4);   
14   }

举一反三

根据本实例,读者可以实现以下功能。

制作一个系统菜单。

制作大型系统的导航界面。

实例005 隐藏式窗体

实例说明

一般情况下,当一个窗体被打开后会处于一个默认位置,如果此时进行其他操作,就必须关闭、移动或者最小化当前窗体。当该窗体再次被应用时,又要对其进行上述的一系列操作,这样就显得有些麻烦。本实例可以解决这个问题。实例运行结果如图1.5和图1.6所示。

图1.5 隐藏式窗体之登录结果

图1.6 隐藏式窗体之登录成功结果

技术要点

本实例主要用到Windows的API函数,它们是 WindowFromPoint函数、GetParent函数和GetSystemMetrics函数。

(1)WindowFromPoint函数。该函数用来获取当前鼠标指针下的控件。其语法如下:

[DllImport("user32.dll")]
public static extern int WindowFromPoint(int xPoint,int yPoint);

参数说明如下。

xPoint:表示当前状态下鼠标指针的x坐标。

yPoint:表示当前状态下鼠标指针的y坐标。

返回值:当前鼠标指针下控件的句柄。

(2)GetParent函数。该函数用来获取指定窗体的父窗体。其语法如下:

[DllImport("user32.dll",ExactSpelling = true,CharSet = CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);

参数说明如下。

hWnd:表示当前窗体的句柄。

返回值:当前窗体的父级句柄。

(3)GetSystemMetrics函数。该函数用来获取当前工作区的大小。其语法如下:

[DllImport("user32.dll",EntryPoint = "GetSystemMetrics")]
private static extern int GetSystemMetrics(int mVal);

参数说明如下。

mVal:表示将进行的操作类型。

返回值:取决于参数值。

注意:在调用Windows的API函数时必须引用命名空间System.Runtime.InteropServices,后文遇到这种情况将不再提示。

实现过程

01 新建一个项目,将其命名为HideToolBar,默认窗体为HideToolBar。

02 在HideToolBar窗体上添加1个ProgressBar控件、2个Label控件和3个Timer组件。设置ProgressBar控件的Style属性为Marquee,设置3个Timer组件的Interval属性分别为3000、1、1。

03 主要代码。

运行本实例,需要进行的变量定义及声明如下:

01   #region 声明本实例中用到的API函数
02   //获取当前鼠标指针下可视化控件的函数
03   [DllImport("user32.dll")]
04   public static extern int WindowFromPoint(int xPoint,int yPoint);
05   //获取指定句柄的父级函数
06   [DllImport("user32.dll",ExactSpelling = true,CharSet = CharSet.Auto)]
07   public static extern IntPtr GetParent(IntPtr hWnd);
08   //获取屏幕的大小
09   [DllImport("user32.dll",EntryPoint = "GetSystemMetrics")]
10   private static extern int GetSystemMetrics(int mVal);
11   #endregion

本实例在加载窗体时,窗体默认位于屏幕的右上侧。代码如下:

01   private void HideToolBar_Load(object sender,EventArgs e)
02   {
03       this.DesktopLocation = new Point(794,0);//为当前窗体定位
04       Tip.Visible = false;                   //设置Label控件为不可见状态
05       progressBar1.Minimum = 0;              //设置ProgressBar控件的最小值为0
06       progressBar1.Maximum = 10;             //设置ProgressBar控件的最大值为10
07       Counter.Start();                       //计时器Counter开始工作
08       //设置ProgressBar控件的滚动块在进度条内滚动所用的时间段
09       progressBar1.MarqueeAnimationSpeed = 100; 
10       this.MaximizeBox = false;              //设置最大化窗体为不可用状态
11   }

当窗体与屏幕边缘的距离小于3px时,如果此时鼠标指针在窗体外,那么窗体自动隐藏。代码如下:

01   private void JudgeWinMouPosition_Tick(object sender,EventArgs e)
02   {
03       if(this.Top < 3)                       //当窗体与屏幕的上边缘的距离小于3px时
04       {
05       //当鼠标指针在该窗体上时
06           if(this.Handle == MouseNowPosition(Cursor.Position.X,Cursor.Position.Y))
07           {
08               WindowFlag = 1;                //设定当前的窗体状态
09               HideWindow.Enabled = false;    //设定计时器HideWindow为不可用状态
10               this.Top = 0;                  //设定窗体上边缘与容器工作区上边缘之间的距离
11           }
12           else                               //当鼠标指针未在窗体上时
13           {
14               WindowFlag = 1;                //设定当前的窗体状态
15               HideWindow.Enabled = true;     //启动计时器HideWindow
16           }
17       }  
18       else                                   //当窗体与屏幕的上边缘的距离大于3px时
19       {
20           //当窗体处于屏幕的最左端、最右端或者最下端时
21           if(this.Left < 3 || (this.Left + this.Width) > (GetSystemMetrics(0) - 3) ||
                (this.Top + this.Height) > (Screen.AllScreens[0].Bounds.Height - 3))
22           {
23               if(this.Left < 3)  //当窗体左边缘与容器工作区左边缘的距离小于3px时
24               {
25                   if(this.Handle == MouseNowPosition(Cursor.Position.X,Cursor.
                                       Position.Y)) //当鼠标指针在该窗体上时
26                   {
27                       WindowFlag = 2;            //设定当前的窗体状态
28                       HideWindow.Enabled = false;//设定计时器HideWindow为不可用状态
29                       this.Left = 0;  //设定窗体左边缘与容器工作区左边缘之间的距离
30                   }
31                   else                          //当鼠标指针未在该窗体上时
32                   {
33                       WindowFlag = 2;           //设定当前的窗体状态
34                       HideWindow.Enabled = true;//设定计时器HideWindow为可用状态
35                   }
36               }
37               //当窗体处于屏幕的最右端时
38               if((this.Left + this.Width) > (GetSystemMetrics(0) - 3)) 
39               {
40                   if(this.Handle == MouseNowPosition(Cursor.Position.X,Cursor.
                                       Position.Y))//当鼠标指针处于该窗体上时
41                   {
42                       WindowFlag = 3;              //设定当前的窗体状态
43                       HideWindow.Enabled = false;  //设定计时器HideWindow为不可用状态
44                       //设定该窗体与容器工作区左边缘之间的距离
45                       this.Left = GetSystemMetrics(0) - this.Width; 
46                   }
47                   else                            //当鼠标指针离开该窗体时
48                   {
49                       WindowFlag = 3;             //设定当前的窗体状态
50                       HideWindow.Enabled = true;  //设定计时器HideWindow为可用状态
51                   }
52               }
53               //当窗体与屏幕的下边缘的距离小于3px时
54               if((this.Top + this.Height) > (Screen.AllScreens[0].Bounds.
                                               Height - 3))
55               {
56                   if(this.Handle == MouseNowPosition(Cursor.Position.X,Cursor.
                                       Position.Y)) //当鼠标指针在该窗体上时
57                   {
58                       WindowFlag = 4;            //设定当前的窗体状态
59                       HideWindow.Enabled = false;//设定计时器HideWindow为不可用状态
60                       //设定该窗体与容器工作区上边缘之间的距离
61                       this.Top = Screen.AllScreens[0].Bounds.Height - this.Height; 
62                   }
63                   else                          //当鼠标指针未在该窗体上时
64                   {
65                       WindowFlag = 4;           //设定当前的窗体状态
66                       HideWindow.Enabled = true;//设定计时器HideWindow为可用状态
67                   }
68               }
69           }
70       }
71   }

当窗体处于应该隐藏的区域时,窗体自动消失,在消失的地方露出窗体的一部分;当与鼠标指针接触时,窗体自动显示。代码如下:

01   private void HideWindow_Tick(object sender,EventArgs e)
02   {
03       switch(Convert.ToInt32(WindowFlag.ToString())) //判断当前窗体处于哪个状态
04       {
05           case 1:             //当窗体处于最上端时   
06               if(this.Top < 5)   //当窗体与容器工作区上边缘的距离小于5px时
07                   this.Top = -(this.Height - 2);//设定当前窗体距容器工作区上边缘的值
08               break;
09           case 2:              //当窗体处于最左端时
10               if(this.Left < 5)//当窗体与容器工作区左边缘的距离小于5px时
11                   this.Left = -(this.Width - 2); //设定当前窗体距容器工作区左边缘的值
12               break;
13           case 3:             //当窗体处于最右端时
14               //当窗体与容器工作区右边缘的距离小于5px时
15               if((this.Right + this.Width) > (GetSystemMetrics(0) - 5))  
16                   //设定当前窗体距容器工作区右边缘的值
17                   this.Right = GetSystemMetrics(0) - 2; 
18               break;
19           case 4:             //当窗体处于最下端时
20               if((this.Bottom + this.Height) > (Screen.AllScreens[0].Bounds.
                     Height - 5)) //当窗体与容器工作区下边缘的距离小于5px时
21                   //设定当前窗体距容器工作区下边缘的值
22                   this.Bottom = Screen.AllScreens[0].Bounds.Height - 2;   
23               break;
24       }
25   }

当鼠标指针在窗体上移动时,需要判断鼠标指针当前所处的窗体是否是隐藏的窗体。代码如下:

01   public IntPtr MouseNowPosition(int x,int y)
02   {
03       IntPtr OriginalHandle;//声明保存原始句柄的变量
04       OriginalHandle = ((IntPtr)WindowFromPoint(x,y));//获取原始的句柄
05       CurrentHandle = OriginalHandle;//保存原始的句柄
06       while(OriginalHandle != ((IntPtr)0))//循环判断鼠标指针是否移动
07       {
08           CurrentHandle = OriginalHandle;//记录当前的句柄
09           OriginalHandle = GetParent(CurrentHandle);//更新原始的句柄
10       }
11       return CurrentHandle;  //返回当前的句柄
12   }

举一反三

根据本实例,读者可以实现以下功能。

在屏幕的任何位置隐藏新建的窗体。

模拟腾讯QQ的登录。

实例006 非矩形窗体

实例说明

大部分Windows窗体都是一个矩形区域,读者可能已经厌倦了这种中规中矩的矩形窗体。本实例中的窗体是一个异形窗体,运行本实例会看到一个非常可爱的窗体,单击窗体右上角的就会使其关闭。实例运行结果如图1.7所示。

图1.7 非矩形窗体

技术要点

以前创建非矩形窗体是一个既费时又费力的过程,其中涉及API调用和大量的编程工作。在.NET 4.5框架中,我们可以不调用API而非常轻松地实现这一功能。只要重写窗体的OnPaint方法,在该方法中重新绘制窗体,然后将窗体设置为透明即可。

Form.OnPaint方法重写Control.OnPaint方法,用来重新绘制窗体。其语法如下:

protected override void OnPaint (PaintEventArgs e)

参数说明如下。

PaintEventArgs:为Paint事件提供数据。

实现过程

01 新建一个项目,将其命名为SpecialSharpWindows,默认窗体为Form1。

02 在Form1窗体中添加Label控件,并将BackColor属性设为透明,将Text属性设为空。

03 将Form1窗体的TransparencyKey属性设为窗体的背景色。

04 主要代码。

设置图片透明颜色的实现代码如下:

01   private void Form1_Load(object sender, EventArgs e)
02   {
03       //从指定的图片初始化System.Drawing.Bitmap类的新实例
04       bit = new Bitmap("heart.bmp"); 
05       //使用透明颜色对System.Drawing.Bitmap类进行透明设置
06       bit.MakeTransparent(Color.Transparent); 
07   }

重写基类方法。代码如下:

01   protected override void OnPaint(PaintEventArgs e)
02   {
03       e.Graphics.DrawImage((Image)bit, new Point(0, 0));//将图片画出
04   }

举一反三

根据本实例,读者可以实现以下功能。

将窗体制作成各种卡通图形。

将窗体制作成桌面“小精灵”。

实例007 任务栏通知窗体

实例说明

在日常上网的过程中,有时候会发现任务栏的右下角有一个图标在闪烁,单击后会弹出一条提示信息。本实例模拟该过程的实现效果。实例运行结果如图1.8所示。

图1.8 任务栏通知窗体

技术要点

本实例主要通过Timer组件的时间事件来实现窗体状态的判断,在显示的过程中借助Windows提供的API函数ShowWindow。

ShowWindow函数用来显示窗体。其语法如下:

[DllImportAttribute("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hwnd,Int32 cmdShow); 

参数说明如下。

hwnd:将要显示的窗体的句柄。

cmdShow:将要显示的窗体的类型。

返回值:返回true,表示窗体显示成功;返回false,表示窗体显示失败。

实现过程

01 新建一个项目,将其命名为TaskMessageWindow,修改默认窗体为TaskMessageWindow;添加一个Windows窗体,将其命名为MainForm。

02 在TaskMessageWindow窗体中添加2个Button控件、3个Label控件以及1个TextBox控件和RichTextBox控件,设置该窗体的StartPosition属性为CenterScreen。在MainForm窗体中添加2个Label控件、3个Timer组件、1个PictureBox控件、1个ImageList控件和1个NotifyIcon控件,修改displayCounter组件中的Interval属性为1000,修改iconCounter组件中的Interval属性为400。

03 主要代码。

在TaskMessageWindow窗体中需要定义及声明一些变量。代码如下:

01   public static string MainFormTitle = "";  //通知窗体的标题内容
02   public static string MainFormContent = "";//通知窗体的文本内容
03   MainForm InformWindow = new MainForm();   //实例化类MainForm的一个对象

在TaskMessageWindow窗体中,当用户输入完通知标题和通知内容后,单击“通知”按钮,在任务栏中会出现一个闪烁图标。代码如下:

01   private void informButton_Click(object sender,EventArgs e)
02   {
03       MainForm.IconFlickerFlag = true;     //设置图标闪烁标识
04       InformWindow.IconFlicker();          //调用闪烁图标方法
05   }   

在上面的代码中,闪烁图标用到IconFlicker方法和iconCounter计时器的Tick事件。代码如下:

01   public void IconFlicker()//自定义方法用来使托盘图标闪烁
02   {
03       if(MainForm.IconFlickerFlag != false)     //当托盘闪烁图标为真时
04       {
05           taskBarIcon.Icon = Properties.Resources._1;//托盘图标显示为图片
06           iconCounter.Enabled = true;//启动托盘图标的Timer
07           //在titleInform中显示通知标题
08           titleInform.Text = TaskMessageWindow.MainFormTitle; 
09           //在contentInform中显示通知内容
10           contentInform.Text = TaskMessageWindow.MainFormContent;
11       }
12   }
13   private void iconCounter_Tick(object sender,EventArgs e)
14   {
15       if(IconFlag)  //当该值为真时
16       {
17           taskBarIcon.Icon = Properties.Resources._1;  //设定托盘控件的图标
18            IconFlag = false;                           //修改该值为假
19        }
20        else                                            //当该值为假时
21        {
22            taskBarIcon.Icon = Properties.Resources._2; //设定托盘控件的图标
23           IconFlag = true;                             //修改该值为真
24       }
25   }

当阅读完提示信息后,单击“关闭”按钮,任务栏中的闪烁图标消失。代码如下:

01   private void closeInform_Click(object sender,EventArgs e)
02   {
03       InformWindow.CloseNewWindow(); //关闭新显示的窗体
04   }

在上面的代码中,让闪烁图标消失用到CloseNewWindow方法。代码如下:

01   public void CloseNewWindow()
02   {
03       base.Hide();//隐藏该窗体
04       iconCounter.Enabled = false;//设定计时器iconCounter不可用
05       taskBarIcon.Icon = Properties.Resources._2;//设定托盘图标
06       MainForm.IconFlickerFlag = false;//更改静态变量IconFlickerFlag的值
07   }

在MainForm窗体中需要显示通知标题和通知内容,因此在TaskMessageWindow窗体中需要保存通知的标题和内容。代码如下:

01   private void title_TextChanged(object sender,EventArgs e)
02   {
03       MainFormTitle = title.Text;          //记录通知的标题
04   }
05   private void content_TextChanged(object sender,EventArgs e)
06   {
07       MainFormContent = content.Text;      //记录通知的内容
08   }

在MainForm窗体的加载过程中,应该在它的构造函数中定义窗体的工作区。代码如下:

01   public MainForm()
02   {
03       InitializeComponent();
04       //开启显示提示窗体的计时器
05       displayCounter.Start();
06       //初始化工作区的大小
07       System.Drawing.Rectangle rect = System.Windows.Forms.Screen.GetWorkingArea
                                       (this);//实例化一个当前窗体的对象
08       this.Rect = new System.Drawing.Rectangle(rect.Right - this.Width - 1,rect.
                     Bottom - this.Height - 1,this.Width,this.Height); //创建工作区
09   }

在MainForm窗体中需要定义和声明一些变量。代码如下:

01   public static int SW_SHOWNOACTIVATE = 4;//该变量决定窗体的显示方式
02   public static int CurrentState;//该变量标识当前窗体状态
03   public static bool MainFormFlag=true ;
04   private System.Drawing.Rectangle Rect;//定义一个存储矩形框的区域
05   private FormState InfoStyle = FormState.Hide;//定义变量为隐藏
06   public static bool MouseState; //该变量标识当前鼠标指针状态
07   bool IconFlag = true;//用来标识图标闪烁
08   public static bool IconFlickerFlag;//运用本标识避免单击"关闭"按钮时弹出信息框
09   protected enum FormState
10   {
11       //隐藏窗体
12       Hide = 0,
13       //显示窗体
14       Display = 1,
15       //隐藏窗体中
16       Hiding=3,
17       //显示窗体中
18       Displaying = 4,
19   }
20   protected FormState FormNowState
21   {
22       get { return this.InfoStyle; }   //返回窗体的当前状态
23       set { this.InfoStyle = value; }  //设定窗体当前状态的值
24   }

如果要使MainForm窗体处于运行状态,那么必须触发NotifyIcon控件的MouseDoubleClick事件。代码如下:

01   private void taskBarIcon_MouseDoubleClick(object sender,MouseEventArgs e)
02   {
03       iconCounter.Enabled = false;//停止闪烁托盘图标计时器
04       taskBarIcon.Icon = Properties.Resources._2;//清空托盘中原有的图片
05       ShowNewWindow();//调用显示窗体方法
06   }

运行MainForm窗体时,需要调用ShowNewWindow方法。代码如下:

01   public void ShowNewWindow()
02   {
03       switch(FormNowState) //判断当前窗体处于哪种状态
04       {
05           case FormState.Hide://当提示窗体的状态为隐藏时
06               this.FormNowState = FormState.Displaying;//设置提示窗体的状态为显示中
07               //显示提示窗体,并把它放在屏幕底端
08               this.SetBounds(Rect.X,Rect.Y + Rect.Height,Rect.Width,0); 
09               ShowWindow(this.Handle,4);      //显示提示窗体
10               displayCounter.Interval = 100;  //设定时间事件的频率为100ms一次
11               displayCounter.Start();         //启动计时器displayCounter          
12               break;
13           case FormState.Display:             //当提示窗体的状态为显示时
14               displayCounter.Stop();          //停止计时器displayCounter
15               displayCounter.Interval = 5000; //设定时间事件的频率为5000ms一次
16               displayCounter.Start();         //启动计时器displayCounter
17               break;
18       }
19       taskBarIcon.Icon = Properties.Resources._1;//设定托盘图标
20   }

MainForm窗体显示时从桌面的右下角出现,本过程通过displayCounter计时器的Tick事件完成。代码如下:

01   private void displayCounter_Tick(object sender,EventArgs e)
02   {
03       switch(this.FormNowState)   //判断当前窗体的状态
04       {
05           case FormState.Display: //当窗体处于显示状态时
06               this.displayCounter.Start();                 //启动计时器displayCounter
07               this.displayCounter.Interval = 100;          //设定计时器的时间事件间隔
08               if(!MouseState)                              //当鼠标指针不在窗体上时
09               {
10                   this.FormNowState = FormState.Hiding;    //隐藏当前窗体
11               }
12               this.displayCounter.Start();                 //启动计时器displayCounter
13               break;
14           case FormState.Displaying:                       //当窗体处于显示中状态时
15               if(this.Height <= this.Rect.Height - 12)     //如果窗体没有完全显示
16               {
17                   this.SetBounds(Rect.X,this.Top - 12,Rect.Width,this.Height + 12);
                                                              //设定窗体的边界
18               }
19               else                                         //当窗体完全显示时
20               {
21                   displayCounter.Stop();                   //停止计时器displayCounter
22                   //设定窗体的边界
23                   this.SetBounds(Rect.X,Rect.Y,Rect.Width,Rect.Height); 
24                   this.FormNowState = FormState.Display;   //修改当前窗体所处的状态
25                   this.displayCounter.Interval = 5000;     //设定计时器的时间事件间隔
26                   this.displayCounter.Start();             //启动计时器displayCounter
27               }
28               break;
29           case FormState.Hiding:                           //当窗体处于隐藏中状态时
30               if(MouseState)                               //当鼠标指针在窗体上时
31               {
32                   this.FormNowState = FormState.Displaying;//修改窗体的状态为显示中
33               }
34               else                                         //当鼠标指针离开窗体时
35               {
36                   if(this.Top <= this.Rect.Bottom - 12)    //当窗体没有完全隐藏时
37                   {
38                       this.SetBounds(Rect.X,this.Top + 12,Rect.Width,this.Height - 12);
                                                              //设定窗体的边界
39                   }
40                   else                                     //当窗体完全隐藏时
41                   {
42                       this.Hide();                         //隐藏当前窗体
43                       this.FormNowState = FormState.Hide;  //设定当前的窗体状态
44                   }
45               }
46               break;
47       }
48   }

在MainForm窗体的隐藏与显示的过程中需要判断鼠标指针是否在窗体中。代码如下:

01   private void MainForm_MouseEnter(object sender,EventArgs e)
02   {
03       MouseState = true;     //设定bool型变量MouseState的值为真
04   }
05   private void MainForm_MouseLeave(object sender,EventArgs e)
06   {
07       MouseState = false;   //设定bool型变量MouseState的值为假
08   }

举一反三

根据本实例,读者可以实现以下功能。

制作任务栏弹出窗体。

实现类似腾讯QQ的闪烁图标提示。

实例008 设置窗体在屏幕中的位置

实例说明

通过设置窗体的Left属性和Top属性可以准确设置窗体的位置。实例运行结果如图1.9所示。

图1.9 设置窗体在屏幕中的位置

技术要点

设置窗体在屏幕中的位置,可以通过设置窗体的属性来实现。窗体的Left属性表示窗体距屏幕左侧的距离,Top属性表示窗体距屏幕上方的距离。

实现过程

01 新建一个项目,将其命名为SetLocation,默认窗体为Form1。

02 在Form1窗体上添加Label控件,添加TextBox控件用来输入距屏幕的距离,添加Button控件用来设置窗体在屏幕中的位置。

03 主要代码。

01   private void button1_Click(object sender, EventArgs e)
02   {
03       //设置窗体左边缘与屏幕左边缘的距离
04       this.Left = Convert.ToInt32(textBox1.Text);
05       //设置窗体上边缘与屏幕上边缘的距离
06       this.Top = Convert.ToInt32(textBox2.Text);
07   }

举一反三

根据本实例,读者可以实现以下功能。

根据分辨率的变化动态设置窗体位置。

用Timer控件实时显示窗体位置。

实例009 获取桌面分辨率

实例说明

获取桌面分辨率可以使用API函数GetDeviceCaps,但该函数参数较多,使用不方便。如何更方便地获取桌面分辨率呢?在本实例中,通过读取Screen对象的属性来获取桌面分辨率(以px为单位)。实例运行结果如图1.10所示。

图1.10 获取桌面分辨率

技术要点

C#中提供了Screen对象,在该对象中封装了屏幕相关信息。可以通过读取Screen对象的相关属性来获取屏幕的信息,其中Screen.PrimaryScreen.WorkingArea.Width属性用于获取桌面宽度,Screen.PrimaryScreen.WorkingArea.Height属性用于获取桌面高度。

Screen.PrimaryScreen.WorkingArea属性用于获取显示器的工作区。工作区是显示器的桌面区域,不包括任务栏、停靠窗体和停靠工具栏。其语法如下:

public Rectangle WorkingArea { get; }

属性值为一个Rectangle,表示显示器的工作区。

实现过程

01 新建一个项目,将其命名为DeskSize,默认窗体为Form1。

02 在Form1窗体上添加一个Button控件,用来获取桌面分辨率;添加两个TextBox控件,用来输出所获取的桌面分辨率。

03 主要代码。

01   private void button1_Click(object sender, EventArgs e)
02   {
03       //在textBox2中显示桌面的高度
04       textBox2.Text = Screen.PrimaryScreen.WorkingArea.Height.ToString();
05       //在textBox1中显示桌面的宽度
06       textBox1.Text = Screen.PrimaryScreen.WorkingArea.Width.ToString();
07   }

举一反三

根据本实例,读者可以实现以下功能。

根据显示器的分辨率设置窗体大小及位置。

根据显示器的分辨率调整窗体。

实例010 在窗体关闭之前加入确认对话框

实例说明

用户对程序进行操作时,难免会有错误操作的情况,例如不小心关闭程序,如果尚有许多资料没有保存,那么损失将非常严重,因此最好使程序具有灵活的交互性。人机交互过程一般都是通过对话框来实现的,对话框中有提示信息,并且提供按钮让用户选择,例如“是”或“否”。这样用户就能对所做的动作进行确认。正如前面所说的不小心关闭程序,如果在关闭程序之前提示用户将要关闭程序,并且提示用户选择是否继续下去,这样可大大减少误操作现象。本实例中的窗体在关闭时会显示一个确认对话框,该对话框中有两个按钮“是”与“否”,代表是否同意关闭窗体的操作。实例运行结果如图1.11所示。

图1.11 在窗体关闭之前加入确认对话框

技术要点

窗体正要关闭但是没有关闭时会触发FormClosing事件,该事件中的参数FormClosing EventArgs e中包含Cancel属性,如果设置该属性为true,窗体将不会被关闭。因此在该事件处理代码中可以提示用户是否关闭窗体,如果用户不想关闭窗体,则设置该属性为true。利用MessageBox参数的返回值可以知道用户所选择的按钮。下面详细介绍一下相关属性。

CancelEventArgs.Cancel属性用来获取或设置指示是否应取消事件的值。其语法如下:

public bool Cancel { get; set; }

如果应取消事件,属性值为true,否则为false。

实现过程

01 新建一个项目,将其命名为QueryClose,默认窗体为Form1。

02 主要代码。

01   private void Form1_FormClosing(object sender, FormClosingEventArgs e)
02   {
03       //当单击"是"按钮时
04       if(MessageBox.Show("将要关闭窗体,是否继续?", "询问", MessageBoxButtons.YesNo) == 
                           DialogResult.Yes)
05       {
06           e.Cancel = false;               //不取消事件的值
07       }
08       else//当单击"否"按钮时
09       {
10           e.Cancel = true;                //取消事件的值
11       }
12   }

举一反三

根据本实例,读者可以实现以下功能。

使窗体的关闭按钮无效。

在系统托盘菜单中显示关闭按钮。

实例011 禁用窗体上的关闭按钮

实例说明

一般情况下,在窗体的右上角都有最大化、最小化和关闭按钮。在多文档界面(Multiple-Document Interface,MDI)中,有时候为了避免重复打开同一个窗体,需要禁用窗体上的关闭按钮。本实例可以解决这个问题。实例运行结果如图1.12所示。

图1.12 禁用窗体上的关闭按钮

技术要点

本实例主要用到窗体处理函数WndProc的重写方法,在该方法内部截获单击关闭窗体的信息,从而实现禁用关闭按钮的功能。

实现过程

01 新建一个项目,将其命名为ForbidCloseButton,修改默认窗体为ForbidCloseButton。

02 在ForbidCloseButton窗体上添加一个MenuStrip控件。

03 主要代码。

通过WndProc的重写方法截获单击关闭窗体的信息实现禁用关闭按钮的功能。代码如下:

01   protected override void WndProc(ref Message m)
02   {
03       const int WM_SYSCOMMAND = 0x0112;  //定义将要截获的消息类型
04       const int SC_CLOSE = 0xF060;       //定义关闭按钮对应的消息值
05       //当单击关闭按钮时
06       if((m.Msg == WM_SYSCOMMAND) && ((int)m.WParam == SC_CLOSE))    
07       {
08           return;                       //直接返回,不进行处理
09       }
10       base.WndProc(ref m);              //传递下一条消息
11   }

举一反三

根据本实例,读者可以实现以下功能。

禁用窗体最大化按钮。

禁用窗体最小化按钮。

相关图书

程序员的制胜技
程序员的制胜技
C#完全自学教程
C#完全自学教程
C#从入门到精通(第2版)
C#从入门到精通(第2版)
 C#初学者指南
C#初学者指南
C#本质论(第4版)
C#本质论(第4版)
C# 5.0入门经典
C# 5.0入门经典

相关文章

相关课程