用C#实现简单的控件数组

2005/08/04 | 21:00 | 分类:Windows开发 | 标签: | 447次阅读

  我的一个同学在做计算器程序,另一个同学在做井字棋游戏。这两个程序有个共同的特点:包含数个具有同类功能的控件(计算器的数字按钮及井字棋的九个落子位)。如果一个个地创建这些控件,不得不写大量重复的代码,修改起来比较麻烦。一个更好的选择是建立控件数组。下面是Button数组的简单实现:

  1. Button[] btns = new Button[9];
  2.   private void ShowButtonArray()
  3.   {
  4.    for(int i = 0; i < 9; i++)
  5.    {
  6.     btns[i] = new Button(); //这一句往往为初学者忽视,须知要创建对象的实例!
  7.     btns[i].Location = new System.Drawing.Point(100 + 50 * (i % 3),100 + 50 * (i / 3));
  8.     btns[i].Name = "btnTest";
  9.     btns[i].Size = new System.Drawing.Size(48, 48);
  10.     btns[i].Text = i.ToString();
  11.     btns[i].Click += new System.EventHandler(this.btns_Click); //统一的事件处理
  12.     this.Controls.Add(btns[i]); //在窗体上呈现控件
  13.    }
  14.   }
  15.   private void btns_Click(object sender, System.EventArgs e)
  16.   {
  17.    MessageBox.Show(((Button)sender).Text + " was clicked !"); //通过sender判断激发事件的控件
  18.   }
  19.   private void Form1_Load(object sender, System.EventArgs e)
  20.   {
  21.    ShowButtonArray();
  22.   }

  事实上,大家只要看一遍“Windows 窗体设计器生成的代码”,就很快能理解.Net创建并呈现控件的过程,从而写出简单的控件数组。在上例中,Button呈现的位置是通过一个的公式计算出的,在实际的运用中,可以根据需要灵活变化(比如计算器的数字按钮,1~9可以用公式算出,0则可以用if之类的语句特殊处理)。同时值得注意的是对事件的统一处理:在btns_Click函数中通过sender判断激发事件的控件。
  如果有必要,可以将控件数组封装成类,加上一定的功能代码,便于灵活使用。甚至可以将一些自定义控件做成数组类,实现更加复杂的功能。

初学C++练习之作——生物节律计算

