.Net Winform开发笔记(一)_C#教程

1. 理解“Windows 窗体应用程序”项目中Program.cs文件中的main方法与传统C++Console控制台程序中的main方法的区别。从程序运行层次上讲,两者无区别,都是程序的入口点,属于进程中的第一个线程。前者隐藏了UI应用程序必需的消息循环,后者没有。

2. 每个Windows桌面应用程序都必须包含至少一个UI线程,所谓UI线程,就是可以响应Windows消息的线程。通常情况下,除非特别需要,一个Windows桌面应用程序只包含一个UI线程。

3. UI线程本质上跟普通线程一样,一般为程序的入口线程,比如Program.cs文件中的main方法,就是UI线程,而Application.Run()方法中封装了消息循环。如果没有Application.Run()方法,那么它跟其他线程一模一样。之所以叫做UI线程,是因为它之中包含一个类似

复制代码 代码如下:

While(GetMessage(…))//取Windows消息
{
//处理windows消息,调用开发者编写的回调方法,如事件处理程序 等。
}

的循环。
4. 有关Windows消息机制等内容,请上网Google或者百度。

5. UI线程主要负责界面的实时更新,所以开发人员编写代码时,请遵守以下规律:
1) 不要在控件的事件处理程序中编写(或者调用)耗时的代码块;
2) 不要在控件的事件处理程序中调用阻塞方法;

6. 明白程序设计中的 委托、事件、事件处理程序的区别

复制代码 代码如下:

1) Publicdelegate void KeyPressEventHandler(KeyPressEventArgse);
2) Public eventKeyPressEventHandler KeyPress;
3) Public void Textbox1_KeyPress(objectsender,KeyPressEventArgs e)
{
//….
}

其中
1为委托 2为事件 3为事件处理程序

7. 所有的事件处理程序都是在UI线程中调用,又因为UI线程负责更新界面,所以UI线程始终必须保持顺畅(表现为3中的while循环体不能耗时太长),即不能出现长时间执 行一个方法不返回的情况。所以,请遵守5中的规律。

8. 同一个方法,可以运行在多个线程之中,方法跟线程没有一对一的原则

复制代码 代码如下:

Private void thread_pro() //
{
}
1) privatebutton1_click(object sender,EventArgs e)
{
thread_pro(); //thread_pro运行在UI线程中
}
2)private button1_click(object sender,EventArgs e)
{
Thread t = new Thread(newThreadStart(thread_pro));
Thread t1 = new Thread(new ThreadStart(thread_pro));
Thread t2 = newThread(new ThreadStart(thread_pro));
t.start(); //thread_pro运行在t线程中
t1.start(); //thread_pro运行在t1线程中
t.2.start(); //thread_pro运行在t2线程中
}

3) 还可以通过Control.Invoke() 或者BenginInvoke方法将方法投递到创建该控件的线程中执行。
以上所有情况,请注意线程共享数据。

9. 多线程编程中,请注意“线程安全”问题,对于一些具备“非原子”操作的对象,必须采取措施避免发生错误。

UI控件(Button、datagridview等等)、集合(List、ArrayList)等属于此类对象,控件任何时间都不能多线程访问。

10. 坚决杜绝跨线程访问UI控件,原因见9。跨线程访问控件的方法见8中的3)。

11. 除了.Net Winform中的事件处理程序是在UI线程中调用以外,其它的回调方法几乎所有都不会在UI线程中执行,所以,开发人员在编写回调方法时,请遵守第9,10两大规律。

12. 明白什么叫回调方法。回调方法一般由开发者编写,但不由开发者调用,由系统(或者说框架)调用。在Windows桌面应用程序开发过程中,控件的事件处理程序都属于回调方法,回调方法一般用在“观察者”设计模式中,当事件的激发者激发一个事件时,它就会调用回调方法。控件的所有事件都属于此类。
另外一种常见为,异步执行某个操作,譬如,socket.BeginAccept()中的AsyncCallBack类型参数。
在框架横行的时代,一般开发者编写的代码都属于回调代码。因为程序的主要结构都由先辈们在框架中集成好了。开发者们只需要像填空一样完善空缺的部分。

13. 阻塞方法指,由于方法体内包含耗时较长的操作,所以方法不能及时返回。
所谓“及时”与“非及时”没有绝对界限,示例如下:

复制代码 代码如下:

int func1() //及时返回
{
Int index = 0;
For(int i=0;i<100;i++)
{
Index ++;
}
Return index;
}
Int func2() //非及时返回
{
Int index = 0;
For(int i=0;i<1000;i++)
{
For(int j=0;j<1000;++j)
{
Index ++;
}
}
Return index;
}

上述func1相对而言,属于非阻塞方法,func2属于阻塞方法。

