2005年03月27日

也论”教学设计”与教学论
–与李秉德先生商榷   何克抗

一、  引言
 ”教学设计”与”教学论”是教育学领域中的两门不同学科,它们各自有不同的研究对象,而且属于不同的学科层次,这本来是很清楚的事情,多年以来在国内外学术界都没有发生过争论。但是近来,这个问题突然变得复杂起来、变得混淆不清了[1]。学术界对某些学术问题有不同的看法,原本是很正常的事情,不值得大惊小怪。但是,考虑到这个问题对于教育技术学专业与学科的建设具有极大的重要性,诚如著名教学论专家王策三先生所言:”对于一门学科来说,生死攸关的一个问题就是要明确自己的研究对象和任务”[2];加上提出这一问题的又是我国教学理论界举足轻重的李秉德老先生,所以我们就更有必要把这一问题讨论清楚,使大家尽可能取得比较一致的共识,以推动我国教育技术专业与学科建设的健康发展。否则,如果让这种混淆不清的状况持续下去,将会对我国的教育技术事业产生很不利的影响。
李秉德先生指出[1]:”本来,在不同学科之间存在’你中有我,我中有你’的现象并不是一件稀罕的事。不过,这种现象的出现有着绝然不同的两种类型。一种是正常的乃至必然的。……另一种’你中有我,我中有你’的现象却并非如此,它们的性质是低层次的重复和名词概念间的混同与歧义。本文所说的’教学设计’与教学论二学科间出现的现象即属于此种类型。这种类型的现象是不健康的。不应该令其长期存在下去。”
李秉德先生的意思很清楚:教学设计与教学论,是性质上的低层次重复和名词概念间的混同与歧义,在文献[1]的第一段末尾,李秉德先生还明确指出:”教学论与教学设计二者讨论的是同一对象”,即二者研究对象相同,因而这种现象是不健康、不正常的,是”不应该存在下去”的。
这个结论是否正确呢?如果正确,我们又该怎样做呢?尽管李秉德先生提出了”应该从现有教育学科群的实际情况出发,两个学科各自找好自己的位置,分工合作,携手共进,形成合力,共同为我国的教育现代化做出各自应有贡献”的解决问题思路,但是我认为,如果上述结论是正确的话,这样的解决问题思路是不可能实现的,也是完全没有必要的(尽管其愿望是良好的),因为它不合乎逻辑–既然二者性质重复,名词概念又混同,其必然的逻辑推论就是由后者去整合前者,或者是由前者去整合后者,二者必居其一。正如李秉德先生自己所言[1]:”对同一对象的论述不能长期地各自用各自的语言,而应该用教育学科通用的语言来表述。而且同一名词的概念不能用不同的含义来表述,否则就会引起学生或读者理解上和思路上的混乱,这对于指导教育教学的实践也是不利的。”李老先生的这段话就一般意义来说无疑是对的,可是,这样一来,教育理论界就要面临一个两难的困境–由谁来整合谁(用教学论去整合教学设计,或者相反)?以及如何进行这种整合?果真如此的话,恐怕没有长期的酝酿、讨论、协商是不会有结果的,甚至经过长期的酝酿、讨论、协商也不一定会有结果。怎么办?
幸亏这个困境的出现是以李秉德先生的上述结论能够成立为前提,而事实上这个结论是值得商榷的–它既未得到客观事实的支持,也未经过严密的逻辑论证。

二、李秉德先生的推理逻辑
如上所述,李老先生的结论认为:教学设计与教学论的研究对象相同,其学科性质和所用的概念也是彼此混同和重复。而这一结论的得出,只是根据李老先生提出的下列”事实”和由这些事实所做出的推理[1]:
1. 美国没有综述教学问题的现成”教学论”;
2. 我国在解放后逐渐形成了有自己特色的”教学论”;
3. 美国既无教学论可作为现代教育技术有效应用于教育教学的理论支柱,就使”教学设计”应运而生;又由于新技术应用于教育教学要考虑教学目的、条件、方法及评价,这就必然导致美国的教学设计与我国的教学论内容重复。
上述推理步骤实际上是包含两个逻辑推理过程的简约表达–一个是假言推理,另一个是直言推理。该假言推理的完整表达形式是:
如果没有教学论又要将现代教育技术有效应用于教育教学,就一定会发展出支持教育技术应用的新理论(大前提);
美国当前正积极运用教育技术且无现成的教学论(小前提);所以,美国一定会”应运而生”地发展出一种支持现代教育技术应用的新理论(结论)。
这样”应运而生”的新理论,在美国称之为”教学设计”。
另一个直言推理的完整表达形式是:
研究教学目的、教学条件、教学方法及教学评价等内容的学科是教学论(大前提);教学设计是要研究教学目的、教学条件、教学方法及教学评价等内容的学科(小前提); —
所以,教学设计必然与教学论内容重复(结论)。
应当说明的是,在李秉德先生的文章中虽未直接使用”教学设计与教学论内容重复”这一判断作为结论,但是,却已明确指出[1]:”二者讨论的是同一对象”,”它们的性质是低层次的重复和名词概念间的混同”,”(教学设计)……就必然与教学论内容重复”等。显然,这三个判断,尤其是第三个判断(主项被省略,只剩下谓项),和”教学设计与教学论内容重复”这一判断,不论从内容到形式,都是完全一致的。
既然李秉德先生的结论是依据上述两个逻辑推理得出的,那么,只要考察一下这两个逻辑推理是否正确,就可以判断该结论能否成立。
众所周知,一个逻辑推理是否正确必须具备两个条件[3]:一是推理的前提要真实,二是要符合逻辑规则。只要这两个条件满足了,推理就必然正确。正如恩格斯所指出的[4]:”如果我们有正确的前提并且把思维规律正确地运用于这些前提,那么,结果必定与现实相符。”
现在我们来看一看上面两个逻辑推理是否符合”前提真实”和符合”逻辑规则”这样两个条件呢?先看第一个,即假言推理。
由该假言推理的完整表达形式可见,其大前提属于充分条件假言判断,其小前提是一个直言判断,结论也是一个直言判断,所以这是一个”充分条件假言推理”,而且是肯定式充分条件假言推理。由肯定式充分条件假言推理的逻辑规则可知,在前提为真的条件下,只要小前提肯定了大前提的前件,那么结论就必然地肯定大前提的后件。由此可见,对于上述假言推理来说,”符合逻辑规则”这一条件是满足的,问题是”前提为真”这一条件是否也能满足?事实上,小前提中关于”美国无现成教学论”的判断并非是真实的(在本文的第三部分中将会对此提出充分证据),因而由这样的前提所推出的结论自然难以使人信服。

下面我们再来看上述第二个推理,即直言推理。
由该直言推理的完整表达形式可见,其大前提、小前提和结论都是直言判断,而且大小前提判断所涉及的真实性均不存在问题,即”前提为真”这一条件是满足的;问题是”符合逻辑规则”这一条件是否也能满足呢?
大家知道,在逻辑学中关于直言推理的逻辑规则共有四条[3],其中与中项周延性有关的一条是”中项在两个前提中至少要周延一次”。所谓”中项”是指只在大、小两个前提中出现,却不会在结论中出现的主项或谓项,上述直言推理的中项就是指”研究教学目的、教学条件、教学方法及教学评价等内容的学科”这一项;在一个直言判断中,若断定了主项或谓项的全部外延,该主项或谓项称作是”周延”的;若只断定了主项或谓项的一部分外延则称该主项或谓项是”不周延”的。所谓”中项在两个前提中至少要周延一次”是指:中项可以只在大前提中周延,或是只在小前提中周延,也可以同时在大、小两个前提中周延,但不允许在大、小两个前提中都不周延,如果出现中项在两个前提中都不周延的情况(即这条规则被违背)会怎么样呢?
由于中项在大前提和小前提之间起着桥梁作用,所以是直言推理的关键因素。中项若在大前提中不周延(即中项在大前提中只被断定了一部分外延),那么,在大前提中就只有一部分外延起作用;若是中项在小前提中也不周延,那么,在小前提中也只有一部分外延起作用。这样,尽管在大小前提中都和中项发生了关系,但是,大小前提是否通过中项外延的相同部分来发生关系呢?这就不一定。如果不是,中项就不能起到桥梁作用来保证直言推理的正确性。而在上面所说的直言推理中恰恰存在这个问题–中项在大小两个前提中都不周延。因为在大前提中,要研究教学目的、条件、方法及评价等内容的不仅有教学论这一个学科,还有教学法、教育心理学等学科(即中项在大前提中只断定了部分外延);在小前提中,要研究教学目的、条件、方法及评价等内容的也不仅仅是教学设计(中项在小前提中也只断定了部分外延)。换言之,在上述直言推理中违背了”中项在两个前提中至少要周延一次”的逻辑规则,而造成这种逻辑错误的根源则在于对教学论和教学设计这两个学科的内涵(即研究对象)未能作出严格的界定。
总之,在李秉德先生所赖以得出结论的两个逻辑推理中,第一个(假言推理)由于不符合”前提真实”的条件,其推出的结论不可能为真;第二个(直言推理)由于不符合直言推理的基本逻辑规则,当然也不可能保证推出结论的正确。可见,基于这两个逻辑推理所得出的结论,不一定是科学的、符合实际的结论。为了更准确地判定李老先生的结论是否能够成立,下面我们将对导致上述逻辑推理出现问题的两个重要因素进行认真的分析,这两个重要因素是:
* 导致前提不真实的关于”美国无现成教学论”的判断;
* 导致违背中项周延推理规则的关于”教学论和教学设计这两个学科的内涵界定”问题。

三、对李秉德先生推理逻辑有关因素的分析

