面向快乐的程序设计-C#篇
前言
当然是为了快——乐——
C#是啥呀
-
用C#做数据库课设的前端真方便。(。・∀・)ノ
——2017.5.10 在做数据库课设的斯斯
-
C# (英文名为 CSharp) 是微软开发的一种面向对象的编程语言,其语法与 C++ 类似,但在编程过程中要比 C++ 简单;C#语言本身简单、安全,它比C、C++、Java提供了更多的数据类型,并且每个数据类型都是固定大小的;C#具有面向对象语言的基本特征,即封装、继承、多态;C#支持跨平台,截止至C#6.0版本,已经能够在Mac、Linux等除Windows以外的多个平台使用;C#不仅能开发在控制台下运行的应用程序,也能够开发Windows窗体应用程序、网站、手机应用等多种应用程序。
——2020.7.21 源自网络
-
采用C#进行窗体开发,可以快速搭建出软件UI界面框架,能在一定程度上进行人机交互。如果需要花里胡哨的UI界面,可以自行编写控件,也可以网上
白嫖下载别人的控件,不过还是建议使用其他语言并在相应引擎上进行开发。 ——2020.7.21 是阳光明媚的一天呀
-
在C#窗体上用GDI和Direct2D绘图真好玩。
——2020.7.31 抓住了七月的尾巴
-
面向C#开发的OpenCVSharp绘图也很方便,绘图效果以及效率会比GDI好一些。
——2021.2.10 除夕前一天,春训第二天
-
C#可以嵌入Unity3d程序,使用unity2d进行2d绘图,效率比自身使用OpenCVSharp高很多,毕竟unity2d是一个封装的比较好的引擎,连自己随便用的绘图工具都比不过,这岂不是很离谱(滑稽)
——2021.5.15 快结项时的忙碌日子
C#基础语法
C#基本数据类型
整型(int)
- …
-
C#进阶数据类型
-
C# 字典(Dictionary)
提到字典就不得不提数组,C#中,数组是通过数组下标来对数组元素进行1对1查找,数组下表仅限于数字本身;而使用字典对一组数据进行管理,可不用数字进行管理,能够有更具有现实意义的字符串或是其他数据类型进行管理,用于唯一标记数值的那个东西称之为key,被标记的数值(不局限于单一数值,可是一任何形式的存在,比如类)称之为key唯一对应的value。
key具有唯一性,在一个字典中,不会也不能存在相同的key。
key、value可以以任意类型存在。
字典(Dictionary)的创建:
//创建一个key为int型,value为string型的字典 //通过检索int,可以唯一对应一个string Dictionary <int, string> myDictionary = new Dictionary <int, string>();
字典(Dictionary)的元素添加:
//添加一个字典元素,这个元素的key为1,value为"message"字符串 myDictionary.Add(1,"message");
字典(Dictionary)的元素查找:
//通过key查找对应元素 if(myDictionary.ContainsKey(1)) { Console.WriteLine("Key:{0}, Value:{1}", "1", myDictionary[1]); }
字典(Dictionary)的遍历:
//通过KeyValuePair遍历元素 foreach(KeyValuePair<int,string> kvp in myDictionary) { Console.WriteLine("Key={0}, Value={1}", kvp.Key, kvp.Value); } //仅遍历Keys属性 Dictionary<int, string>.KeyCollection KeyCol = myDictionay.Keys; foreach(int key in KeyCol) { Console.WriteLine("Key={0}", key); } //仅遍历Values属性 Dictionary<int, string>.ValueCollection ValueCol = myDictionay.Values; foreach(string value in ValueCol) { Console.WriteLine("Value={0}", value); }
字典(Dictionary)的移除:
//使用Remove移除指定键值 myDictionary.Remove(1);
-
C#信号量(Mutex)
通过定义信号量,实现同一资源的互斥访问。可用于判定是否已有某程序正在运行,以确保当前至多只有一个程序正在运行。
//本例用于禁止两例或多例程序同时运行,即禁止同时打开同一软件 bool flag = false; Mutex muutex = new Mutex(true, "互斥体Name", out flag); if(flag) { MessageBox.Show("程序已运行!"); Environment.Exit(1); //退出程序 }
除上述内容外,互斥信号量还可以用于对某些资源进行互斥管理,即同一时间只允许单一线程使用,不可同时访问。
-
C#运算符
C#变量
C#常量
C#变量命名规则
C# if else
C# switch case
C# for循环
C# while循环
C# do while循环
C# break
C# continue
C# goto
C#类和对象
C#字符串
-
C#可变字符串(StringBuilder)
介绍可变字符串之前,需要知道系统是怎么处理字符串(string)的:
当我们请求开辟一个内存空间,用于存储“你好”二字时,系统会为我们恰到好处地开辟出刚好存放“你好”的空间,并把“你好”放进去,如果这时我们想存储“你好呀”,系统会怎么做呢?
系统会在 你好 这个空间后再开辟一个空间,存放 吗 这个字么?
不,当然不是,因为系统也无法保证 你好 二字之后的空间是空余的。此时的系统会再去开辟一个恰到好处存放 你好呀 三个字的内存空间,并把它存进去。那么现在内存中就有了 你好 和 你好呀 两块空间了。如果我们恰好需要多次增加、替换、移除文字,那么系统会不断的开辟新的空间存放新的文字(因为不能在老文字上修改),此时很多文字内容是不再被需要的,但是仍然占用空间,造成内存资源浪费。
而使用可变字符串,可以在一定程度上减少垃圾的产生。
那么什么是可变字符串?
可变字符串,是告诉系统,不要按照当前存放的字符大小开辟空间,而是直接开辟一块定值的空间,这个空间都属于我,其他数据不允许来占用,因此,这个空间就仅属于我们了,我们只要不超出空间大小,在这里面想加就加,想改就改,想删就删,不用考虑会占用其他空间的问题。
我们开辟一个可变字符串,可能直接开辟了10个字符大小的空间,可是我们仅仅存了 你好 两个字符大小的内容,会造成浪费吗?这个问题不能简单回答 是 或 不是,因为这需要分情况。如果这个变量存好了这个字符串,以后基本不变动,那我们就是亏了,而如果这个变量里的字符串会经常变动,那我们就是赚了。
以下是可变字符串的声明,以及常用的操作:
//可变字符串的声明: StringBuilder builder = new StringBuilder(); //也可具体输入想要开辟空间的大小,比如开10个字符大小的空间: StringBuilder builder = new StringBuilder(10); //可变字符串的拼接: string value = "拼多多"; builder.Append(value);
使用场景:频繁对字符串进行操作(增加、替换、移除)
C#数组
-
C#参数数组(Params)
“我现在想写一个方法,这个方法能够完成a+b的操作,并返回a+b的结果,我们要怎么定义这个方法呢?”
“那我们可以写一个 public int Add(int a, int b) { return a+b; } 的方法呀”
“那我现在希望写一个方法,这个方法可以完成a+b+c呢?完成这个之后我还希望完成一个a+b+c+d的方法呢?在这之后我还想继续加呢….”
“emmmmm…..”(磨刀)
“别急别急!别磨刀了!你还记得有什么方法是可以随意写入参数的吗?”
“啊?我想想…..是 Console.WriteLine(“{0}{1}{2}”, 1, 2, 3); 这种类型的方法!只要我想加参数,直接调用的时候加就可以了!方法/函数本体我不用动的!”
“是的没错,今天我们要讲的就是这种方法的实现了!”(hhhhh别激动, 快把刀放下)
参数数组在一定程度上可以方便调用者调用方法,并且也不用不断地去重载一个方法。通过Params参数数组,能够实现类似
Console.WriteLine("{0}{1}{2}", 1, 2, 3);
的方法。具体示例见下方咯。public int Add(params int[] arr){ int sum = 0; for(int i=0;i<arr.count;i++){ sum += arr[i]; } return sum; //该函数: //1.可以传递一组数据类型相同的变量集合(因为使用了params) //2.甚至可以不传递参数 //3.还可以传递数组 }
C#继承
C#接口
C#集合
C#泛型
C#文件操作
-
打开文件对话框
我们希望按下一个按钮后,弹出文件对话框(就是平常安装软件的时候,想自定义安装路径时,点击按钮后,开始选择路径的窗口)。
private void button_Click(object sender, EventArgs e) { //创建文件对话框对象 OpenFileDialog ofd = new OpenFileDialog(); //文件对话框展示 ofd.ShowDialog(); //获取所选择的文件的全路径 string path = ofd.FileName; //将文件路径显示到我们定义的textbox框中 textBox.Text = path; }
C#委托和事件
A给了B一个委托:“B君B君,要不你去和C妹说一下我喜欢她?”
-
C#委托(Delegate)
委托不是方法!不是方法!不是方法!
虽然委托的定义与方法比较相似,但是委托并不能被称为方法,委托是一种引用类型。
使用委托时需要遵循三步走:声明委托、实例化委托、调用委托。
“委托很负责任,只要被调用了,他就会竭尽全力把被委托的事情全部做了。”
-
C#命名方法委托
//修饰符 delegate 返回值类型 委托名(参数列表); public delegate void TellMyLove();
委托的定义和方法的定义非常相似,但是委托不是方法嗷!!!
当委托定义好之后,我们就可以对委托进行实例化了。
委托酱紫实例化 ↓
//委托名 委托对象名 = new 委托名(方法名); TellMyLove B = new TellMyLove(TalkToC);
在委托时应用的方法可能是静态方法(static)或是实例方法,对于这两种情况,需要带入的方法名有所不同。现在大家都是B了,下面各位B们将在这两种情况下,帮助A告诉C:“A喜欢C”。
-
委托中传入静态方法
class Program { public delegate void TellMyLove(); static void Main(String[] args) { TellMyLove B = new TellMyLove(Talk.WhatAWantToSay); B();//到你行动了! } } class Talk { public static void WhatAWantToSay() { MessageBox.Show("Hey, C, A said he love you."); } }
在静态方法中,向委托传递方法名时只需要使用“
类名.方法名
”的形式。 -
委托中传入实例方法
class Program { public delegate void TellMyLove(); static void Main(String[] args) { TellMyLove B = new TellMyLove(new Talk().WhatAWantToSay); B();//行动吧! } } class Talk { public void WhatAWantToSay() { MessageBox.Show("Hey, C, A said he really love you."); } }
这里在委托中使用的是实例方法,因此需要通过类的实例来调用方法,即需要使用“
new 类名().方法名
”的形式。
-
-
C#多播委托
多播委托就是指在一个委托中注册多个方法,在注册方法的时候可以通过使用“+”(或者“-”)来实现添加(或者撤销)方法。
也就是说A君除了可以委托B君告诉C妹他喜欢C妹以外,还可以委托B君帮忙约个时间啥的。也就是说一个委托里可以包含多件事情。
“诶诶,B君,你看到C妹的时候告诉她我喜欢她,然后把这束玫瑰花🌹也送给她!谢谢!”
下面除了委托B君告诉C妹A君喜欢她之外,还希望B君帮A君把玫瑰花送给C妹。
class Program { public delegate void TellMyLove(); static void Main(String[] args) { //把这个委托需求交给B了!(实例化委托) TellMyLove B = new TellMyLove(new Talk().WhatAWantToSay); //还希望B能够帮A送一朵花,因此需要向这个委托注册方法 B += new Talk().SendFlower; B();//行动吧! } } class Talk { public void WhatAWantToSay() { MessageBox.Show("\"Hey, C, A said he really love you.\""); } public void SendFlower() { MessageBox.Show("Send a flower to C"); } }
如果A君得知C妹花粉过敏,在B君开始完成这个委托之前,A君还可以撤销委托中注册的这个送花的方法,即在委托注册方法时使用
-=
操作就可以了。 -
C#匿名委托
匿名委托实际上就是在委托中通过定义代码块来实现委托的作用。匿名委托的语法形式如下。
//1.定义委托 修饰符 delegate 返回值类型 委托名(参数列表);
//2.定义匿名委托 委托名 委托对象 = delegate { //代码块 /该委托想要做的事情 }; //←细节标点嗷
//3.调用匿名委托 委托对象名(参数列表);
现在我们通过匿名委托的方式,让B君再帮A君对C妹进行一次表白,这次我们仍然需要告诉C妹A君喜欢她,并送她一朵花。
“表白的形式各不同相同,但其本质都是一次勇敢的尝试。”
public delegate void TellMyLove(); class Program { public delegate void TellMyLove(); static void Main(String[] args) { //不用注册方法了,直接在实例化委托的时候实现具体操作 TellMyLove B = delegate { MessageBox.Show("\"Ms.C, Mr.A said he love you!\""); MessageBox.Show("Mr.B send a flower to Ms.C"); }; //要做的事情都写委托里了,上吧,B君! B(); } }
-
C#事件
事件是一种引用类型,实际上也是一种特殊的委托。
每一个事件的发生都会有一个发送方和接收方,发送方是指引发事件的对象,接收方则是获取、处理事件。事件需要与委托一起使用。
A君对B君说:“诶诶,B君,当你看到C妹后,你和她说我喜欢她呗。”
事件定义的语法形式如下:
访问修饰符 event 委托名 事件名;
在定义事件时会使用到委托,因此需要在定义事件之前定义委托。
定义事件之后需要定义事件所使用的方法,并通过事件来调用委托。
现在我们需要在B君碰到C妹这个事件发生时,才让B君帮A君传递告白信息。
class Program { //定义委托 //我们把这个委托给B public delegate void B(); //定义事件 //当B遇到C public event B MeetC; //定义委托中调用的方法(定义需要委托的事) //B的委托内容就是帮助A君告白 public void TellALove() { MessageBox.Show("\"Hey, Ms.C, Mr.A said he love you\""); } //创建触发事件的方法 //触发B去执行他的委托的这样一个事件 public void WhenMeetC() { //触发事件,必须与事件是同名方法 MeetC(); } static void Main(String[] args) { //创建该类实例 Program program = new Program(); //实例化事件,使用委托指向处理方法 program.MeetC = new B(program.TellALove); //调用触发事件的方法 program.WhenMeetC(); } }
“诶诶,B君,你再帮我把这束花给C妹吧!”
按照之前的老规矩,这次我们需要B君告诉A君喜欢C妹的同时,还需要B君帮忙送一朵花。
class Program { static void Main() { //创建TellALove类的实例 TellALove tellALove = new TellALove(); //实例化TellALove类中的事件,使用委托指向处理方法 //把需要B做的事情绑到B遇到C这个事件上 tellALove.MeetCEvent += new TellALove.B(TellALove.TalkToC); tellALove.MeetCEvent += new TellALove.B(TellALove.SendFlowerToC); //调用触发事件的方法 //现在让B去学校吧,他会在学校碰到C tellALove.BGoToSchool(); } } class TellALove { //定义委托 //我们把委托给B君 public delegate void B(); //定义事件 //我们把 B君遇到C妹 称为一个事件 public event B MeetCEvent; //定义委托中使用的方法 //写下B具体需要做的一些事情 public static void TalkToC() { MessageBox.Show("\"Hey, Ms.C, Mr.A said he love you\""); } public static void SendFlowerToC() { MessageBox.Show("B send a flower to C"); } //创建触发事件的方法 //写下B遇到C的方法,比如B去学校了,那么B可以在学校里碰到C public void BGoToSchool() { MeetCEvent(); } }
需要注意的是,在使用事件时如果事件的定义和调用不在同一个类中,实例化的事件只能够出现在
+=
或者-=
操作符的左侧。(上述代码事件在TellALove类中,而事件的调用在Program类中)在上面的代码中,实例化事件的代码只能写成
tellALove.MeetCEvent += new TellALove.B(TellALove.TalkToC);
而不能使用
tellALove.MeetCEvent = new TellALove.B(TellALove.TalkToC);
C#WinForm
WinForm 是 Windows Form 的简称,是基于 .NET Framework 平台的客户端(PC软件)开发技术,一般使用 C# 编程。C# WinForm 编程需要创建「Windows窗体应用程序」项目。
“啊!WinForm!是你让我跳出了小黑框编程!”长期小黑框编程的B君泪流满面。
-
C#创建Windows窗体应用程序(WinForm程序)
在VS2015中,单击“文件”→“新建”→“项目”,会弹出新建项目框,选择该框中的“Windows窗体应用程序”,并更改项目名称、存储位置、解决方案名称等内容,更改完毕后单机“确定”,一个Windows窗体应用程序就创建好啦。
“这么简单的步骤俺就不配图了”B君如是说道。
每一个创建好的Windows窗体应用程序的项目文件中,都会有一个默认的窗体程序Form1.cs,并且在Program.cs文件里,会指定第一个要运行的窗体,可在Program.cs文件中进行更改,选择想要第一个打开的窗体。
在Windows窗体应用程序创建完成后,就已经是一个可以运行的程序了。此时启动程序,会得到一个啥内容都没有的弹窗。虽说是啥内容都没有,但是!!有!弹!窗!在C++中做题的时候,面对的永远是控制台的小黑框,希望有些现代弹窗还需要调用相关函数实现,可是在WinForm中,能够实现所见即所得,窗口设计中设计成啥样,打开后就能够是啥样。这在面对非解题向的的编程,当然是有个好看的框框会舒服一些。
让我们再看回Program.cs文件,不出意外(你没有加什么花里胡哨的插件的话)它的代码长这样:
static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }
在上述代码的Main方法中:
- 第一行代码,用于启动应用程序中可视的样式,如果控件和操作系统支持,那么控件的绘制就能根据显不风格来实现。
- 第二行代码,控件支持 UseCompatibleTextRenderingproperty 属性,该方法将此属性设置为默认值。
- 第三行代码,用于设置在当前项目中要启动的窗体,这里 new Form1() 即为要启动的窗体。
在Windows窗体应用程序中,界面是通过采用不同类型的控件构成的,系统默认可使用的控件都在工具箱中。啥?工具箱在哪?工具箱寻找路径:“视图“→”工具箱“,就可以得到工具箱了。
工具箱中的控件划分为公共控件、容器、菜单和工具栏、数据、组件、打印、对话框等组。这些控件将在后续一一学习。如果学完了发现这些控件不够用,功能不能实现自己想要的结果咋办?
自己做控件呀可以在网上下载别人的控件用呀!Windows窗体应用程序也称为事件驱动程序,也就是通过鼠标单击界面上的控件、通过键盘输入操作控件等操作来触发控件的不同事件完成相应的操作。也就是说WinForm提供了一个可视化的界面,输入输出均可以体现在该界面上,但是程序核心的逻辑还是需要在后台实现。
-
C#窗体属性
每一个Windows窗体应用程序都是有好多好多窗体构成的(如果你就一个窗体当我没说),窗体的外观可以在窗体的属性中进行调整。
右键窗体,选择“属性”,可以得到该窗体的属性面板,虽然属性看着都是英文的,但是当你点击某一个属性后,在最下方会有中文对该属性进行介绍嗷。(多谢VS救我狗命)
窗体经常会更改到的内容有:Name、Text、BackColor、BackgroundImage等内容,具体内容就不细说咯。
-
C#窗体事件
在上面介绍了窗体属性,而超级细
(指细心)的小伙伴就会注意到了,在窗体属性面板上方,有一个闪电的图标,当我们单击这个图标,就能够查看到窗体中的事件了。同样,窗体的事件也有很多种,单击每一种事件在最下方也有对该事件的中文描述(感谢VS的翻译!),经常会用到的一些事件有:Load、MouseClick、KeyDown、KeyPress、FormClosed等。双击你想添加的事件,就会跳到代码界面,同时VS也给你准备好了事件的框架,你只需要往里面添加触发该事件后要做的事情就可以咯。
由于我不细,所以不细说咯。 -
C#窗体方法
C#中自定义的窗体都会继承自System.Windows.Form类,可以使用该类中已有的成员,也就是属性、方法、事件等等。
下面简单
备忘介绍几个打开、关闭、调整窗体时常用的一些方法。方法 作用 void Show() 显示窗体 void Hide() 隐藏窗体 void Close() 关闭窗体 void CenterToParent() 使该窗体在父窗体边界内居中 void CenterToScreen() 使该窗体在当前屏幕上居中 在使用窗体中的方法时需要注意,如果是当前窗体需要调用方法直接使用
this
关键字代表当前窗体,通过this.方法名 (参数列表)
的方式调用即可。如果要操作其他窗体,则需要用窗体的实例来调用方法。
-
MessageBox类
“在C++做题的时候,我可以中途在小黑框里输出一些我希望追踪的值,方便我调试程序,现在C#没看到有小黑框了,那我调试程序的时候想中途有个弹窗告诉我当前数值该怎么做呀?”A君发出了疑惑,“或许你可以用Message Box进行弹窗!”C妹表示这应该很简单。
在Windows中,消息框是通过 McssageBox 类来实现的,在 MessageBox 类中仅定义了 Show 的多个重载方法,该方法的作用就是弹出一个消息框。
由于 Show 方法是一个静态的方法,因此调用该方法只需要使用
MessageBox.Show( 参数 )
的形式即可弹出消息框。上面也提到了,MessageBox类中有多个关于Show的重载,因此消息框显示时可有不同的样式,例如标题、图标、按钮等内容都可以不同。
常用的Show方法参数如下表所示。
方法 说明 DialogResult Show(string text) 指定消息框中显示的文本(text) DialogResult Show(string text, string caption) 指定消息框中显示的文本(text)以及消息框的标题(caption) DialogResult Show(string text, string caption, MessageBoxButtons buttons) 指定消息框中显示的文本(text)、消息框的标题(caption)以及消息框中显示的按钮 (buttons) DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) 指定消息框中显示的文本(text)、消息框的标题(caption )、消息框中显示的按钮 (buttons)以及消息框中显示的图标(icon) 比较细的同学又发现了,上述方法中有两个类被加粗了
(因为太细了你们看不到),这两个类都是枚举类型,我们可以来细细记一记品一品里面都有些啥:- MessageBoxButtons枚举类型主要用于设置消息框中显示的按钮,具体的枚举值如下。
- OK:在消息框中显示“确定”按钮。
- OKCancel:在消息框中显示“确定”和“取消”按钮。
- AbortRetryIgnore:在消息框中显示“中止” “重试”和“忽略”按钮。
- YesNoCancel:在消息框中显示“是” “否”和“取消”按钮。
- YesNo:在消息框中显示“是”和“否”按钮。
- RetryCancel:在消息框中显示“重试”和“取消”按钮。
- MessageBoxIcon 枚举类型主要用于设置消息框中显示的图标,具体的枚举值如下。
- None:在消息框中不显示任何图标。
- Hand、Stop、Error:在消息框中显示由一个红色背景的圆圈及其中的白色X组成的图标。
- Question:在消息框中显示由圆圈和其中的一个问号组成的图标。
- Exclamation、Warning:在消息框中显示由一个黄色背景的三角形及其中的一个感叹号组成的图标。
- Asterisk、Information:在消息框中显示由一个圆圈及其中的小写字母 i 组成的图标。
在调用MessageBox类的Show方法时会返回一个DialogResult类型的值,这个值也是一个枚举类型,是消息框所返回的值,通过单击消息框中不同的按钮,会得到不同的消息框返回值。也就是说我们在消息框中点击的”是”、”否”、”取消”、”中止”等按钮,在DailogResult中都会对应一个返回值。
- DialogResult 枚举类型的具体值如下。
- None:消息框没有返回值,表明有消息框继续运行。
- OK:消息框的返回值是 0K (通常从标签为“确定”的按钮发送)。
- Cancel:消息框的返回值是 Cancel (通常从标签为“取消”的按钮发送)。
- Abort:消息框的返回值是 Abort (通常从标签为“中止”的按钮发送)。
- Retry:消息框的返回值是 Retry (通常从标签为“重试”的按钮发送)。
- Ignore:消息框的返回值是 Ignore (通常从标签为“忽略“的按钮发送)。
- Yes:消息框的返回值是 Yes (通常从标签为“是“的按钮发送)。
- No:消息框的返回值是 No (通常从标签为“否“的按钮发送)。
- MessageBoxButtons枚举类型主要用于设置消息框中显示的按钮,具体的枚举值如下。
-
C#控件(WinForm控件)简介及相关控件介绍
-
C#控件简介
控件特别容易理解,控件就是控制各个零件的这样一个设备。通过使用控件,我们可以指定输入文本的位置、可单击的位置、图片显示的位置等等,控件就是用来控制界面元素的显示、交互。
其中:
- 可输入文本位置对应于 Windows 窗体应用程序中的文本框、多行文本框等。
- 可选择的位置对应于 Windows 窗体应用程序中的复选框、单选按钮、下拉列表框。
- 可单击位置对应于 Windows 窗体应用程序中的按钮、超链接标签、菜单栏、工具栏等。
- 图片显示位置对应于 Windows 窗体应用程序中的图片控件。
我们就是通过丰富多彩的控件组成了一个好看的窗体(当然,用VS自带的控件肯定做不出太华丽的窗体,人家QQPC版登录界面的控件当然是人家自己写的控件来做的啦!)
-
C#Label和LinkLabel:标签控件
“我已经会打开一个空白的窗体了,可是我这么大个窗体里咋写文字啊,啥都没有,一片空白。”C妹看着空白的窗体陷入了沉思。“没有写文字的地方,给一个就好了。”A君显然是了解一些内幕,似乎知道该怎么做。
由于在窗体中无法直接编写文本,通常使用标签控件来显示文本。
在Windows窗体应用程序中,标签控件主要分为普通标签(Label)和超链接形式的标签(LinkLabel),C#中的任何一个控件,都可以右键它,选择属性,来显性地更改该控件的内容,这里的标签控件也是如此,通常会更改的内容有:Name、Text、Font、Size等内容,需要更改其他内容,可以自己在相应的属性面板里寻找。
标签控件既然是作为一个控件,当然也是可以为其添加事件的,添加事件也是在小闪电里进行添加。后续的控件简述将不再继续赘述“事件在哪里添加”的问题(高冷),当然,遇到一些好玩的事件相关内容会有提及。
所有控件的内容除了在属性面板显性更改外,还可以在程序中进行更改,只需要
控件名.控件属性 = xxx
就可以对该控件的相应属性在程序运行时进行实时更改。比如标签控件的Text属性,就是展示出来给大家看的,如果希望这个值随着程序的运行展示出相应的值,那么就可以在程序中对Text的值进行修改,这样前端的窗口上也会实时被修改。 -
C#TextBox:文本框控件
“我看人家的界面都有输入信息的地方,为啥我就只有一个显示信息的Label啊,我不听我不听我也要我也要!”C妹似乎不太适合学习C#,A君有点头疼。
文本框(TextBox)是在窗体输入信息时最常用的控件,通过设置文本框属性可以实现多行文本框、密码框等。在窗体上输入信息时使用的最多的就是文本框了。(QQ登录界面里的输入qq号和密码的地方就是文本框呀)
文本框的属性也可以在属性面板看到噢,这里就提几个label控件没有的属性叭:
- WordWrap:文本框中的文本是否自动换行,如果是 True,则自动换行,如果是 False,则不能自动换行。
- PasswordChar:将文本框中出现的字符使用指定的字符替换,通常会使用“*”字符。(密码框的实现就在这里!)
- ReadOnly:指定文本框中的文本是否可以更改,如果为 True,则不能更改,即只读文本框,如果为 False,则允许更改文本框中的文本。(我们在填写一些信息的时候,有些框框长得是个文本框,但是别人给我们填好了信息,我们填不了的,就是通过这个实现的啦)
到这里为止,我们就能做一个简易的登录窗体咯。(想要真正实现登录操作,还需要连数据库噢,这里只是意思意思,你懂我的意思吧!)
啥?等着我给登录窗体源码?不存在的,自己去做。
啊…喜欢我?
这我考虑考虑
你等等啊,一会儿就好
来了来了,一个破败不堪的登录窗体:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button2_Click(object sender, EventArgs e) { MessageBox.Show("醒醒,数据库都没连呢,还想着注册的事"); } private void button1_Click(object sender, EventArgs e) { if(textBox1.Text == "" || textBox2.Text == "")//白嫖的人就喜欢啥都不写就像登录! { MessageBox.Show("好歹写点什么吧"); } else if(textBox1.Text == "123456" && textBox2.Text == "123456")//宇宙级难度的密码! { MessageBox.Show("芜湖!登录成功!"); } else//告诉告诉不看源码、不看博客的小伙伴们用户名和密码吧! { MessageBox.Show("登录失败!"); MessageBox.Show("用户名123456,密码123456,快去试试吧!"); } } }
由于这个博主实在是太懒了,登录窗体的背景什么的都没换,口中还不停说着什么“简约风”、“欧美流行风”等让人听不懂的词语。下面就是这个懒惰博主做的运行界面啦:
登录窗体内的一些彩蛋就自己去找着玩叭!
C#异常与调试
-
跨线程访问窗体导致异常
在.net 2.0以后加强了安全机制,不允许winform直接跨线程访问控件属性,因此一个窗体/控件等内容,通过a线程启用,那么除了a线程以外,其他线程均不可以更改该窗体的属性。
“有人想找你借钱,他可以直接在你的钱包中拿,这样太不安全,因此必须让别人先要告诉你,你再从自己的钱包把钱拿出来借给别人,这样就安全了”
“其他线程希望更改当前窗体/控件属性”这个需求是存在的,因此需要通过其他方式,来使得这个需求得到满足。
既然不能跨线程访问,那同线程访问就好了。其他线程(b、c、d…)希望更改a线程下的窗体属性时,不直接对a窗体属性更改,而是告知a线程,让a线程去做这件事就可以了。
//通知对应线程进行窗体更改处理 public delegate void UpdateValueChangeEventHandler(); private void UpdateValueChange() { //C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。 //InvokeRequired:当前线程不是创建控件的线程时为true if (InvokeRequired) { //推送到原线程去做 BeginInvoke(new UpdateValueChangeEventHandler(UpdateValueChange), null); } else { //更改窗体属性 lblUpdate.Text = "Changed"; } }
C#进程与线程
在操作系统中,每运行一个程序都会开启一个进程,一个进程又由一个或多个线程构成。
线程是程序执行时的最小单元。
本章将记录一些C#中的进程以及线程的使用。
-
C#Process
Process类是在
System.Diagnostics
中的。Process类就目前而言,我是用的比较少的,更多的会用到线程。这里就做一个简要介绍吧,后续用到进程类的内容再继续补充。
属性或方法 说明 MachineName 属性,获取关联进程正在其上运行的计算机的名称 Id 属性,获取关联进程的唯一标识符 ProcessName 属性,获取该进程的名称 Threads 属性,获取再关联进程中运行的一组线程 Close() 方法,释放与此组件关联的所有资源 Dispose() 方法,释放有Component使用的所有资源 GetProcesses() 方法,方法,为本地计算机上的每个进程资源创建一个新的 Process 组件 除上述的属性和方法外,Process类中还有其他的属性和方法,这里就不一一例举了。
下面我们使用GetProcesses()方法,来显示当前系统运行的所有进程。
... using System.Diagnostics; ... namespace ...//命名控件 { public partial class ... { public ...()//窗体 { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { richTextBox1.Text += (p.ProcessName + "\r\n");//窗体设计中记得加入"richTextBox"控件 } } } }
点击button后,在richTextBox控件中就会显示出当前系统中所有运行的进程了。
举一反三,使用其他的方法也能够实现根据进程名称启动进程、根据进程名称关闭进程。
-
C# Thread
线程(Thread)是包含在进程(Process)中的,它位于
System.Threading
命名空间中。与线程相关的一些类也在这个命名空间中。主要的一些类如下表所示。
线程相关的一些类表
类名 说明 Thread 在初始的应用程序中创建其他的线程 ThreadState 指定 Thread 的执行状态,包括开始、运行、挂起等 ThreadPriority 线程在调度时的优先级枚举值,包括 Highest、AboveNormal、Normal、BelowNormal、Lowest ThreadPool 提供一个线程池,用于执行任务、发送工作项、处理异步I/O等操作 Monitor 提供同步访问对象的机制 Mutex 用于线程间同步的操作 ThreadAbortException 调用Thread类中的Abort方法时出现的异常 ThreadStateException Thead处于对方法调用无效的ThreadState时出现的异常 这里主要讲述其中的Thread类,而Thread类主要用于实现线程的创建以及执行,它常用的一些属性和方法如下表所示。
Thread类相关的属性或方法表
属性或方法 说明 Name 属性,获取或设置线程的名称 Priority 属性,获取或设置线程的优先级 ThreadState 属性,获取线程当前的状态 IsAlive 属性,获取当前线程是否处于启动状态 IsBackground 属性,获取或设置值,表示该线程是否为后台线程 CurrentThread 属性,获取当前正在运行的线程 Start() 方法,启动线程 Sleep(int millisecondsTimout)() 方法,将当前线程暂停指定的毫秒数 Suspend() 方法,挂起当前线程(已经被弃用) Join() 方法,阻塞调用线程,直到某个线程终止为止 Interrupt() 方法,中断当前线程 Resume() 方法,继续已经挂起的线程(已经被弃用) Abort() 方法,终止线程 平时常用的方法就是Sleep()这个方法了,将当前线程暂停指定的毫秒数,常可用于程序调试(当程序过程发生的太快了)、控制刷新频率(不断刷新图片,模拟绘制动图时常用)等。