14. Windows窗体应用程序不会直接跟键盘、鼠标等硬件设备交互,它只与Windows消息有直接交互。虽然表面上鼠标键盘等硬件设备是操作在窗体之上的,但实质上,你 编写的桌面应用程序是不会理解这些硬件设备的一举一动。他们是通过操作系统(驱动程序)进行桥接的,操作系统先将硬件设备的一举一动翻译成windows消息(一种数据结构,程序可以理解),然后供程序理解,作出相应的反应。
15. 所谓“阻塞调用线程”,是指在某一个线程中调用了阻塞方法,从而使该线程不能及时执行以后的代码。

复制代码 代码如下:

Void func()
{
Int index=0;
For(int i=0;i<10000;++i)
{
For(int j=0;j<10000;++j)
{
I ndex++;
}
}
}
Thread t = newThread(new ThreadStart(func));
t.Start(); //线程t中调用了阻塞方法func,因此线程t会被阻塞

在介绍func方法时,可以这样描述:该方法会阻塞调用线程。

16. 同一个方法可以被多个线程调用,既可被UI线程调用,也可被非UI线程调用,那么在方法体内怎么编写访问UI控件(UI元素)的代码呢?(跨线程访问UI控件会引发异常)

复制代码 代码如下:

Void func()
{
Textbox1.Text=”测试”;
PictureBox1.Image = Image.FromFile(“a.jpg”);
}

1)以上func方法可能运行在UI线程中,如下:

复制代码 代码如下:

Private voidbutton1_Click(object sender,EventArgs e)
{
func(); //调用func方法
}

2)有如下,func方法可能运行在其他非UI线程中

复制代码 代码如下:

Private void button1_Click(object sender,EventArgs e)
{
Thread t = new Thread(newThreadStart(func));
t.Start(); //func访问运行在t线程中
}

在2)中,可能引发异常。
以上问题的解决方案为:
修改func代码为:

复制代码 代码如下:

Func()
{
If(this.InvokeRequired)
{
This.BeginInvoke((Action)delegate(){func()});
}
Else
{
Textbox1.Text=”测试”;
PictureBox1.Image = Image.FromFile(“a.jpg”);
}
}

有关BeginInvoke或者Invoke方法的使用,请上网Google或者百度。

17. 有关“跨线程访问UI控件可能引发异常”的原因,跟多线程访问集合可能出现错误的原因基本相似。下面列举一段代码说明情况

复制代码 代码如下:

ClassMyControl
{
Object root;
Public Draw()
{
GetRoot(root);
// 一系列操作…
ReleaseRoot(root);
}
Public OtherDraw()
{
GetRoot(root);
// 一系列操作 …
ReleaseRoot(root);
}
}

其中root变量同时只能被占用一次,GetRoot()获取root的访问权,如果root已经被占用,则抛出异常。ReleaseRoot()释放root占用。

当在一个线程中(比如UI线程中)访问MyControl类对象A,调用A.Draw()方法,执行到GetRoot(root)方法后,该线程失去控制权,暂停运行一下的代码,即此时root已被占用。而另一线程中如果也要访问同一对象A的Draw()方法,那么就会引发异常。

18.在.Net Winform应用程序中,程序与用户的交互主要包含两个方面,一是用户用鼠标、键盘灯硬件设备进行操作,程序响应操作,然后进行反馈(比如更新界面、刷新数据等),二是不需要用户用鼠标等硬件设备进行操作,程序自己自动进行反馈(比如QQ弹出新闻窗体、360弹窗等)。

第一种情况是我们所熟知的,比如用户用鼠标点击按钮(button1),程序则弹出一个MessageBox,我们在程序中是这样子写的:事件处理程序如下

复制代码 代码如下:

Private voidbutton1_Click(object sender,EventArgs e)
{
MessageBox.Show(“弹出对话框,或者其他操作”);
}

再来理一下这个过程,首先用户拿起鼠标点击button1,操作系统(鼠标驱动)会捕获这个事件,经过分析,操作系统得知用户点击的是哪个窗体(按钮)、点击的位置(坐标),点击类型(左键还是右键或者其他),以及其他信息,之后,将这些信息封装成一个类型(即windows消息)发送给创建该窗体(控件)的线程中的消息队列,之后,操作系统(鼠标驱动)就不在负责了。接着,UI线程从线程消息队列中获取该消息(注意:这个过程是一直存在的),分析消息,调用开发人员编写的一些回调方法,如button1_Click()方法,从而到达相应鼠标键盘操作的目的。从上面分析过程来看,再一次说明,程序是不会直接跟鼠标等硬件设备交互的,与它直接交互的只有Windows消息,而这个过程需要Windows操作系统起着重要作用。

第二种情况一般用在多线程编程中,当程序有耗时操作、或者需要一直监听等情况的时候,是不能放在UI线程之中的,这时候就需要另外开辟线程,在另外的线程中处理。这种情况中,另外开辟的线程有时候需要反馈跟用户一些信息,即更新UI界面或者弹出一个窗体等,这就涉及到跨线程访问UI元素的问题了,详见5和