1. 美国到底有无现成的教学论
关于这个问题,李秉德先生断然予以否认,他认为[1]:在美国”关于’教学’方面,则有’课程论’这个科目,包括内容很广,也有’教学方法’、’教育心理’、’学习心理’等,但是没有综述’教学’问题的’教学论’,像欧洲所讲的didactics(教学论)那样的东西。”李老先生非常肯定地作出了这个结论,但却没有提出任何事实或论据来加以证实,因而这个结论未免有武断之嫌。实际的情况是怎样的呢?下面我们拟从三个方面予以说明:
1)美国使用的”教学论”术语
从有关专著和文献资料可以看到,自20世纪50年代以来,美国教育界曾经使用过的、与教学论有关的名词术语有这样几个:”instructional theory”、”instructional theories”、”theory of instruction”、”theories of instruction”、”principles of instruction”、”theory of teaching”。这些术语都或多或少含有教学论的意思,但是又不完全相同。根据这些术语在有关文献中所处上下文的具体使用环境,不难将它们的确切含义区分如下:
instructional theory(或instructional theories)–多数情况下泛指各种教学理论;
theory of instruction (或theories of instruction)–多数情况下与我国的”教学论”内涵相近;
principles of instruction–多数情况下指教学原则或教学原理;
theory of teaching–也是泛指教学理论,但这里的”教学”比”instruction”的含义要窄一些,teaching特指”主要由一位老师来传播信息的教学”[5],一般是指以教为主的教学;而instruction则可包括所有的教学–既可以是以教为主的教学,也可以是以学为主的教学(例如教师指导下的探索式学习和协作式学习)。
应当指出,在美国文献中didactics一词是特指”教学法”,一般不会用它来表示”教学理论”或”教学论”。
下面我们再对theory of instruction(或theories of instruction)的含义作进一步的说明。由于美国有关文献中,对theory这个词的单复数在上述词组中的意义不加区别,所以在本文中也不加区别。根据美国著名教学专家R.M.加涅的观点(加涅的教学设计与教学论著作在美国当代有很大的影响,在国际上也有较高声望),theory of instruction的含义是指[6]:
 ”它试图把各种教学事件(教学活动)和学习过程、学习成果以及由学习理论所确定的有关因素联系起来。这种理论往往是规定性的–它力图确定用来优化学习(即优化知识保持和迁移)的各种教学条件。……它可以为教学过程与提高学生成绩、能力的教学结果之间提供一种符合因果关系的理论阐述。”
上述关于theory of instruction的含义有三个要点:
1 它是关于教学过程与教学结果的规律性探讨与阐述;
2 它试图阐明各种教学事件(即教学活动)和学习过程、学习成果以及学习理论之间的内在联系;
3 它属于规定性理论–因为它力图确定优化学习过程的各种教学条件。
(2)美国所用教学论术语和国内所用教学论术语的比较
现在我们再来看看国内对”教学论”是如何阐述的。目前国内最权威的教学论著作有三部:王策三先生的”教学论稿”、李秉德先生的”教学论”和王逢贤先生的”学与教的原理”。这三部著作中对教学论有以下三种说法:
王策三先生的说法是:”教学论应该为解决教学问题而研究一般教学规律;以研究一般教学规律来帮助解决教学问题。”(”教学论稿”,56页[2])
如上所述,加涅所阐明的关于theory of instruction的含义中包含三个要点,其中前两个要点(①和②)实际上是指要研究、探讨教学问题的本质和有关的规律,第三个要点(③)实际上是指通过确定优化学习过程的各种教学条件来帮助解决具体教学问题。可见,若将王策三先生的说法与加涅的说法加以比较,除了王的说法较为概括、简洁以外,两者的基本含义是很相近的(王策三先生说法的第一句和加涅说法的前两个要点意思相同,王的第二句则和加涅的第三个要点一致)。
再看李秉德先生的说法:”现代教学论的研究对象与任务在于探讨教学的本质与有关规律,寻求最优化的教学途径与方法,以达到培养社会所需人才的目的。”(”教学论”第8页[7])
将李老先生的说法和王策三先生的说法加以比较,不难看出,两种说法的第一句意思相同,第二句则是以”寻求最优化的教学途径与方法”来替代”以研究一般教学规律来帮助解决教学问题”。而这样替代以后,实际上更加接近加涅说法的第三个要点。如上所述,加涅说法的前两个要点(①和②)其含义和王策三先生说法的第一句意思相同,因而与李老先生说法的第一句意思也相同。这就表明李老先生的说法和加涅的说法从基本含义上看,也无二致,而且和王策三先生的说法相比,要更加接近加涅的说法。
王逢贤先生的说法则是:”教学理论是对教学活动规律的探索与研究,是对’教’的规律的描述;但相对于描述性理论的学习理论来说,教学理论在整体上属于规定性理论。”(”学与教的原理”24页[8])
将王逢贤先生的说法与加涅的说法加以比较,可以看出,分号前面的一段话与加涅的前两个要点的含义相同,分号后面的一段则和加涅的第三个要点几乎完全一样。尽管王逢贤先生在这里使用的术语是”教学理论”而非”教学论”,但联系其上下文不难看出,王逢贤先生所说教学理论的内涵正是教学论,只是因为要与”学习理论”相提并论,这里才采用了”教学理论”这一术语。
以上的分析有力地证明,美国教育理论界多年来所用的术语”theory of instruction” (或theories of instruction)从基本含义上看,和我国教育理论界自建国以来所用的术语”教学论”(有时也称为教学理论,如文献[8])是完全一致的,断言美国一直没有像中国那样的综述教学问题的”教学论”是难以令人信服的。下面我们还可以通过美国多年来在这一领域所发表的诸多著作来进一步加以证实。
(3)美国发表的主要教学论著作
下面只列举自20世纪60年代以来美国所发表的、在书名中涉及”theory of instruction”或其内容完全属于theory of instruction 的主要教学论著作(为了节省篇幅,下面所列只涉及教学论的专著或教材,而未包括发表在期刊上的大量有关”theory of instruction”的论文):
1. 布鲁纳在1966年发表的”关于教学论”( J.S.Bruner,Toward a theory of instruction, Cambridge, MA: Harvard University Press。国内也有学者把这本书译为”教学论探讨”[9])。
著名教育技术专家史密斯和雷根指出[5],布鲁纳的这部著作在美国被誉为”第一部阐述教学论特征的专著”。
2. C.H.帕特森在1977年发表的”教学论与教育心理学基础”( C.H.Pattorson, Foundation for a theory of instruction and educational psychology, University of Illinois Urbana )。
帕特森在本文中不仅论述了教学论的有关基础,而且论述了教学论在美国60-70年代的简要发展概况。
3. 布鲁纳在1979年发表的 “论教学的若干原则”(见《教育研究》1979年第5期)。
王策三教授认为[2],在这本著作中,布鲁纳较完整地阐述了自己的教学论思想–强调学习学科的基本原理,从小学开始螺旋式上升;主张”发现式”学习;并根据他对教学过程性质的理解提出了著名的教学四原则,即动机原则、结构原则、程序原则和反馈原则。
加涅在1985年发表的”学习的条件与教学论”( R.M. Gagne, The conditions of learning and theory of instruction )。
皮连生教授指出[10]:传统教学论以哲学心理学和教学经验总结为基础,而加涅则”在阐述学习条件的基础上,提出了一个教学论新体系”。
瑞奇鲁斯在1979年和1987年先后发表的”细化理论”(Elaboration Theory)和”教学理论应用”( C.M.Reigeluth, Instructional theories in action, Englewood Cliffs, NJ: Erlbaum )。
在这些著作中,瑞奇鲁斯第一次把教学策略划分为教学组织策略、教学传递策略、教学管理策略和激发学生动机策略等四种;对其中的教学组织策略又进一步区分为宏策略和微策略两类。在此基础上,瑞奇鲁斯提出了属于教学组织宏策略的”细化理论”,并对这种理论的基本内容及其在教学中的应用作了深入阐述[11] [12]。由于细化理论强调要按照学习者的认知特点和认知结构来组织教学内容,因而对教学系统设计具有重要的指导意义。 v除此以外,自20世纪60年代以来,在美国还有大量有关教学论的文章与专著以其他相关学科名称发表或是直接以作者所研究的具体课题名称发表,如布鲁姆的”教育目标分类学”和”教育评价”,布鲁纳的”教育过程”和”教育过程再探”,以及奥苏贝尔的”先行组织者教学策略”(发表在他的代表性著作”教育心理学–认知观点”一书中)……等等。这些论著虽未直接冠以”教学论”的名称或标题,但是又有谁能否认这些书的内容确实反映了教学论的基本内涵,而且是教学论领域的不朽论著呢!可见,认为美国多年来一直没有类似我国”教学论”那样的学科的说法,是与客观事实不相符的。
(4)否认美国有教学论的思想根源
我国教育界之所以有部分学者不承认美国有教学论,其根本原因在于思想方法有问题–把欧洲所用的教学论术语”didactics”照搬到美国:既然欧洲所用的教学论术语是didactics,就想当然地认为美国也应当如此,而未考虑不同国家和不同地域之间的语言文化背景所造成的差异。如前所述,在美国教育文献中虽然也经常使用didactics这个词。但一般是特指”教学法”,而不是指”教学论”或”教学理论”。事实上,同一个概念在不同国家或不同地区,由于语言文化背景的差异而用不同的术语来表达是常有的事;即使两个国家的语言完全相同,由于文化传统的差异也会出现这种现象。例如,同样的”教育技术”概念,在英国是用”Educational Technology”这个术语,而美国则喜欢用”Instructional Technology”(教学技术)这一术语。在美国教育技术界普遍认为这两个术语含义基本相同,但他们却更愿意使用后者而不是前者;在英国则刚好相反。在东方国家中也有类似情况:仍以上述概念Educational Technology为例,在日本称之为”教育工学”,而中国则称之为”教育技术”(在中国,一般人不习惯用”教学技术”这个术语)。可见,对于某个概念术语或学科名称的使用,不仅要看其外在的符号表达形式,更要重视其实际的内涵,才不致被事物的外部现象所迷惑。

