文档章节

.Net Winform开发笔记(一)

IT周见智
 IT周见智
发布于 2015/06/05 17:17
字数 2543
阅读 14
收藏 1
点赞 0
评论 0

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线程,是因为它之中包含一个类似

 1 While(GetMessage(…))//取Windows消息

2 {
3      // 处理windows消息,调用开发者编写的回调方法,如事件处理程序 等。
4 }    

的循环。

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

5.  UI线程主要负责界面的实时更新,所以开发人员编写代码时,请遵守以下规律:

     1) 不要在控件的事件处理程序中编写(或者调用)耗时的代码块;

     2) 不要在控件的事件处理程序中调用阻塞方法;阻塞与非阻塞应参见13.

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.  所谓“阻塞调用线程”,是指在某一个线程中调用了阻塞方法,从而使该线程不能及时执行以后的代码。

 1      Void func()
 2      {
 3          Int index= 0;
 4          For( int i= 0;i< 10000;++i)
 5          {
 6               For( int j= 0;j< 10000;++j)
 7              {
 8                    I ndex++;
 9               }
10           }
11      }
12      Thread t = newThread( new ThreadStart(func));
13       t.Start();        // 线程t中调用了阻塞方法func,因此线程t会被阻塞
14  

在介绍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控件可能引发异常”的原因,跟多线程访问集合可能出现错误的原因基本相似。下面列举一段代码说明情况

 1    ClassMyControl
 2    {
 3        Object root;
 4        Public Draw()
 5        {
 6            GetRoot(root);
 7             //  一系列操作…
 8            ReleaseRoot(root);
 9         }
10        Public OtherDraw()
11       {
12           GetRoot(root);
13            //  一系列操作 …
14            ReleaseRoot(root);
15        }
16    }

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

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

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

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

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

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

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

以上代码部分均为现写的,可能出现拼写错误,包涵!

另外,请配合笔记(二)中的“DOT NETWinform应用程序运行结构图”阅读。

© 著作权归作者所有

共有 人打赏支持
IT周见智

IT周见智

粉丝 10
博文 61
码字总数 185891
作品 0
西青
Web开发系列 - GIS

Google Maps JQuery Maps google map是怎样工作的 Google Maps API编程资源大全 google map限制地图缩放级别和显示范围 WebGIS近来学习小结,GoogleMap影像在线矢量化简述 google map v3 ap...

长征2号 ⋅ 2017/08/09 ⋅ 0

圣殿骑士博文索引

“圣殿骑士”技术博客,书写自己对技术的理解。天道酬勤、坚持不懈! 圣殿骑士很荣幸入住博客园和51CTO写技术博客,目前主要在一家外资企业从事项目管理、技术架构及企业技术培训工作。由于工...

晨曦之光 ⋅ 2012/03/09 ⋅ 0

C# WinForm开发系列 - XML/XSLT/XPATH

XML在WinForm, Asp.Net, Java, AJAX开发等方面应用, 以及对XML文档基本操作,如XPath检索节点. 当前XML被广泛应用与配置保存, 简单数据存储, 跨平台数据传输, 其身影活跃于Web Services, AJA...

长征2号 ⋅ 2017/07/09 ⋅ 0

Winform开发框架之Office Ribbon界面

在前面几篇文章介绍我的Winform框架随笔文章,包括有《Winform开发框架之字典数据管理》、《Winform开发框架之权限管理系统》、《Winform开发框架之终极应用》,其中Winform开发框架之终极应...

长平狐 ⋅ 2012/06/11 ⋅ 0

Winform开发框架之Office Ribbon界面

在前面几篇文章介绍我的Winform框架随笔文章,包括有《Winform开发框架之字典数据管理》、《Winform开发框架之权限管理系统》、《Winform开发框架之终极应用》,其中Winform开发框架之终极应...

长平狐 ⋅ 2012/08/22 ⋅ 0

循序渐进开发WinForm项目(5)--Excel数据的导入导出操作

随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习...

walb呀 ⋅ 2017/12/04 ⋅ 0

Winform开发框架之混合型框架的实现