19.以上代码部分均为现写的,可能出现拼写错误,包涵!
另外,请配合笔记(二)中的“DOT NETWinform应用程序运行结构图”阅读。

时间: 2024-08-31 01:59:37

.Net Winform开发笔记(一)_C#教程的相关文章

.Net WInform开发笔记(三)谈谈自制控件(自定义控件)_C#教程

末日这天写篇博客吧,既然没来,那就纪念一下. 这次谈谈自制控件,也就是自定义控件,先上图,再说 1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能 开发笔记(三)谈谈自制控件(自定义控件)_C#教程-winform自定义控件"> 2.重写ListBox,增加折叠.鼠标背影.分类等功能 -----------------------------分割线-------------------------------

.Net WInform开发笔记(二)Winform程序运行结构图及TCP协议在Winform中的应用_C#教程

中午没事,把去年刚毕业那会画的几张图翻出来了,大概介绍Winform应用程序运行的过程,以及TCP协议在Winform中的应用.如果有Windows消息机制等基础,很好理解这两张图. (1)Winform应用程序运行结构图 开发笔记(二)Winform程序运行结构图及TCP协议在Winform中的应用_C#教程-tcp ip协议 层结构图"> (2)TCP通讯协议在Winform程序中的应用示意图 熟悉整个程序的来龙去脉,编程的时候就会很轻松,不会云里雾里. 另附公司招聘面试题一份,用了几

.Net Winform开发笔记(四)透过现象看本质_C#教程

写在前面: 从一个窗体的创建显示,再到与用户的交互,最后窗体关闭,这中间经历过了一系列复杂的过程,本文将从Winform应用程序中的Program.cs文件的第一行代码开始,逐步分析一个Winform应用程序到底是怎样从出生走向死亡,这其中包括Form.Show()和Form.ShowDialog()的区别.模式对话框形成的本质原因.消息循环.Windows事件与.net中事件(Event)的区别.System.Windows.Form.Application类的作用.以及我之前一篇博客中(.N

.Net WInform开发笔记(五)关于事件Event_C#教程

我前面几篇博客中提到过.net中的事件与Windows事件的区别,本文讨论的是前者,也就是我们代码中经常用到的Event.Event很常见,Button控件的Click.KeyPress等等,PictureBox控件的Paint等等都属于本文讨论范畴,本文会例举出有关"事件编程"的几种方法,还会提及由"事件编程"引起的MemoryLeak(跟"内存泄露"差不多),以及由"事件编程"引起的一些异常. 引子: .net中事件最常用

C#微信公众号与订阅号接口开发示例代码_C#教程

本文实例讲述了C#微信公众号与订阅号接口开发示例代码.分享给大家供大家参考,具体如下: using System; using System.Web; using System.IO; using System.Text; using System.Web.Security; using weixin_api; public class wxgz_api : IHttpHandler { public void ProcessRequest(HttpContext context) { cont

那些年,我还在学习C# 学习笔记续_C#教程

那些年,我还在学习C#续 那些年学习C#,就是对C#相关的一些知识有一个了解,等到要用时才不会找不到方向,比如说扩展方法,开始时怎么觉得没有用,后来了解到asp.net MVC,它可以用来扩展Html类,比如做一个分页的方法:所以对一门语言了解宽一些是没有坏处的:C#中还有一些在上文中没有提到的,如:读取文件.网络(socket)编程.序列化等,他们都是非常重要的啊,下面再来看一看吧! 一.读取文件 在文件读取学习时,一般都会提到字节流与字符流,前都按字节读取,后都按字符读取:我们通过FileS

请问谁有winform开发的应用程序实例或教程也行

问题描述 请问谁有winform开发的应用程序实例或教程也行,最好是源码,VS2005+C#,谢谢啦 解决方案 解决方案二:http://download.csdn.net/source/267815csdn下载频道多的是啦解决方案三:这方面的中文书籍不多,你可以去看看外文的书:Manning.Publications.Windows.Forms.in.Action.2nd.Edition.Apr.2006.pdf电驴上有,这本我看过,还是不错的

使用VS2010 C#开发ActiveX控件(下),完整代码打包下载_C#教程

其实如果我们不进行设置,只是修改了代码,运行程序以后,其出错界面如下图1所示: 开发ActiveX控件(下),完整代码打包下载_C#教程-vs2010使用教程 c"> 图1 抛出异常如下: ************** Exception Text ************** System.MethodAccessException: Attempt by security transparent method 'Rare.Card.Libary.Controls. ReadCardCo

Android开发笔记之Intent初级学习教程_Android

本文讲述了Android开发笔记之Intent初级学习教程.分享给大家供大家参考,具体如下: 项目创建步骤: New Android Project-> Project name:Intent Build Target:Android 2.2 Application name:IntentDemo Package name:com.b510.intent.activity Create Activity:MainActivity Min SDK Version:8 Finish 1.拨打电话 按