2. “教学论”和”教学设计”这两个学科的内涵界定
(1)教学论的内涵界定
关于教学论的内涵,在上面讨论”美国有无现成教学论”的部分,我们已经对美国关于这一问题的代表性说法(加涅说法)和我国关于这一问题的代表性说法(王策三、李秉德和王逢贤等人的说法)作了分析比较,发现它们之间只是表述方式或叙述的详略有所不同,而其基本含义则并无二致,我们不妨从中选择一种说法来界定教学论的内涵(即作为教学论的定义)。由于加涅不仅在美国而且在全世界都享有较高的学术声望,而且其说法也较为全面,因此我们可以用加涅的说法为基础并综合国内的三种说法,最后作出关于”教学论”的定义如下:
 ”教学论是通过研究教学的本质与一般规律来寻求优化学习的各种教学条件与方法的学科。教学的本质与一般规律是指教学过程的基本性质以及教学过程与教学结果之间的因果关系,即各种教学事件(教学活动)和学习过程、学习结果之间的内在联系;由于要通过规律性的认识来确定优化学习的各种教学条件与方法,所以教学论属于规定性理论。”
这个定义的内涵所包括的基本属性有以下两个方面:
1 教学论的研究对象是教学的本质与教学的一般规律;
2 通过对教学本质与规律的认识来确定优化学习的教学条件与方法。
(2)教学设计的内涵界定
 ”教学设计”(Instructional Design,简称ID)也称”教学系统设计”,自60年代末以来,经过30多年的发展有第一代(ID1)和第二代(ID2)之分[13]。其中ID2的代表性模型一般认为是”史密斯-雷根模型”,它是由P.L.Smith和T.J.Ragan于1993年提出并发表在他们两人合著的”教学设计”一书中。目前该书在国际上有较大影响,所以该书对教学设计理论与方法的论述具有权威性(该书作者之一的T.J.Ragan在九十年代曾任美国AECT理论研究部的主席)。史密斯和雷根在该书中对”教学设计”所下的定义是[5]:
 ”教学设计是指运用系统方法,将学习理论与教学理论的原理转换成对教学资料和教学活动的具体计划的系统化过程。”
加涅在其著名的”教学设计原理”一书中,对教学系统设计(即教学设计)所下的定义则是[14]:
 ”教学系统设计是对教学系统进行具体计划的系统化过程。”
我们国内对教学设计的定义可以乌美娜教授主编的高等院校统编教材”教学设计”一书中所给出的定义为代表[15]:
 ”教学设计是运用系统方法分析教学问题和确定教学目标、建立解决教学问题的策略方案、试行解决方案、评价试行结果和对方案进行修改的过程;它以优化教学效果为目的,以学习理论、教学理论和传播学为理论基础。”
以上三个定义都强调教学设计是运用系统方法对教学进行具体计划的过程,这正是”教学设计”最本质的特征。三个定义的不同之处在于:加涅的定义未对具体计划的内容作任何说明,显得过于简略;乌美娜的定义则明确地规定计划的内容应包括教学目标、教学策略、解决方案和结果评价等,并指出教学设计的理论基础是学习理论、教学理论、传播理论(实际上还包括系统论);史密斯和雷根的定义也对计划的内容和理论基础作了适当说明,但比较概括,显得简明扼要。

综合上述三个定义的共性与个性,可以作出比较全面而概括的关于”教学设计”的定义如下:
 ”教学设计是运用系统方法,将学习理论与教学理论的原理转换成对教学目标(或教学目的)、教学条件、教学方法、教学评价……等教学环节进行具体计划的系统化过程。”
这个定义的内涵所包括的基本属性为:
1 教学设计的研究对象是用系统方法对各个教学环节进行具体计划的过程;
2 指导计划过程的主要理论基础(即教学设计的主要理论基础)是学习理论和教学理论。
由上面给出的教学论内涵和教学设计内涵的基本属性可见,二者都包含两个方面。其中第一个方面是:
教学论的研究对象是教学的本质与教学的一般规律;
教学设计的研究对象是用系统方法对各个教学环节进行具体计划的过程。
显然,这方面的属性二者有明显区别:教学论是研究教学本质与规律的理论性学科,而教学设计则是对各个教学环节进行具体设计与计划的应用性学科。根据这方面的属性,二者不应该混淆,也不可能混淆。
两学科内涵属性的第二个方面是:
1 教学论要通过对教学本质与规律的认识来确定优化学习的教学条件与方法(即要以教学理论作为理论基础来确定优化学习的条件与方法);
2 教学设计的主要理论基础是学习理论和教学理论。
可见,两学科内涵属性的第二个方面有某种共性–都强调要以教学理论为基础。所不同的是:
1 教学设计的理论基础不仅强调教学理论而且还强调学习理论;
2教学设计要在理论指导下对各个教学环节(教学目标、教学条件、教学方法、教学评价……等)进行具体的设计与计划,而教学论只是依据理论来确定优化学习的教学条件与方法。
仅就”确定优化学习的教学条件与方法”而言,它和”对各个教学环节进行具体的设计与计划”之间也有同有异。共同之处是,二者的目的相同–都是为了优化学习(即促进知识的保持与迁移);不同之处是,前者只确定优化学习的条件和方法,而后者则要进一步确定优化学习的具体计划和实施步骤,即后者要比前者更具体化,更具有可操作性。
以上分析表明,教学论是研究教学的本质和教学一般规律的理论性学科,同时它要通过对教学本质与规律的认识确定优化学习的教学条件与方法,即要联系实际,要用它来指导解决实际的教学问题,所以这种理论是属于规定性的而不是描述性的;而教学设计本身并不去研究教学的本质和教学的一般规律,它只是在教学理论、学习理论指导下,运用系统方法对各个教学环节(教学目标、条件、方法、评价……等)进行具体的设计与计划,换句话说,它是介于教学理论、学习理论与教学实践之间的桥梁或中间环节。在美国往往把教学设计称之为”桥梁学科”(理论与实践之间的桥梁),其道理就在于此。
可见,教学论和教学设计二者的研究对象完全不同,学科层次也不同(教学论属于较高理论层次的学科,而教学设计在学科层次上要比教学论低一级);虽然二者也有某些相似之处(例如,二者都是为了优化学习,二者都要涉及各个教学环节,如教学目标、教学条件、教学方法、教学评价……等),但如上所述,二者研究的对象和研究的方式是不同的,所以不会混同。
而李秉德先生却抓住教学论和教学设计这两个学科内涵中的非本质属性(如二者都要涉及教学目标、条件、方法、评价……等内容),却抛开二者研究对象不同这一根本属性,并以上述非本质的属性作为大、小前提进行直言推理,这就不可避免地要违背中项周延的推理规则;加上在前面的假言推理中又使用了未能证实为真的小前提,因而,最后根据这个复合推理(假言推理+直言推理)所得出的结论–关于教学设计必然与教学论内容重复的结论是站不住的,没有根据的。 在美国,教学论和教学设计的学科内涵界定历来是很分明的,在这两门学科之间没有发生过任何概念上的纷争或混淆,应该说这在很大程度上应归功于加涅的工作。加涅不仅是美国当代最有影响的教学设计专家,而且也是享有盛名的教学论专家。他一方面在有关论文和专著中给出了”教学论”和”教学设计”的科学定义(参看上面所述),为教学论和教学设计的学科内涵界定作出了贡献;另一方面他又在自己的两部代表性著作中最充分、最完整地体现了教学论和教学设计这两门学科的内涵。众所周知,加涅一生著作颇丰,而他最重要的代表作则是”学习的条
目标分类)的基础上形成自己独树一帜的教学论新体系;后一本是他的教学设计代表作–全书分四大部分,除了第二部分因简介教学设计的理论基础而与其前一本书(教学论代表作)略有重复以外,其余三部分都是体现教学设计学科自身内涵的全新内容,与教学论没有任何雷同之处。其中第二部分之所以有重复,完全是为了便于学生学习的需要,而并非教学设计学科内容的需要。换句话说,若把第二部分拿掉,并不会影响该书体系结构的完整性。总之,对于加涅来说(或者推而广之,对于整个美国教育理论界来说),从来就不存在教学论和教学设计发生混淆或重复的问题。
事实上,不仅在美国,就是在我国教学理论界,对于教学论和教学设计,在此之前也从来没有混同过。著名教学论专家王策三先生早在1985年就曾经指出[2]:”教学论和教学实践之间,是有着一定距离的,而不是直接联系的。它对教学实践的指导和调节作用是通过教学法及其它中间环节而实现的。……不能像要求教学论那样要求教学法;也不能像要求教学法那样来要求教学论。如果混淆不清,就会两败俱伤。……对于教学论来说,必须坚持研究教学的一般规律,必须不断提高其抽象概括水平。”
王策三先生在这段话中,虽然只提到”教学法”,而未提到”教学设计”,但是,由于教学法和教学设计二者是性质相同的学科(二者的研究对象相同,学科层次也相同),只是来源和理论基础有所不同而已(教学法在解放初由前苏联引进,较重视教学经验和教学理论的指导作用;教学设计则在80年代中期由美国引进,不仅重视教学理论而且更重视学习理论的指导作用),因此把王策三先生这段话中的”教学法”换成”教学设计”,或是换成”教学法与教学设计”应不会改变其原意。实际上,王先生在上面这段话中所使用的术语–”教学法及其它中间环节”,就已经包含这样的意思,这里的”中间环节”就是指”教学设计”。任何一位客观的读者都可以从王策三先生上面这段话中明确地读出下面的信息:
* 教学论与教学法(或教学设计)绝不能混同,否则会两败俱伤;
* 教学论必须坚持研究教学的一般规律以提高其抽象概括水平。
一言以蔽之,可能混同的只是教学法与教学设计,而绝不是教学论与教学设计。虽然只有一字之差,却谬之千里。
参考文献
1. 李秉德,”教学设计”与教学论,电化教育研究,2000年第10期。
2. 王策三,教学论稿,人民教育出版社,1985年6月。
3. 何名申,逻辑,云南人民出版社,1983年1月。
4. 恩格斯,马克思恩格斯全集,第20卷,第661页。
5. P.L.Smith, T,J.Ragan, Instructional Design, Macmillan Publishing Company, 1993.
6. R.M.Gagne, W.Dick, Instructional Psychology, Annual Review of Psychology, 34.
7. 李秉德,教学论,人民教育出版社,1991年9月。
8. 王逢贤,学与教的原理,高等教育出版社,2000年7月。
9. 汪刘生,教学论,中国科学技术大学出版社,1996年4月。
10. R.M.Gagne, 学习的条件和教学论,华东师范大学出版社,1999年11月。
11. C.M. Reigeluth, In Search of a Better Way to Organize Instruction: The Elaboration Theory, Journal of Instructional Development, 1979/2(3).
12. CM. Reigeluth, Instructional Theories in Action, Englewood Cliffs, NJ:Erlbaum, 1987.
13. 何克抗,教学设计理论的新发展,中国电化教育,1998年第10、11、12期(连载)。
14. R.M. 加涅等著,教学设计原理,华东师范大学出版社,1999年11月。
15. 乌美娜主编,教学设计,高等教育出版社,1994年10月。