我在之前一篇文章《Winform开发框架之框架演化》中,介绍了传统Winform开发框架、传统WCF开发框架、离线式WCF开发框架、混合式WCF开发框架,其中前面两种就是大家比较熟悉的框架了,后面的离...

walb呀 ⋅ 2017/12/04 ⋅ 0

Winform分页控件最新版本发布,并提供基于DotNetBar界面的版本

该Winform分页控件很早就开发了,一直在我所有的共享软件项目中使用,并得到很多Winform开发者的热爱和支持,其中逐步完善了一些功能及优化。从较早时期的随笔《WinForm界面开发之“分页控件...

长平狐 ⋅ 2012/08/22 ⋅ 0

Winform分页控件最新版本发布,并提供基于DotNetBar界面的版本

该Winform分页控件很早就开发了,一直在我所有的共享软件项目中使用,并得到很多Winform开发者的热爱和支持,其中逐步完善了一些功能及优化。从较早时期的随笔《WinForm界面开发之“分页控件...

长平狐 ⋅ 2012/06/11 ⋅ 0

循序渐进开发WinForm项目(4)--Winform界面模块的集成使用

随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习...

walb呀 ⋅ 2017/12/04 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

两道面试题,带你解析Java类加载机制

在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Grandpa{ static { System.out.println("爷爷在静态代码块"); }} cl...

1527 ⋅ 13分钟前 ⋅ 0

SpringCloud(Data Flow)

dataflow-server

赵-猛 ⋅ 24分钟前 ⋅ 0

深入理解Java虚拟机

这本书我读到第8章,之后就是在读不下去了。 读到后面是一种痛苦的体验,太多的东西是不全面的,大量的专有名词是没有解释的,读到最后很多东西仅仅是一个侧面,所以我觉得,这本书不适合初学...

颖伙虫 ⋅ 29分钟前 ⋅ 0

B树和B+树的总结

B树 为什么要B树 磁盘中有两个机械运动的部分,分别是盘片旋转和磁臂移动。盘片旋转就是我们市面上所提到的多少转每分钟,而磁盘移动则是在盘片旋转到指定位置以后,移动磁臂后开始进行数据的...

浮躁的码农 ⋅ 32分钟前 ⋅ 0

NanoPi NEO core/ Ubuntu16.04单网卡配置3个IP地址(2个静态,1个动态)

配置 root@NanoPi-NEO-Core:/etc/network# cat interfacesauto loiface lo inet loopbackallow-hotplug eth0iface eth0 inet static address 172.31.188.249 netmask 255.......

SamXIAO ⋅ 58分钟前 ⋅ 0

三步为你的App集成LivePhoto功能

摘要:LivePhoto是iOS9新推出的一种拍照方式,类似于拍摄Gif图或录制视频片段生成图片。如果没有画面感,可以联想《哈利波特》霍格沃茨城堡的壁画,哈哈,很炫酷有木有,但坑爹的是只有iphone6S以...

壹峰 ⋅ 今天 ⋅ 0

centos7 git安装

由于centos中的源仓库中git不是最新版本,需要进行源码安装。 1、查看yum仓库git信息 [root@iZm5e3d4r5i5ml889vh6esZ zh]# yum info gitLoaded plugins: fastestmirrorLoading mirror s...

xixingzhe ⋅ 今天 ⋅ 0

input file 重复上传同一张图片失效的解决办法

解决办法 方法一:来回切换input[type='file']的type属性值,可以是‘text’,'button','button'....,然后再切换回来‘file’ 方法二:每次取消图片预览后,重置input[type='file']的value的...

时刻在奔跑 ⋅ 今天 ⋅ 0

Mahout推荐算法API详解

前言 用Mahout来构建推荐系统,是一件既简单又困难的事情。简单是因为Mahout完整地封装了“协同过滤”算法,并实现了并行化,提供非常简单的API接口;困难是因为我们不了解算法细节,很难去根...

xiaomin0322 ⋅ 今天 ⋅ 0

WampServer默认web服务器根目录位置

安装WampServer之后的web服务器根目录默认位置在WampServer安装目录下的www:

临江仙卜算子 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部