2005/07/25 | 22:22 | 分类:Windows开发 | 标签: | 559次阅读
  1. //
  2. // 生物节律计算
  3. //
  4. // 作者:林健 北京理工大学01110407班
  5. // Website:http://www.linjian.cn/ E-mail:yumenlj@126.com QQ:71424
  6. //
  7. // Version 0.02 2005/07/25 于甘肃玉门
  8. //
  9. // 初学C++练习之作
  10. // 在VC++6.0和gcc4.0下编译成功。
  11. // 请在 http://disk.linjian.cn 下载。文件名:biothythm.rar。
  12. //
  13.  
  14. ////////////////////// calendar.h //////////////////////
  15.  
  16. #ifndef CALENDAR_H
  17. #define CALENDAR_H
  18.  
  19. class calendar
  20. {
  21. private:
  22.  //以下函数计算日期差
  23.  //算法来自:http://search.csdn.net/Expert/TopicView3.asp?id=1720498
  24.  //对原作者表示感谢
  25.  int ymdTOdate(int vy, int vm, int vd); //主要函数:年月日化为自0001-1-1以来的天数
  26.  int IsLeapYear(int vy);
  27.  int MonthMaxDay(int vy, int vm);
  28.  int MonthTtlDay(int vy, int vm);
  29.  int* MonthMaxDayTable(int vy);
  30.  int* MonthTtlDayTable(int vy);
  31. public:
  32.  int year, month, day; //年月日
  33.  calendar(); //以1900-1-1构造calendar对象
  34.  calendar(int y, int m, int d); //以年月日构造calendar对象
  35.  int operator - (calendar& theold); //返回日期差
  36. };
  37.  
  38. #endif
  39.  
  40. ////////////////////// calendar.cpp //////////////////////
  41.  
  42. #include "calendar.h"
  43.  
  44. calendar::calendar()
  45. {
  46.  year = 1900;
  47.  month = 1;
  48.  day = 1;
  49. }
  50.  
  51. calendar::calendar(int y, int m, int d)
  52. {
  53.  year = y;
  54.  month = m;
  55.  day = d;
  56. }
  57.  
  58. int calendar::operator - (calendar& theold)
  59. {
  60.  int newy, newm, newd, oldy, oldm, oldd;
  61.  newy = this->year;
  62.  newm = this->month;
  63.  newd = this->day;
  64.  oldy = theold.year;
  65.  oldm = theold.month;
  66.  oldd = theold.day;
  67.  int newint = ymdTOdate(newy, newm, newd);
  68.  int oldint = ymdTOdate(oldy, oldm, oldd);
  69.  return newint - oldint;
  70. }
  71.  
  72. int calendar::ymdTOdate(int vy, int vm, int vd)
  73. {
  74.  if(vy<1 || vy>32767 || vm<1 || vm>12 || vd<1 || vd>MonthMaxDay(vy,vm))
  75.   return -1;
  76.  vd += MonthTtlDay(vy,vm)-1;
  77.  vy--;
  78.  return((vy*365 + vy/4 - vy/100 + vy/400)+vd);
  79. }
  80.  
  81. int calendar::IsLeapYear(int vy)
  82. {
  83.  return(((vy%4)==0) && (((vy%100)!=0) || ((vy%400)==0)));
  84. }
  85.  
  86. int calendar::MonthMaxDay(int vy, int vm)
  87. {
  88.  return(MonthMaxDayTable(vy)[vm-1]);
  89. }
  90.  
  91. int calendar::MonthTtlDay(int vy, int vm)
  92. {
  93.  return(MonthTtlDayTable(vy)[vm-1]);
  94. }
  95.  
  96. int* calendar::MonthMaxDayTable(int vy)
  97. {
  98.  static int mmd0[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  99.  static int mmd1[12]={31,29,31,30,31,30,31,31,30,31,30,31};
  100.  if(IsLeapYear(vy))
  101.   return(mmd1);
  102.  else
  103.   return(mmd0);
  104. }
  105.  
  106. int* calendar::MonthTtlDayTable(int vy)
  107. {
  108.  static int mtd0[13]={0,31,59,90,120,151,181,212,243,273,304,334,365};
  109.  static int mtd1[13]={0,31,60,91,121,152,182,213,244,274,305,335,366};
  110.  if(IsLeapYear(vy))
  111.   return(mtd1);
  112.  else
  113.   return(mtd0);
  114. }
  115.  
  116. ////////////////////// calculator.h //////////////////////
  117.  
  118. #ifndef CALCULATOR_H
  119. #define CALCULATOR_H
  120.  
  121. #include "calendar.h"
  122.  
  123. class calculator
  124. {
  125. private:
  126.  int days; //出生后天数
  127. public:
  128.  enum BIOTHYTHM //生物节律种类和周期
  129.  {
  130.   PHYSICAL = 23,
  131.   EMOTIONAL = 28,
  132.   INTELLECTUAL = 33
  133.  };
  134.  int GetRate(calculator::BIOTHYTHM type); //返回生物节律值
  135.  void SetDays(const calendar birth, const calendar ask); //设置生日与查询日
  136. };
  137.  
  138. #endif
  139.  
  140. ////////////////////// calculator.cpp //////////////////////
  141.  
  142. #include <cmath>
  143. #include "calculator.h"
  144.  
  145. int calculator::GetRate(calculator::BIOTHYTHM type)
  146. {
  147.  int cycle = (int)type; //生物节律周期
  148.  int mod = days % cycle; //出生后天数除以生物节律周期的余数
  149.  double percent = sin((double)mod / (double)cycle * 6.2832);
  150.  int result = (int)(percent * 1000); //返回值,换算成(-1000~+1000)的数
  151.  return result;
  152. }
  153.  
  154. void calculator::SetDays(calendar birth, calendar ask)
  155. {
  156.  days = ask - birth;
  157.  return;
  158. }
  159.  
  160. ////////////////////// start.cpp //////////////////////
  161.  
  162. #include <iostream>
  163. #include "calendar.h"
  164. #include "calculator.h"
  165.  
  166. using namespace std;
  167.  
  168. int main()
  169. {
  170.  calculator calc;
  171.  calendar Birthday;
  172.  calendar DayToAsk;
  173.  cout << "Input Birthday (YYYY MM DD) : ";
  174.  cin >> Birthday.year >> Birthday.month >> Birthday.day;
  175.  cout << "Input the day to ask (YYYY MM DD) : ";
  176.  cin >> DayToAsk.year >> DayToAsk.month >> DayToAsk.day;
  177.  cout << endl;
  178.  cout << "-----------------------------" << endl;
  179.  cout << " Birthday       : " << Birthday.year << "-" << Birthday.month << "-" << Birthday.day << endl;
  180.  cout << " The day to ask : " << DayToAsk.year << "-" << DayToAsk.month << "-" << DayToAsk.day << endl;
  181.  cout << "-----------------------------" << endl;
  182.  cout << "< BIOTHYTHM (-1000 ~ +1000) >" << endl;
  183.  cout << "-----------------------------" << endl;
  184.  calc.SetDays(Birthday, DayToAsk);
  185.  cout << " PHYSICAL       : " << calc.GetRate(calculator::PHYSICAL) << endl;
  186.  cout << " EMOTIONAL      : " << calc.GetRate(calculator::EMOTIONAL) << endl;
  187.  cout << " INTELLECTUAL   : " << calc.GetRate(calculator::INTELLECTUAL) << endl;
  188.  cout << "-----------------------------" << endl;
  189.  cout << endl;
  190.  return 0;
  191. }
  192.  
  193. ////////////////////// THE END //////////////////////

大一这一年的编程学习总结

2005/07/24 | 21:09 | 分类:学习随感 | 标签: | 664次阅读

  我在上大学之前,有一定的计算机学习基础。在编程方面,小学时自学过QBasic,中学时自学并简单地应用过VB6。我还做过网站,熟悉HTML,了解一点javascript。一定的编程思想基础是快速学好一门新的程序设计技术的必要条件。

  说到编程的基础,人们往往首先想到C语言。的确,C语言是一门基础而重要的课程,深入地学习好C语言是培养编程思想的重要途径。而就我个人来讲,我在大一这一年编程方面的学习顺序有点与众不同:在我们入学后,我的班主任金旭亮老师提出了他的一套“打破常规”的教学理念,引起了我班不少同学的兴趣。我们在金老师的指导下开始学习.NET。我有一定的基础,因此对程序语言(C#.NET、VB.NET)的理解很快。但我那时缺乏对面向对象设计方法和程序在计算机中运行机理的认识。我当时对面向对象(Object-Oriented)的理解就是“Control-Faced”,这大概是原先学习 VB6留下的印象。由于不清楚程序在计算机中运行的机理,我写的不少程序大量浪费内存,算法也比较笨拙。大一第二学期,学校开始上C语言课。对学校的教材、教法、考试方法,不满意的恐怕不只我一人:C语言之精华在学校的教育中被淡化,学生被引入一个个的牛角尖,良好的编程理念和习惯培养无从谈起。我主要靠自学,读了一些国外教材(《K&R C》、《深入理解计算机系统》),同时也简单地看了看C++(《Thinking in C++》)。通过这些基础学习,我澄清了很多原先学习VB6、.NET等方面的问题,对编程思想的认识有了进一步提高。总之,我在有的方面走了捷径,有的方面也走了一些弯路。但事实上,每个人都有适合自己的学习方法,我认为我们一方面要把握自己的实际(兴趣、基础等),另一方面一定要多听听别人,特别是本专业老师、学长的建议,少走弯路,高效地向真正对自己有益的技术方向迈进。

  再说说我对.NET的看法。.NET似乎是一个争议很大的事物。逛逛论坛,发现其狂热支持者和坚决抵制者真是针锋相对。就拿我们北京理工大学来说,金老师可谓.NET阵营的代表,他在学校大力推广.NET,开选修课、带兴趣小组、做项目,热火朝天。而soff老师(著名的珊瑚虫QQ作者)则对. NET不屑一顾,坚持自己的研发道路。他们的观点也各有其道理,在校内论坛上引起热烈的讨论。通过一年的学习以及与老师、同学的交流,我不敢下什么结论,但从自己的实际出发,我的看法是:学习计算机原理、编程思想基础,C、C++这条老路还是不错的,而要以.NET作为出发点似乎不太合适。.NET的确好上手,但如果缺少正确的导引,往往会给初学者在概念上、理论上以错误或不足的认识。要深入理解计算机系统,.NET这样高度托管化的体系更是不容易做到。但.NET的优势在于它高度的开发效率和它丰富的Framework类库。在实际工作中用.NET开发,为我们节约了大量的时间和精力。同时,作为一个新生的事物,.NET本身也在不断地自我完善中,相信Microsoft会在给它一个明确的定位的基础上,平衡技术与应用之间的关系。

  总结大一这一年我在编程方面的学习,有以下几点经验:通过多种途径(老师、同学、书籍、网络……)虚心学习,团队合作共同提高;理论与实践相结合,加速加强认知;认识到个人精力有限,不要想把什么都学会;选择学习一种编程技术,应当求其精髓,不要什么都碰,浅尝辄止;找到自己的兴趣与擅长,发现自己的潜能所在,学习适合自己的知识,不盲目跟风,不迷他人;注重知识的积累和联系。最后,最重要的是该干啥干啥,能干啥干啥,大学时间宝贵,切忌虚度光阴!

LynxFx软件开发文档

2005/07/23 | 09:13 | 分类:Windows开发 | 标签: | 464次阅读

.NET软件开发文档

软件名称

LynxFx

软件简介

  函数图象绘制软件。可方便快捷地绘制整式、分式、指数、对数、三角、双曲、反三角、反双曲等各类二维显函数图象,正确处理定义域和间断点。可以自定义坐标系和函数图象的各种属性。同一窗口中能打开多个坐标系,同一坐标系中能显示多个函数图象。能将函数保存为文件并导出为位图。

作者信息

林健 北京理工大学01110407班

Website:http://www.linjian.cn/ E-mail:yumenlj@126.com QQ:71424

版本信息

Version 1.11 2005/01/30 于甘肃玉门

开发说明

我最初开发这个软件是为了学习面向对象的一些特性。程序中的“坐标系”、“函数”都是类,有一些特定的属性和方法。另外我的支持鼠标定位、拖动、缩放等功能“绘图纸”本来是可以作为一个用户控件独立出来的,时间仓促,没有做。程序还调用了班主任金旭亮老师开发的数据存储层组件(DataAccessLayer.dll),事实上仅用到其中的一点功能(序列化),偷懒没有自己在程序中写这些代码。后来我对程序进行了完善,处理了各种可能出现的情况,增加了一些辅助功能。

 

程序用C#写成。绘制函数图象的核心问题是表达式计算。在绘制函数图象时正确处理定义域、间断点、无穷大值也是要特别注意的问题。

 

其他用到的技术会在后面详细说明。

使用方法

LynxFx软件开发文档

程序中可以看到一些在Windows中很常见的菜单项。点击“文件”—“新建”菜单,将新建一个坐标系子窗体。注意“文件”菜单中的“打开”、“保存”等操作,都是针对当前活动的坐标系进行的。可以将当前坐标系保存为本软件自定义的“*.cartesian”格式,以便日后查看;也可保存为位图文件,方便在其他地方使用。

LynxFx软件开发文档

LynxFx软件开发文档

新建坐标系窗体后,就可以在“f(x)=”之后的文本下拉框中输入函数了。点右边“添加函数”按钮或敲回车,函数图像将呈现在坐标系中。每个坐标系中可容纳多个函数图像。在下拉列表中选择特定的函数,可以点右边“删除函数”按钮删除之,或点“函数属性”按钮修改表达式和图像特征。同时,主窗体的工具栏按钮可以控制当前活动坐标系的位置、视野等。“坐标系属性”按钮提供对坐标系图像特征和行为的详细设置。

LynxFx软件开发文档

软件提供对函数表达式输入的帮助,可以随时查阅,并自动复制函数到剪贴板。表达式语法符合VBScript(部分函数为本软件扩充),具体使用方法可参阅《Microsoft VBScript 用户手册》之语言参考。

结构设计

这个软件主要由以下几个类构成:

FrmCartesian

坐标系窗体

Afunction

函数类

FrmCartesianAttribute

坐标系属性窗体

Cartesian

坐标系类

FrmFunctionInfo

函数属性窗体

 

 

FrmHelp

帮助窗体

 

 

FrmLogo

Logo窗体

 

 

FrmMain

主窗体

 

 

 

其中FrmMain是启动窗体,它是一个MDI容器,可以容纳多个FrmCartesian。FrmCartesian是坐标系窗体,包含对“绘图纸”的操作响应。FrmHelp是一个总在最前面的独立窗体,方便随时查阅帮助。其他各种属性窗体、Logo窗体均以Dialog形式呈现。

 

Afunction是函数类,实现了对一个函数的基本操作,如表达式分析,由表达式和自变量生成函数值等。Cartesian是坐标系类,记录一个坐标系中的所有函数,并可完成真实坐标系中的点与显示坐标系中的像素点转换等操作。

技术说明

这个软件整体上比较简单清楚,我就不对每个过程一一讲解了。以下几处我认为是有必要说明的:

 

●有关表达式计算我曾试过不少方法:1、自己动手写表达式解析算法:我当时还没有学习编译原理,上网找了一些资料试着写了,仅仅是处理括号就把我搞得晕头转向了,放弃。2、用别人的表达式计算组件:我在网上找到了几个表达式计算组件,可是它们要么收费、要么功能有限,放弃。3、用SQL:建一个数据库,在其中执行类似这种SQL命令“Update AnswerTable Set Answer = 1+(2-3)*4/5  Where ID = 1”,这是我在CSDN上得到的一个回复,这种方法似乎有点变态,放弃。4、用脚本语言:微软有个Script Control组件(MSSCRIPT.OCX),其中的ScriptControlClass可以直接运行vbscript。对于表达式计算,它有两种可行的方法,一是调用其Eval方法直接返回一个表达式的值,二是先用AddCode方法自己写一些脚本完成计算,再将结果用Eval方法返回。考虑到我的表达式计算中涉及变量与自定义函数,我选用了后一种方法,详见Afunction类。

 

●我用float.NaN表示的无定义的点。事实上float、double等基本数据类型下的一些特殊常数值是可以充分利用的,利用它们可以减少很多不必要的辅助变量。

 

●使用MDI窗体可能出现各种各样始料不及的问题,比如在子窗体最大化时新建一个子窗体会出问题;对于这个程序在子窗体最小化时,由于子窗体中的“绘图纸”显示域达到了一个极限小值,会引发问题;在子窗体之间切换时(尤其是使用快捷键切换)鼠标位置将立即地在各个窗体间变换,MouseEnter、MouseLeave、MouseDown、MouseUp、MouseMove等事件的发生顺序将与常规的情况不同。诸如这类情况我都作了处理,保证了程序的健壮性。

 

●FrmMain与FrmCartesian,FrmCartesian与FrmCartesianAttribute、FrmFunctionInfo之间传递数据用的是事件委托。这个机制比较有用,大家可以在源代码中仔细研究。

 

●FrmCartesian中绘图时,先在内存中的一个Bitmap对象上绘制,再将该Bitmap贴在屏幕上。这样做一是减少绘图时屏幕的闪烁,二是保证序列化保存图象时,即使屏幕上的图象被遮盖也能正确取图保存。

 

●FrmCartesianAttribute和FrmFunctionInfo中的颜色选择按钮完全可以作成一个用户控件(继承自Button,包装ColorDialog),这一工作留给感兴趣的朋友完成。

 

●ImageList控件的要求比较苛刻,每个ImageList里面所有图象的大小和颜色深度必须一致。特别是我们把*.ico的图标文件加入ImageList的时候,由于一个图标文件中可能存在多个格式的图象,这往往会被VS.net报告无效。所以在ImageList加入图标前最好将所有图标修改为统一的大小和颜色深度。

开发感悟

LynxFx与MathGV等专业的函数图象绘制软件相比还有一定的差距,表现在没有自己的表达式解析优化算法,用现成的vbscript效率不高,绘制复杂的图象(特别是不连续的函数图象)较慢等。但通过做这样一个小软件,我感觉到软件是需要细节打磨的。比如上面所说的“MDI窗体可能出现各种各样始料不及的问题”,为了程序的健壮性,都需要想办法一一解决,有时候为了解决一点小问题而不影响程序整体结构,还真得动一些脑筋。再如处理用户输入的数据一定要想到各种情况判断合法与否(比如一个字串是否是数字,这个数是否溢出)。再如打开或保存一个文件的时候文件名不对怎么办?有非法字符怎么办?权限不够、缺扩展名、文件损坏又怎么办?这些都要捕获并按照大家习惯理解的方式处理。只有解决了这些看似琐碎的问题,充分保证了程序的健壮性,用户感觉才比较专业,用起来才比较舒服。细节决定成败。

Easay软件开发文档

2005/07/23 | 09:08 | 分类:Windows开发 | 标签: | 438次阅读

.NET软件开发文档

软件名称

Easay

软件简介

  Easay是一款插件式多功能局域网双机交流软件。局域网上两台运行本软件的计算机分别作为服务器和客户端进行联接,可实现即时聊天;打开插件后可实现更多实用功能。目前版本提供“网络五子棋”和“电子白板”插件,可实现网上对弈(单机运行人机对战亦可)和网上图形交流。

作者信息

林健 北京理工大学01110407班

Website:http://www.linjian.cn/ E-mail:yumenlj@126.com QQ:71424

版本信息

Version 1.03 2005/02/11 于甘肃兰州

开发说明

这个软件是我学习局域网编程时做的,最初做的时候仅是一个试验性质的聊天程序(Easay就是Easy-say的意思)。做好后觉得没什么意思,恰好几天前阅读了金旭亮老师在《开发高手》2004年第6期上发表的文章——“基于组件的.NET软件开发”,于是产生了编写以局域网聊天程序为平台的插件式多功能交流软件的想法。

 

网络五子棋的游戏实现原理简单,很多人在学习网络编程时都练习这个项目。金老师的选修课的学生作业中就有不少人做网络五子棋。我一方面从网上得到了一些关于五子棋算法的资料,另一方面参考了他人的一些网络五子棋程序,取长补短,完善了我的网络五子棋插件。

 

而电子白板插件是我把原先做的一个试验性质的矢量绘图小程序直接移植上去的,功能与NetMeeting上那种专业的电子白板相比差距挺大的。

 

程序主要用C#写成。插件是一个个独立的DLL文件,可以用其他语言编写(电子白板就是用VB.net写的),只要继承EasayPlugin.FrmProgram类并实现指定的方法即可。程序中的EasayTestPlugin是一个最简单的插件实现的例子。有兴趣的朋友可以参照现有的插件开发自己的插件。

 

其他用到的技术会在后面详细说明。

使用方法

首先需要在两台可以通过IP直接访问的计算机上分别运行本软件,一台作为服务器,一台作为客户端。对于服务器,在Local IP下拉列表中选择与客户端在同一网络上的那个IP,点击“Create Server”按钮。客户端在Server IP文本框中填写服务器的IP,点击“Link to Server”按钮。如果一切顺利,两端的Action区域都会显示连接成功的提示。如果提示“Fail to link to the server.”,则可能是网络配置有问题或开启了防火墙,这时需要检查并调整网络或防火墙配置。如果只有一台计算机,也可以本地测试本软件的两个实例互联,IP为127.0.0.1。

Easay软件开发文档

连接成功后,可以在Communion区域发送聊天信息,用法和一般的聊天室相当。停止使用时,双方都点击“Disconnect”按钮即可。

 

点击“Open Plug-in”按钮可以打开并加载软件的插件。目前版本提供“网络五子棋”(EasayFive.dll)和“电子白板”(EasayPad.dll)插件。双方加载同一插件,即可实现网上对弈或网上图形交流。

 

五子棋的使用比较简单。右边下面三个按钮可以向对方发出开始或结束游戏的请求,游戏中聊天功能亦可使用。右边上方的“Play with computer”按下后,再提出游戏请求,将和电脑进行人机对战(该功能在非连接状态仍可使用)。

Easay软件开发文档

Easay软件开发文档

电子白板的使用与一般画图软件相似,只是双方共用一块画板,可以相互看到对方所画内容。

 

“Close Plug-in”按钮关闭当前插件;“Refresh Local IP” 按钮刷新本机IP列表。

结构设计

这个软件主要由以下几个工程和类构成:

主程序

Easay

主程序

FrmMain

主窗体

PluginWorks

动态加载插件的类

EasayPlugin

插件的基类工程

FrmProgram

插件的基类窗体

插件

EasayTestPlugin

示例插件

FrmProgram

插件窗体

EasayFive

网络五子棋插件

FrmProgram

插件窗体

ComputerPlayer

电脑玩家控制类

EasayPad

电子白板插件

FrmProgram

插件窗体

 

其中各种插件均继承自EasayPlugin.FrmProgram类。

技术说明

这个软件在结构和功能上有一些需要说明的地方:

 

●有关局域网连接和聊天:采用基本的套接字连接,用一个线程持续监听,这种经典的方法不必多说,任何讲网络编程的资料都会介绍这种基础方法,.NET中用现成的类就可以实现。程序通过网络传输文本串,以“MESS”开头表示聊天消息,“PROG” 开头表示程序指令。我对IP的处理比较细致,获知本机IP列出了本机所有IP(因为一台计算机可以同属多个网络:本地局域网、Internet、虚拟局域网、虚拟机的局域网……),同时加入了127.0.0.1这个连接本机的IP。对IP的正确性也用正则表达式去判断了。

 

●有关动态加载插件:推荐大家先去阅读金旭亮老师的“基于组件的.NET软件开发”一文(《开发高手》2004年第6期),里面有详细原理说明。即使你不清楚这些原理也不用怕,微软的工程师早已将这些技术封装在System.Reflection.Assembly类中,照我的程序的PluginWorks中的样子直接调用即可。实现的基本步骤是:先装入指定的组件代码库,再装入组件代码库中的类,随即创建一个该类的对象实例。

 

●有关插件的实现:各种插件均继承自EasayPlugin.FrmProgram类。EasayPlugin.FrmProgram类提供了GetInfo虚方法,在插件中重写该方法以得到网上连接的另一方发来的命令,完成指定任务。该类又提供了可直接调用的SetInfo方法,采用.NET事件委托机制,调用FrmMain的SendCommand函数向网上连接的另一方发送命令。

 

●有关网络五子棋的简单说明:五子棋的开发资料网上多的是,经典的人工智能算法是按双方在棋盘各点处获胜或出现“活三”的可能性种数的计分,选择高分点落子,攻防兼备。在数据传输方面,用不同的字母加数字表示不同的操作和操作者的角色(详见程序注释),用一串0、1、2表示15*15的棋盘上落子的情况(每次都传输整个棋盘似乎不太好,但这样看上去保险一些)。

 

●有关电子白板的简单说明:用GDI+实现矢量绘图比较简单,但用于网络程序就会出现新的问题:如果一方正在拖动鼠标绘图,另一方同时绘好了一个图形,发送绘图指令给前者,前者程序的Graphics对象就又会被收到的指令引发的函数调用。这样一来就会出异常。我的解决方案是建立一个ArrayList类型的缓冲区,记录在本机Graphics对象工作时累积收到的绘图指令,待本机Graphics对象完成本机用户的任务后读取执行。

开发感悟

正如“基于组件的.NET软件开发”一文中所说,“.NET下开发基于组件的应用软件之简便性与灵活性,为我们开发“拥抱变化”的软件系统提供了新的可能性。原来适用于源代码级别的一些面向对象特性,比如继承和多态,现在都可以推广应用到组件级别。”.NET大大提高了开发效率。做这个小软件来了解组件模式的程序架构设计,确实开拓了我的思路。不要小瞧了.NET,将它看作是简单的“拖控件,搭积木”。真正深入地学习与应用一门技术,才能发现它的精华所在,充分利用它提高我们的工作效率和理论水平。

页面存档: 上页 1 2 3 ...41 42 43 44 45 46 47 48 49 下页