2005年03月26日

如何提高VFP应用软件的路径适应性
在使用各种商品化的应用软件时,发现部分软件,可以将它安装在硬盘的任何盘的任何目录中;而有的软件,则只能安装在设计者事先规定的特定盘和特定路径中。假如将它放在了另外的盘号和路径,就会出现”XXXX数据库找不到”等等之类错误,从而中断软件的运行。本文从VFP语言的编程特点出发,介绍了提高VFP应用程序路径适应性的具体实现方法。
1. 在VFP应用软件的项目中,有一个主文件,它是整个应用程序的运行起始点。在主文件的开头处,插入以下语句:
A.将该软件的目录(安装后目录),设置为当前目录
SET DEFAULT TO SYS(5)+SYS(2003)

B.从.INI文件或路径数据表中读取用户指定的该软件下的子目录名(.INI文件或路径数据表的内容,待软件安装后,根据软件下的子目录名,由用户输入),例如B.1。若软件设计时确定了子目录名,则执行B.2的语句。
B.1 这里以”路径数据表”为例,说明读取路径的过程。
USE PGPATH
MPATH=‘‘
LOCA FOR PH#‘ ‘ &&搜索字段PH(路径)不为空的记录
DO WHILE .NOT.EOF()
MPATH=MPATH+TRIM(PH)+‘,‘ &&连加成搜索路径
SKIP
ENDD
MPATH=LEFT(MPATH,1,LEN(MPATH)-1)

B.1 若子目录名固定,则将各子目录名赋给MPATH,例: 两个子目录名为
DATA1,DATA2,DATA3则:
MPATH=‘DATA1,DATA2,DATA3‘

C.设置搜索路径
SET PATH &MPATH.
D.打开各数据库
OPEN DATABASE 数据库1
OPEN DATABASE 数据库2

2. 在主程序文件以外的程序中,作如下处理:
在这些程序中,以上设定的搜索路径仅由项目内的自由表使用,打开自由表时,不要使用路径。对于只包含单个数据库的项目,数据库中的表、视图等可以通过以下语句来使用,无须再使用路径。
OPEN DATABASE [数据库名]
USE [表名]
而对于包含多个数据库的项目,在使用其内的表、视图等对象时,采用:
USE 数据库名!表名


3. 若要求应用程序在用户指定的目录中新建文件,则可以通过以下VFP函数来实现:
=PUTFILE([cCustomText] [, cFileName] [, cFileExtensions])
运行此函数,出现一个对话框,让用户选择文件(cFileName).( cFileExtensions)的存放目录。

4. 对VFP数据环境的分析。
报表、表单等设计时,数据环境内各个对象(如表、自由表、视图等)的CursorSource属性中包含自由表的绝对路径,以及Database属性中包含数据库的绝对路径。并且这二个属性都是只读的,无法修改。然而,在编程实践中发现,这二个属性值在项目连编时,都转化为相对于项目文件所在位置的相对路径。从而为设计路径通用性程序成为可能。这一点可通过查看使用了数据环境的各类文件(如:表单的 *、SCT,报表的 *、FRT)来证明,因此,在设计数据环境时,对数据对象,无须考虑路径的相对性。而且,数据环境中的数据库也是按SET PATH设定的顺序查找的。
5. 项目文件中的数据库
首先,要将它们标为”排除”。笔者,在项目”连编”前,有意改变它的数据库目
录,使”项目连编”时找不到数据库,当出现”找不到数据库”等错误信息时,选择 “忽
略”,连编后的应用程序,照样可使用。因此,连编后的应用程序中,不固定项目内(已
标为”排除”的)数据库的路径,也不必考虑路径问题。
6.最后要注意,在打开项目的环境下,调试运行主文件(整个应用程序的运行起始点)
时要将 SET DEFAULT TO SYS(5)+SYS(2003) 改为
SET DEFAULT TO [(设计环境中)连编后的应用程序存放的绝对路径]
因为设计项目时 SYS(5)+SYS(2003)得出的不一定是[连编后的应用程序存放的
绝对路径],如:设计项目时是先运行VFP系统,再打开项目文件,则得出的路径是
VFP系统的安装目录。

如何使用_Screen对象

VisualFoxPro中充分利用_SCREEN对象,就能对主窗口进行各种操作,如可以动态地改变主窗口的图标、标题、颜色等,也可以通过它引用其它的对象或属性。
   _SCREEN对象(即指主窗口)是从表单类派生出来的,表单中的部分属性、事件和方法在_SCREEN对象也存在,因而可以把主窗口当作表单来操作(但也有所不同,如在_SCREEN中不能像表单那样对LOADACTIVATE等事件添加代码,即不能够为_screen指定事件)
   下面举例说明如何利用_SCREEN对象:
  1.更改图标和标题
   VFP窗口的小狐狸图标和“MicrosoftVisualFoxPro”标题,改为你所喜欢的图标和标题,只需执行代码
   _SCREEN.ICON=″你的图标文件名(.ICO)″
   _SCREEN.CAPTION=″你的标题名

如果需要修改的东西很多,大家也可以用with…endeith进行设定,如:

with _screen
.visible=.t.
.closable=.t.
.controlbox=.t.
.maxbutton=.t.
.minbutton=.t.
.movable=.t.
.top=0
.left=0
.windowstate=2
.caption=’
建设银行计算机台帐管理系统
.picture=’zhj6.bmp’
.icon=’support.ico’
endwith
  2.动态地改变主窗口的图标和标题

   即让主窗口的图标和标题随时间的变化而变化。这就需要在_SCREEN中加入一个Timer类,Timer类是每经过一定的时间(Interval属性中设定,单位为毫秒)循环地执行其Timer事件中的代码。方法如下:先定义你的Timer类:点文件/新建//新建文件,这时弹出一窗口,上面有这几项需要填写:类名项填新建类名(如MyTimer),派生于项选Timer来源于项不用填,存储于项填新建类库名(如MyClass)。确定后出现类设计器(很像表单设计器),上有一小闹钟(Timer控件)把其Interval属性设为1000(时间间隔,毫秒),在Init事件中输入代码:
   PUBL NN
   NN=0&&NN为变化的标志变量

   Timer事件(每隔一定时间执行)输入:
   _SCREEN.ICON=IIF((1)^NN,″所需加入的图题1″图标2″)
   _SCREEN.CAPION=IIF((1)^NN,″所需加入的标题1″标题
2″)
   NN=NN
1
   关闭并保存,类建立完毕。

   可视地把该类添到_SCREEN中:由于_SCREEN与表单有所区别,故不能用表单设计器工具来完成拖放类定义,而是用类浏览器来完成,寻工具/类浏览器后,弹出一打开窗口,选择刚才建立的类库文件(MYCLASS),确定后,弹出类浏览器窗口,选中MyTimer类,这时在类浏览器的左上角有一小闹钟,把它拖放到主窗口中,你就会惊奇地发现你的主窗口不再是死气沉沉的了。不信?试试!
  3.利用_SCREEN得到主窗口内包含的表单数量
   执行:?_SCREEN.FORMCOUNT即可
4.
_screen中加入对象(如:图画),举例如下:
_screen.addobject(“imge1″,”quit1″) &&
建立一副图画
define class quit1 as image
top=-10
left=0
*height=_screen.height-50
*width=_screen.width-50
picture=’main_bmp.bmp’
backstyle=0
visible=.t.
* stretch=1

procedure click
quit
endproc 
enddefine
基于这个例子,上面第2个问题中加入timer类的问题可以这样解决:
_screen.addobject(“tmr1″,”caption1″) &&
建立一个timer
define class caption1 as timer1
Interval=1000
procedure init
PUBL NN
    NN=0&&NN为变化的标志变量

endproc

procedure timer
_SCREEN.ICON=IIF((
1)^NN,″所需加入的图题1″图标2″)
   _SCREEN.CAPION=IIF((1)^NN,″所需加入的标题1″标题
2″)
   NN=NN
1
endproc

enddefine
以上是有关_SCREEN对象的简单阐述,想要你的窗口美化起来,具体的工作还需要你自己去做。

在VisualFoxPro中充分利用_SCREEN对象,就能对主窗口进行各种操作,如可以动态地改变主窗口的图标、标题、颜色等,也可以通过它引用其它的对象或属性。
_SCREEN对象(即指主窗口)是从表单类派生出来的,表单中的部分属性、事件和方法在_SCREEN对象也存在,因而可以把主窗口当作表单来操作(但也有所不同,如在_SCREEN中不能像表单那样对LOAD、ACTIVATE等事件添加代码)。
下面举例说明如何利用_SCREEN对象:

1更改图标和标题
把VFP窗口的小狐狸图标和“MicrosoftVisualFoxPro”标题,改为你所喜欢的图标和标题,只需执行代码 _SCREEN.ICON=″你的图标文件名(.ICO)″
_SCREEN.CAPTION=″你的标题名″

2动态地改变主窗口的图标和标题
即让主窗口的图标和标题随时间的变化而变化。这就需要在_SCREEN中加入一个Timer类,Timer类是每经过一定的时间(在Interval属性中设定,单位为毫秒)循环地执行其Timer事件中的代码。
方法如下:先定义你的Timer类:点“文件/新建/类/新建文件”,这时弹出一窗口,上面有这几项需要填写:“类名”项填新建类名(如MyTimer),“派生于”项选Timer,“来源于”项不用填,“存储于”项填新建类库名(如MyClass)。确定后出现类设计器(很像表单设计器),上有一小闹钟(Timer控件)把其Interval属性设为1000(时间间隔,毫秒),在Init事件中输入代码:
PUBL NN
NN=0&&NN为变化的标志变量
在Timer事件(每隔一定时间执行)输入:
_SCREEN.ICON=IIF((-1)^NN,″所需加入的图题1″,″图标2″)
_SCREEN.CAPION=IIF((-1)^NN,″所需加入的标题1″,″标题2″)
NN=NN+1
关闭并保存,类建立完毕。
可视地把该类添到_SCREEN中:由于_SCREEN与表单有所区别,故不能用表单设计器工具来完成拖放类定义,而是用“类浏览器”来完成,寻工具/类浏览器”后,弹出一打开窗口,选择刚才建立的类库文件(MYCLASS),确定后,弹出类浏览器窗口,选中MyTimer类,这时在类浏览器的左上角有一小闹钟,把它拖放到主窗口中,你就会惊奇地发现你的主窗口不再是死气沉沉的了。

3利用_SCREEN得到主窗口内包含的表单数量
执行:?_SCREEN.FORMCOUNT即可

4利用_SCREEN得到打开的表单名
可执行:?_SCREEN.FORMS(1).NAME&&FORM(1)为数组

如何从表单返回一个值
可以在表单的Unload事件中用来RETURN命令返回一个值。

如何检查变量是否存在
使用TYPE()函数来检查一个变量是否存在。
IF TYPE(“Var”) = .t.
    MESSAGEBOX(“It exists”,0,”")?
ENDIF
或你可以检查是否TYPE(“Var”)=”U”,若是,则变量不存在。

如何把数组作为参数传递给表单
在FoxPro中,数组必须按址传递。SET UDFPARMS决定参数的传递方式。在默认情况下,当你用DO…WITH调用过程或表单时,参数是按址传递。在函数或方法中,参数是按值传递。
DIMENSION array[10]
DO FORM Form1 WITH array
在这种情况下,你必须用一个”@”符号强迫参数的传递为按址传递。
DIMENSION array[10]
MyForm = CREATEOBJECT(“Form1″, @myarray)

为什么退出VFP时提示“不能退出VFP”
使用ON SHUTDOWN命令。

如何同时设置多个对象的同一属性
用SetAll方法。例如,你决定为全部网格中的标题设置9号宋体。将以下代码放入网格的Init事件:
THIS.SETALL(“FontName”,”Arial”,”Header”)

Wait命令的应用
Wait命令应用很方便,我在VB,DELPHI等语言中就始终没有找到与其功能 相似的语句,
可是我们在VFP中使用时常常只是用到了它的部分功能,而忽略了 一些很有用的功能。 Wait
命令也有不少子句:
WAIT
  [cMessageText]
   [TO MemVarName]
   [WINDOW [AT nRow, nColumn]]
   [NOWAIT]
   [CLEAR | NOCLEAR]
   [TIMEOUT nSeconds]
比如我们的使用常常只是:
WAIT WIND ‘暂停…’
使’暂停…’这两个字停留在其默认窗口的右上角,其实完全可以用 [AT nRow, nColumn]来改
变其停留位置:
WAIT WIND ‘暂停…’ AT 10,30
使’暂停…’停留在指定的第10行,第30列上。
WAIT WIND ‘暂停…’ +CHR(13)+’另起一行…’ AT 10,30
在提示信息中加上CHR(13),提示信息可分行显示。 而:
WAIT WIND ‘暂停10秒…’ TIMEOUT 10
则可在无击键和鼠标移动的情况下只暂停10秒。




如何制作WINDOWS格式的更改日期的程序
制作一个WIN9X格式的日期设置程序对于你自己的软件,除能够起到美化程序界面,而且能够提高程序的易用性,主要步骤如下:
1、首先建立一个表单,在表单的INIT事件中要首先设置一个判断星期的循环程序。
2、在表单中加入年微调控件,编写该控件的InteractiveChange事件过程,主要是编写当年变动的时候,要刷新月中每天的星期。
3、加入月下拉选项框,进行月份选择,同样要编写该控件的InteractiveChange事件过程,主要是编写当月变动的时候,要刷新月中每天的星期。
4、加入Grid控件,以选择日期。
实例文件下载

如何将金额由小写变为大写
1、编写如下函数。该函数的返回值能够舍去数字之间的零,比较准确,符合会计准则,比较适合打印银行存单等。
PARAMETERS nAmount
PRIVATE ALL LIKE L_*
L_S1=’零壹贰叁肆伍陆柒捌玖’
L_S2=’亿仟佰拾万仟佰拾亿仟佰拾万仟佰拾元角分’
nAmount=LTRIM(STR(nAmount*100,LEN(L_S2)/2))
L_LEN=LEN(nAmount)
L_SS2=RIGH(L_S2,2*L_LEN)
L_DX=”
L_0JS=0 &&连续零的个数
L_N=0
DO WHILE L_N<L_LEN
    L_N=L_N+1
    L_SZ=SUBSTR(nAmount,L_N,1)
    L_SZ=SUBSTR(L_S1,2*VAL(L_SZ)+1,2)
    L_DW=SUBSTR(L_SS2,2*L_N-1,2)
    IF L_SZ=’零’
        L_SZ=”
        DO CASE
            CASE L_DW$’亿元’
            CASE L_DW=’万’
                IF RIGH(L_DX,2)=’亿’
                    L_DW=”
                ENDIF
            OTHERWISE
                L_DW=”
        ENDCASE
        L_0JS=L_0JS+1
    ELSE
        IF L_0JS>0
            L_SZ=’零’+L_SZ
        ENDIF
        L_0JS=0
    ENDIF
    L_DX=L_DX+L_SZ+L_DW
ENDDO
L_DX=L_DX+IIF(RIGH(nAmount,1)=’0′,’整’,”)
RETURN L_DX

2、编写如下函数。该函数的返回值比较适合于一些单据的填空式打印,如增值税发票等。
PARAMETERS nAmount,IsDW
nDZS=STRTRAN(ALLTRIM(STR(nAmount,18,2)),”.”,”") &&把小数点去掉
cHZDX=”零壹贰叁肆伍陆柒捌玖”
cDW=”分角元拾佰仟万拾佰仟亿拾佰仟万拾佰仟亿”
cRMBDX=”"
nCd=LEN(nDZS)
FOR i=1 TO LEN(nDZS)
    cNumbers=SUBSTRC(cHZDX,INT(VAL(SUBSTR(nDZS,I,1))+1),1) && 数字转换
    IF IsDW=1 &&如果要单位
        cDWs=SUBSTRC(cDW,nCd,1)
    ELSE
        cDWs=SPACE(0) &&不要单位的情况下
    ENDIF
    cRMBDX=cRMBDX+cNumbers+cDWS
    nCd=nCd-1
ENDFOR
RETURN cRMBDX

2005年03月25日

实现组合框的记忆选择

在编制管理系统时,往往一个相同的系统要由好几个部门使用,这样,在进入系统时,就要选择不同部门的名称。作为一个使用者,我们希望选择第一次,以后每次进入就不用再选择。以下方法将实现此功能。

  新建一个表单,在上面创建一个“组合框”对象,对表单做如下设置:

  init event:

  set safety off

  public z

  if .not.file(′mv.mem′)

  z=1

  else

  restore from mv additive

  endif

  thisform.combo1.additem(“第一个单位”)

  …

  thisform.combo1.additem(“第二个单位”)

  thisform.combo1.listindex=z

  destroy event:

  z=thisform.combo1.listindex

  save to mv all like z

vfp中将表单的内容直接打印
*******************************************
*– 程序名称:将表单的内容直接打印 –*
*– 将窗口中的内容以一个位图的形式打印 –*
*– 程序作者:未知,来源论坛转帖 –*
*– 使用方法:在表单中执行该程序既可 –*
* 例:在某表单的一个command –*
* 按纽中执行 do printform.prg –*
*******************************************
*– 定义常量
#define logpixelsx 88
#define logpixelsy 90
#define physicaloffsetx 112
#define physicaloffsety 113
#define srccopy 13369376
#define dib_rgb_colors 0

*– 调用本程序段中的子过程
do decl

*– 定义变量
private pnwidth, pnheight, lnbitsperpixel, lnbytesperscan
store 0 to pnwidth, pnheight, lnbitsperpixel, lnbytesperscan
local hwnd, hformdc, hprndc, hmemdc, hmembmp, hsavedbitmap,;
xoffsprn, yoffsprn, xscale, yscale, lcdocinfo, lcbinfo, lpbitsarray

*– 得到打印机设备的坐标偏移量
hprndc = getdefaultprndc() && 没有进行错误检查
xoffsprn = getdevicecaps(hprndc, physicaloffsetx)
yoffsprn = getdevicecaps(hprndc, physicaloffsety)

*– 得到屏幕的窗口句柄,及她们的宽度、高度等。
hwnd = getfocus() && a window with keyboard focus
hformdc = getwindowdc(hwnd)
= getwinrect (hwnd, @pnwidth, @pnheight)

*– 根据屏幕和打印机得到缩放值
xscale = getdevicecaps(hprndc, logpixelsx)/getdevicecaps(hformdc,logpixelsx)
yscale = getdevicecaps(hprndc, logpixelsy)/getdevicecaps(hformdc,logpixelsy)

*– 将屏幕的内容创建为位图图象数据
hmemdc = createcompatibledc (hformdc)
hmembmp = createcompatiblebitmap (hformdc, pnwidth, pnheight)
hsavedbitmap = selectobject(hmemdc, hmembmp)

*– 将位图数据从屏幕拷贝到虚拟设备上
= bitblt (hmemdc, 0,0, pnwidth,pnheight, hformdc, 0,0, srccopy)
= selectobject(hmemdc, hsavedbitmap)

* retrieving bits from the compatible bitmap into a buffer
* as a device-independent bitmap (dib) with a bitsperpixel value
* as one of the printer device context
lcbinfo = initbitmapinfo(hprndc)
lpbitsarray = initbitsarray()
= getdibits (hmemdc, hmembmp, 0, pnheight,;
lpbitsarray, @lcbinfo, dib_rgb_colors)

lcdocinfo = chr(20) + repli(chr(0), 19) && docinfo struct – 20 bytes
if startdoc(hprndc, @lcdocinfo) > 0
= startpage(hprndc)

= stretchdibits (hprndc, xoffsprn, yoffsprn,;
xoffsprn + int(xscale * pnwidth),;
yoffsprn + int(yscale * pnheight),;
0,0, pnwidth, pnheight,;
lpbitsarray, @lcbinfo, dib_rgb_colors, srccopy)

= endpage(hprndc)
= enddoc(hprndc)
endif

*– 退出时释放系统资源
= globalfree(lpbitsarray)
= deleteobject(hmembmp)
= deletedc(hmemdc)
= deletedc(hprndc)
= releasedc(hwnd, hformdc)
return

procedure getwinrect (lnhwnd, lnwidth, lnheight)
*– 返回指定句柄的窗口的宽和高
#define maxdword 4294967295 && 0xffffffff
local lprect, lnleft, lntop, lnright, lnbottom
lprect = repli (chr(0), 16)
= getwindowrect (lnhwnd, @lprect)

lnright = buf2dword(substr(lprect, 9,4))
lnbottom = buf2dword(substr(lprect, 13,4))

lnleft = buf2dword(substr(lprect, 1,4))
if lnleft > lnright
lnleft = lnleft – maxdword
endif
lntop = buf2dword(substr(lprect, 5,4))
if lntop > lnbottom
lntop = lntop – maxdword
endif

lnwidth = lnright – lnleft
lnheight = lnbottom – lntop
return

function getdefaultprndc
* returns device context for the default printer
#define pd_returndc 256
#define pd_returndefault 1024
local lcstruct, lnflags
lnflags = pd_returndefault + pd_returndc

* fill printdlg structure
lcstruct = num2dword(66) + repli(chr(0), 16) +;
num2dword(lnflags) + repli(chr(0), 42)
if printdlg (@lcstruct) <> 0
return buf2dword (substr(lcstruct, 17,4))
endif
return 0

function initbitmapinfo(htargetdc)
#define bi_rgb 0
#define rgbquad_size 4 && rgbquad
#define bhdr_size 40 && bitmapinfoheader

local lnrgbquadsize, lcrgbquad, lcbihdr

* use printer bitperpixel value
lnbitsperpixel = 24

* initializing bitmapinfoheader structure
lcbihdr = num2dword(bhdr_size) +;
num2dword(pnwidth) + num2dword(pnheight) +;
num2word(1) + num2word(lnbitsperpixel) +;
num2dword(bi_rgb) + repli(chr(0), 20)

* creating a buffer for the color table
if lnbitsperpixel <= 8
lnrgbquadsize = (2^lnbitsperpixel) * rgbquad_size
lcrgbquad = repli(chr(0), lnrgbquadsize)
else
lcrgbquad = “”
endif
return lcbihdr + lcrgbquad

procedure initbitsarray()
#define gmem_fixed 0
local lnptr, lnallocsize

* making sure the value is dword-aligned
lnbytesperscan = int((pnwidth * lnbitsperpixel)/8)
if mod(lnbytesperscan, 4) <> 0
lnbytesperscan = lnbytesperscan + 4 – mod(lnbytesperscan, 4)
endif

lnallocsize = pnheight * lnbytesperscan
lnptr = globalalloc (gmem_fixed, lnallocsize)
= zeromemory (lnptr, lnallocsize)
return lnptr

function num2word (lnvalue)
return chr(mod(m.lnvalue,256)) + chr(int(m.lnvalue/256))

function num2dword (lnvalue)
#define m0 256
#define m1 65536
#define m2 16777216
local b0, b1, b2, b3
b3 = int(lnvalue/m2)
b2 = int((lnvalue – b3*m2)/m1)
b1 = int((lnvalue – b3*m2 – b2*m1)/m0)
b0 = mod(lnvalue, m0)
return chr(b0)+chr(b1)+chr(b2)+chr(b3)

function buf2word (lcbuffer)
return asc(substr(lcbuffer, 1,1)) + ;
asc(substr(lcbuffer, 2,1)) * 256

function buf2dword (lcbuffer)
return asc(substr(lcbuffer, 1,1)) + ;
asc(substr(lcbuffer, 2,1)) * 256 +;
asc(substr(lcbuffer, 3,1)) * 65536 +;
asc(substr(lcbuffer, 4,1)) * 16777216

procedure decl && so many of them declared here
declare integer getfocus in user32
declare integer enddoc in gdi32 integer hdc
declare integer getwindowdc in user32 integer hwnd
declare integer deleteobject in gdi32 integer hobject
declare integer createcompatibledc in gdi32 integer hdc
declare integer releasedc in user32 integer hwnd, integer hdc
declare integer getwindowrect in user32 integer hwnd, string @lprect
declare integer globalalloc in kernel32 integer wflags, integer dwbytes
declare integer getdevicecaps in gdi32 integer hdc, integer nindex
declare integer selectobject in gdi32 integer hdc, integer hobject
declare integer startdoc in gdi32 integer hdc, string @ lpdi
declare integer globalfree in kernel32 integer hmem
declare integer printdlg in comdlg32 string @ lppd
declare integer deletedc in gdi32 integer hdc
declare integer startpage in gdi32 integer hdc
declare integer endpage in gdi32 integer hdc

declare rtlzeromemory in kernel32 as zeromemory;
integer dest, integer numbytes

declare integer createcompatiblebitmap in gdi32;
integer hdc, integer nwidth, integer nheight

declare integer bitblt in gdi32;
integer hdestdc, integer x, integer y,;
integer nwidth, integer nheight, integer hsrcdc,;
integer xsrc, integer ysrc, integer dwrop

declare integer stretchdibits in gdi32;
integer hdc, integer xdest, integer ydest,;
integer ndestwidth, integer ndestheight, integer xsrc,;
integer ysrc, integer nsrcwidth, integer nsrcheight,;
integer lpbits, string @lpbitsinfo,;
integer iusage, integer dwrop

declare integer getdibits in gdi32;
integer hdc, integer hbmp, integer ustartscan,;
integer cscanlines, integer lpvbits, string @lpbi,;
integer uusage
return && decl

如何创建一个根据屏幕分辩率自动调整的表单

概述
在一种分辩率下开发的屏幕在另一种分辩率下运行时, 可能与你期望的不同. 一种方案是开发不同分辩率下的多个不同的屏幕, 并在某一分辩率下运行相应的屏幕.

但是在 Visual FoxPro 中, 可以根据当前分辩率动态调整屏幕大小并重新安排对象. 本文用例子演示了如何这样做.

更多信息

逐步示例
在 640×480 分辩率下创建一个新表单. 添加一些文本框, 命令按钮和标签这样的控件到表单.

将以下代码放入表单的 Init 事件中和表单中的任何窗口任何容器对象(如页框中的页)的 Init 事件中:

** 假定屏幕是在 640×480 下创建的
LOCAL lnHeight, lnWidth, lnHeightdiff, lnWidthdiff

lnHeight = 480 && 原分辩率下的控件高度
lnWidth = 640 && 原分辩率下的控件宽度
lnHeightdiff = 0 && 保存不同高度的变量
lnWidthdiff = 0 && 保存不同宽度的变量

IF SYSMETRIC(2) <> lnHeight && 如果目前不是运行于 640×480 分辩率下
lnHeightDiff = SYSMETRIC(2) / lnHeight
lnWidthDiff = SYSMETRIC(1) / lnWidth

** 如果是在容器对象(如页框中的页等非表单且具有 ControlsCount 属性的容器)的 Init 事件中, 你需要注
** 释掉以下代码直到 ENDIF 代码行.

This.Height = This.Height * lnHeightDiff
This.Width = This.Width * lnWidthDiff
This.Top = This.Top * lnHeightDiff
This.Left = This.Left * lnHeightDiff

** 遍历各对象, 重新调整和安排它们.

FOR I = 1 TO This.ControlCount
WITH This.Controls(i)
.Height = .Height * lnHeightdiff
.Width = .Width * lnWidthdiff
.Top = .Top * lnHeightdiff
.Left = .Left * lnWidthdiff

** 你也可以在这里修改 FontSize 属性来重调字体
** IF TYPE(“.FontSize”) # “U”
** && IF 用于确保控件具有 FontSize 属性
** .FontSize = .FontSize * ((.5 * lnWidthdiff) + (.5 * lnHeightdiff))
** ENDIF
** 但是, 一些较高的屏幕分辩率会适当地修改字体外观, 因此在试以上代码前应进行测试.

ENDWITH
ENDFOR
ENDIF

ThisForm.Refresh()

在表同分辩率下运行表单. 可以观察到它们的自动适应

一、Flash 组件概述

组件是带有参数的电影剪辑,这些参数可以用来修改组件的外观和行为。每个组件都有预定义的参数,并且它们可以被设置。每个组件还有一组属于自己的方法、属性和事件,它们被称为应用程序程接口(Application Programming Interface,API)。使用组件,可以使程序设计与软件界面设计分离,提高代码的可复用性。Flash MX 2004 或 Flash MX Professional 2004 中包含的组件不是 FLA 文件,而是 SWC 文件。SWC 是用于组件的 Macromedia 文件格式。库项目中的电影剪辑可以被预编译成swf文件。这样可以缩短影片测试和发布的执行时间。将 SWC 文件拷贝到 First Run\Components目录后,该组件便会出现在“组件”面板中。

Flash 自带的组件都位于mx.controls包中。组件直接或间接继承于UIComponent类,UIComponent类对UIObject类做了扩展,而UIObject类又是MovieClip类的子类。MovieClip类不属于任何包,定义文件位于FlashInstalDir\en\First Run\Classes目录。UIComponent类和UIObject类都位于mx.core包中,定义文件位于FlashInstalDir\en\First Run\Classes\mx\core目录。

二 组件基类
UIObject类对MovieClip类进行封装,所有Flash V2组件都可以共享它的方法、属性和事件。UIObject类使组件在样式、事件和缩放比例调整上得到了实现。它提供了动态创建删除组件的方法:

·createObject方法:直接调用attachMovie函数,返回一个MovieClip类型的引用。

·createClassObject方法:调用createObject方法,创建一个指定类的组件实例,并返回所创建的组件的引用。

·createEmptyObject方法:创建一个空的UIObject实例。

·destroyObject方法:使用delete语句删除已经创建的组件实例。

UIObject还封装了其它的一些方法,包括:

·redraw方法:在当前帧重新绘制组件。

·invalidate方法:标记组件,使之在下一个帧间隔重新绘制。

·move方法:把组件移动到指定位置。

·setSize方法:设置组件大小。

·setSkin方法:设置组件皮肤。

·getStyle方法:获取样式信息。

UIObject类的属性除了scaleX和scaleY外,其它都是只读属性,使用时需要注意。所以如果要在运行时调整组件外观,就必须使用UIObject类的方法。对于只读属性的赋值是无效的,即使是对非只读属性的赋值,有时也会造成组件在外观显示上的差错。UIObject类还定义了一系列的事件,包括加载事件(load)、卸载事件(unload)、移动事件(move)、重绘事件(draw)和大小调整事件(resize)。

利用UIObject类的组件创建方法,可以在运行时创建组件实例。

UIComponent类从UIObject类继承,它并不是一个可视的组件。和UIObject类一样,所有Flash V2组件都可以共享它的方法和属性。它实现了组件的焦点获取、键盘输入,组件的禁用和启用以及组件的按布局调整自动大小。

UIComponent类的主要方法有:

·getFocus方法:利用焦点管理器(Selection)返回一个当前获取焦点的对象的引用。

·setFocus方法:使组件获取焦点。

UIComponent类的enabled属性指定组件实例是否可用;tabIndex属性指定组件的焦点获取顺序。UIComponent类还定义一系列焦点和键盘事件:焦点获取事件(focusIn),焦点转移事件(focusOut)、键盘按下事件(keyDown)和键盘释放事件(keyUp)。

三、Flash V2 组件的基本使用方法

在Flash MX 2004和 Flash MX Professional 2004自带了一套组件称为Flash V2组件,它是从Flash MX的V1组件升级而来的,V1组件和V2组件尽量不要一起使用以免发生一些错误。V2组件支持实时预览,使开发者在编辑状态下就能看到组件的外观效果。开发者可以自行设置是否需要实时预览和实时预览的模式,以节省计算机资源。在ControlàEnable Live Preview菜单设置是否使用实时预览,在ViewàPriview Mode菜单选择预览模式。组件实质上是一些被编译好的电影剪辑,其格式为SWC。SWC文件类似与Zip文件,可以使用一般的解压缩程序进行解压查看其内容。电影剪辑可以在编辑环境中直接发布为组件,也可以直接编译存储为swc格式的组件文件,便于其他开发者使用。同样,开发者也可以从外部导入组件。

使用组件,必须把组件面板中所需要的组件拖到舞台(Stage),使组件出现在库面板。这样组件就可以象普通的库项目一样被使用。使用脚本可以动态的创建组件实例,运行时创建组件可以有三个方法:createObject()、creatClassObject()和attachMovie()。attachMovie()是MovieClip类的方法,createObject()和creatClassObject()是UIObject类方法,但事实上它们都是对attachMovie()的直接或间接调用。

组件被添加后可以在属性或参数面板中直接设置组件的参数。另外还要为组件定义事件,最简单的方法是使用on(eventName)进行定义。另外还可以使用侦听器和事件处理函数等定义组件事件的处理方法。组件的事件定义比较灵活,使用何种方式定义取决与个人的习惯与偏好。当事件发生时,组件会广播一个Object类型的事件参数,该参数中包括了事件发生对象和事件类型的信息。另外,使用深度管理器(DepthManager)可以对组件进行深度管理。

Flash组件最大的特点是开发者可以自定义组件,尤其是界面元素的自定义,使其更具有吸引力。V2组件有它自己的一套默认界面方案,称为主题(Theme)。主题包括两个方面:样式(Style)和外观(Skin)。UIObject类的setStyle()和getStyle()方法可以获取和设置组件的样式。样式的设置比较简单,主要有以下三种方式:

·直接设置属性:

T.color=0xff5567;

·使用简单的setStyle()名值对方法自定义样式:

instanceName.setStyle(“property”, value);

·把样式对象与setStyle()方法结合使用来自定义样式:

//创建一个样式对象

var styleObj = new mx.styles.CSSStyleDeclaration;

//刻画样式细节

//……

styleObj.fontSize = 18;

//把样式应用到组件实例

b.setStyle(“styleName”, styleObj);

样式的级别包括4个层次:全局样式,应用于所有组件;组件类单独的默认样式,应用于某一类的组件;自定义样式;直接设置组件实例的属性。对于不同级别的样式Flash按照一定的顺序分别把这些样式应用到组件。

首先,Flash 查找组件实例上的样式属性,如果实例上没有直接设置样式,Flash 将查看实例的styleName 属性,确定是否向它分配了样式声明。如果 styleName 属性没有被分配样式声明,Flash 将查找默认类样式声明上的属性。如果没有类样式声明,并且属性没有继承它的值,则将检查 _global 样式声明。如果属性未在 _global 样式声明中定义,则该属性为 undefined。

另外,如果没有类样式声明,但属性确实继承了它的值,Flash 将查找该实例父级上的属性。如果属性未在父实例上定义,Flash 将检查父实例的 styleName 属性;如果未定义该属性,Flash 将继续查看父实例,直到 _global 级别。如果属性未在 _global 样式声明中定义,则该属性为undefined。

另一方面,为了更好的使用组件,还需要修改或重新定义组件外观。Flash V2的外观定义文件位于FlashInstralDir\en\First Run\ComponentFLA文件夹下。在修改外观时,需要把原始的外观定义文件作为外部库导入(FileàOpen External Library),并把外部库中的外观定义库项目拖到当前文档的库中,外观库项目通常都是电影剪辑(MovieClip)。每一个组件外观定义库项目文件夹中都包含一个States文件夹,其中定义了组件在不同状态下的外观,每一个状态都指定了一个链接名(linkageName)作为脚本引用,如果修改了这个名称,则会使组件在该状态下的外观加载发生错误。在组件的类文件中,每一个状态都使用一个字符串属性指定其外观库项目的链接名,如果要重新定义组件某一状态下的外观,只要在脚本中修改这个字符串属性的值就可以了,当然首先必须存在可链的新的外观库项目。

了解了这样一个组件外观机制以后,组件外观的修改就变的非常简单。其关键就是修改或重新定义一组新的外观库项目并指定其链接名,然后修改组件类文件中指定组件外观的属性。开发者可以在组件实例的初始化事件(initialize)中修改组件实例的外观。修组件所有实例外观的方法比较多,例如_root的第一帧使用ClassName. prototype.stateName=value语句,该语句也可以直接在#initclip和#endinitclip代码块中使用;另外还可以使用类继承的方式,在组件初始化时就为组件外观链接属性指定新的值,这里也需要用到#initclip、#endinitclip代码块。#initclip和#endinitclip是Flash的编译器命令,作为初始化代码块的开始标记和结束标记。只要符号(symbol)被定义,那么初始化代码就被先于第一帧执行,而且紧执行一次。所以在这个代码块中,可以为整个组件类指定不同状态下的外观。

四、Flash V2组件开发

在Flash MX Professional 2004中,开发人员开可以在项目中自己创建新的V2组件。在开始开发V2组件之前,首先要了解MovieClip类、UIObject类和UIComponent类,因为它们是Flash V2组件的基类,所有Flash自带的组件都是它们的直接或间接子类。开发一个组件可以从原先的父类继承,扩展现有的组件类,使被创建的组件在父类的基础上进一步扩展。另外,也可以创建一个不带父类的组件,以实现一些其它的功能。在这里将重点讨论如何扩展现有的组件类。扩展一个现有组件类,通常包括以下几个步骤:

1、组件首先作为一个电影剪辑(MovieClip)被创建,在该电影剪辑中创建两个图层,分别用于组件界面元素和脚本。然后在库的下拉菜单中选择Component Defination,在该对话框中的AS2.0 Class中输入该组件所要关联的类的完全限定名。当界面元素定义完成以后,就可以在外部文件中创建组件的属性、事件和方法,当然,组件的界面元素也可以在类中使用代码动态创建。

2、在定义组件类文件时,首先必须导入所需要的类,前面已经说过,MovieClip类、UIObject类和UIComponent类是V2组件的基类,所以如果当前创建的组件是从V2组件的任意组件扩展,那么就必须了解这三个基类,和其下的相关组件类。关于组件的特性可以查看Macromedia公司发布的组件字典,也可以直接在FlashInstalDir\en\First Run\Classes目录查看类文件代码。

3、确定组件所要扩展的类以后,就要为组件类编写构造函数(Constructor)。一般情况下,构造函数建议为空,这样才能用对象的属性接口来定义对象属性。此外,根据初始化调用顺序的不同,有时在构造函数中设置属性会导致覆盖默认值。

4、接下来要做的是添加组件的版本信息,如果当前开发的组件是作为组件包的一部分,那么可以把版本信息放到另外一个外部文件中。在定义版本信息时,可以继承UIObject类的静态字符串属性version。另外还需要定义三个比较重要的属性:symbolName,symbolOwner和className;他们都是从组件基类继承的。symbolName定义为静态的字符串变量,用于指定组件符号的名称;symbolOwner定义为静态的Object类型,是该类的一个完全限定名,它将在createClassObject() 方法的内部调用中被使用;className定义了组件类名称。

5、当这些都完成以后,就要开始定义组件功能相关的属性和方法。在属性和方法定义时,比较好的做法是确定每一个属性和方法的访问特性,使组件为用户提供一个良好的应用程序接口(API)。所以在定义属性和方法时要使用private和public来声明属性的可访问性,并使用setter和getter函数来设置和获取属性的值。这样就更好的地实现了组件的封装,使用户无须了解组件的内部细节。

所有组件必须实现两个核心方法:始化方法和大小调整。如果不在自定义组件的类文件中覆盖两个方法,Flash Player 可能会产生错误。Flash 在创建类时调用初始化方法。初始化方法应该调用父类的初始化方法,因为只有在调用此方法之后,才能正确设置width、height 和和其它的影片剪辑参数。


function init(Void):Void

{

// 调用父类的初始化方法

super.init();

//在这里添加与本组件相关的初始化代码

}

大小调整的方法也与初始化方法类似:

function size(Void):Void

{

super.size();

//在这里添加与本组件相关的大小调整代码

}

6、为了让属性在开发面板中可见,还必须为属性声明相关的元数据(Metadata)。元数据标记可以定义组件属性、数据绑定属性和事件。Flash 可以解释这些语句并相应地更新开发环境。元数据与类声明或单个的数据字段相关。元数据语句绑定到脚本的下一行。例如,在定义组件属性时,在属性声明的前一行添加元数据标记。在定义组件事件时,在类定义之外添加元数据标记,以便将事件绑定到整个类。对属性而言,有两个比较重要的元数据:Inspectable和InspectableList。

Inspectable元数据定义了在“组件检查器(Component Inspector)”面板中向用户显示的组件的属性。语法如下:

[Inspectable( value_type=value [,attribute=value,...] ) ]

property_declaration name:type;

Inspectable元数据还包括若干元数据标记:

·name:类型为String(可选),属性在开发面板中的显示名称。

·type:类型为String(可选)指定属性的类型。如果省略,则使用属性的类型。以下下是可接受的值: Array、 Object、List、String、Number、Boolean、Font Name、Color。

·defaultValue:类型可以是String或Number(必需)。指定属性的默认值。

·enumeration:类型为String(可选)。指定以逗号分隔的属性合法值列表。

·category:类型为String(可选)。将属性划分到属性检查器中的某个特定子类别中。

InspectableList 元数据关键字用于确切地指定属性检查器中应显示可检查参数的哪个子集。可以将 InspectableList 与 Inspectable 组合使用,这样即可隐藏子类组件的继承属性。如果不给组件的类添加 InspectableList 元数据关键字,所有可检查的参数(包括组件父类的可检查参数)都会显示在属性检查器中。其的语法如下:

[InspectableList("attribute1"[,...])]

// class definition

InspectableList 关键字必须紧挨着类定义且在它之前,因为它应用于整个类。

7、定义组件的事件,首先要使用Event元数据关键字声明事件。Event 元数据关键字用于定义组件事件。其语法如下:

[Event("event_name")]

在类文件中必须把 Event 语句添加到类定义之外,以便将它们绑定到类,而不绑定到类的特定成员。首先组件类会继承基类事件。如果当前定义组件的基类是UIComponent,那么该组件中已经包括了MovieClip类、UIObject类和UIComponent类的28个事件。但事实上,这28个事件并不是都可以用的。其原因很简单,因为组件可以有它的组成结构,组件内部可能还有组件或是其它的元件实例。组件要为用户提供一个统一的接口,就必须抽取其内部结构的某些事件并把它们定义为组件的事件,这是一种基于组件内部组成结构的事件。比如说组件包括两个文本框T1和T2,这时可以根据组件的功能要求把T1的change事件发布为组件的PP事件,而把T2的change事件发布为组件的UU事件,当然关于组件事件的名称可以由组件开发人员自己定义。

那么究竟如何为组件定义一个新的事件呢?对于上面的那个例子,可以做如下定义:

//导入类

import mx.core.UIComponent;

//用元数据声明组件事件

[Event("PP")]

[Event("UU")]

//指明该类从UIComponent继承

class T_T extends UIComponent

{

//在编辑环境中已经创建两个输入文本(Input Text),并在类中声名其引用。

var T1:TextField;

var T2:TextField;

//定义构造函数

function T_T()

{

//在构造函数中发布T1的change事件

T1.onChanged=function()

{ //创建一个事件对象,存放与事件相关的信息

var eventObj = new Object();

//定义事件类型的名称

eventObj.type = “PP”;

//指明事件广播(发生)的对象

eventObj.target = _parent;

//把事件作为组件的事件发布

_parent.dispatchEvent(eventObj);

}



//在构造函数中发布T2的change事件

T2.onChanged=function()

{

var eventObj = new Object();

eventObj.type = “UU”;

eventObj.target =_parent;

_parent.dispatchEvent(eventObj);

}

}

}

在以上代码中,先用元数据声明了组件的两个事件UU和PP,然后又在组件类的构造函数中定义T1和T2的change事件,并在它们的change事件中用_parent.dispatchEvent(eventObj);语句把chang事件发布为组件事件。dispatchEvent()方法需要一个Object类型的事件对象作为参数,该对象中保存了与事件相关的信息:target指明事件广播(发生)的对象;type定义了事件类型的名称——也可以认为是事件名称。在事件脚本中使用如下代码就可以对事件做出响应,其使用方法与一般的事件处理方法一致:

//对组件的PP事件做出响应

on(PP)

{ trace(“PP”); }

//对组件的UU事件做出响应

on(UU)

{ trace(“UU”); }

组件的属性的改变也可以作为事件发布,例如:

private var Tm:String;

//在setter中发布组件的事件

public function set TTm(val:String)

{

Tm=val;

var eventObj = new Object();

eventObj.type = “KK”;

eventObj.target =this;

this.dispatchEvent(eventObj);

}

当然,还要用Event元数据为组件声明事件。但事实上,没有Event元数据声明的事件组件同样可做出响应。Event元数据声明似乎仅仅是多了一个代码提示而已。所以元数据只是为组件提供了一个更加友好的用户接口,使开发人员更加容易得使用组件。

在发布组件之前,还可以为组定制一个图标。图标大小要求为 18 x 18 像素,并保存为 PNG 格式。它的 Alpha 透明度必须是8位,左上角的像素要求是透明的,以支持遮罩。另外还需要在组件类文件中定义添加元数据声明:

[IconFile("component_name.png")]

该声明和事件声明一样,必须放在类定义之前,使该声明作用到组件类。最后将该图像保存到到FLA文件所在的同一目录中。在导出 SWC 文件时,Flash将在自动包含该图像。

当组件定义完整,测试通过后,就可以发布组件供其他开发人员使用。Flash MX 2004 将组件导出为组件包(SWC 文件)。在发布组件时,只需向其他开发者提供 SWC文件就可以了。此文件包含与组件相关的所有代码、SWF 文件、图像和元数据,因此其他开发者可以方便地将它放到自己的 Flash开发环境中。

这里对Flash V2组件开发做了初步的讨论。在具体的开发时,应根据组件的功能特性非常细致地刻画组件的属性、事件和方法,声明元数据定义良好的用户接口。如果该组件是一个可视的组件还需要为组件制作组件界面的图形元素。