开发PalmOS的步骤
第三章 文本框中的乐趣 |
在本章节中,我们将通过讨论第二章中Hello应用程序的副本,来研究文本框的属性和事件。涉及的内容包括文本框如何调用Palm OS存储器和处理它们之间的关系等。我们也将花些时间讨论与文本框相关的其它一些内容: ◆手写输入转换指示器(Graffiti shift indicators) ◆字符串资源(String resources) ◆编辑菜单的特殊之处和普通菜单 ◆Palm OS 版本可兼容代码 ◆错误信息和警告 文本框究竟是什么? 文本框其实就是一个编辑框(单行可编辑的),利用它你可以让你的用户输入文字或数据。让我们来创建一个文本框并感受学习的乐趣吧。 先在Code Warrior集成开发环境中创建一个应用程序: 1.运行Code Warrior集成开发环境。 2.选中菜单File | New Project来创建一个新的项目。 3.从项目选项中选出Hello应用程序,并重命名为你的新应用程序的名称。 4.从项目中移走原来的资源文件。你可以这样操作:在Src文件夹中用右键点击Hello.rsrc,选中Remove Selected Items。 5.到你的项目文件夹的Src目录下删除Hello.rsc。 6.运行资源构造器(Constructor)来创建一个资源文件。 7.在资源列表中选取窗体并按Ctrl+K,创建一个名为Contact Detail的窗体。点击默认名称并重新命名窗体。 8.双击窗体以打开该窗体进入编辑状态。 9.向窗体中拖放一个文本框。你可以选中菜单Window/Catalog来打开目录窗口。然后从目录窗口中拖动一个文本框控件放到窗体中。 10.命名文本框名为FirstName。你可以这样操作:点击该文本框来显示它的属性,在窗体的左边方框会出现一个窗口,点击Object Identifier属性,然后输入FirstName。 11.在这一步,最好把手写输入转换指示器(Graffiti shift indicators)放到你的窗体中。从目录窗口中拖动一个手写输入转换指示器控件放到窗体中,这个转换器的标准位置应放在窗体的右下角。 12.按以上操作后,窗体显示如图3-1所示。 13.回到Code Warrior集成开发环境中,在项目中加入一个新的资源文件。在Code Warrior集成开发环境中选中Project | Add Files命令。 14.选中Project | Make命令编译连接项目。 15.到调试器中调试程序。首先通过菜单Project | Enable Debugger来击活调试器。 16.打开你的Palm,确认它固定在支架上。然后关掉PC的HotSync软件。 17.在PC上,选中Project | Debug命令。 18.在你的装置中,输入l连写的字母“1”,两个点号“..”,和一个数字“2”(1..2),来启动控制台程序(console)。 19.在PC上,点击OK启动调试器。 20.在调试器中点击前进箭头,执行应用程序。 图3-1 程序中窗体的外观 在Palm上运行应用程序,点中文本框,你就可以输入文字或数字了。如果你在大字状态时向右上一挑或者在符号中一点,你放在窗体上的手写输入转换指示器会分别露出箭头或点。应用程序显示如图: 图 需要结束应用程序时,请切记点击调试器窗口中的X来重启你的装置或停止控制台程序的运行。让控制台继续运行会引起Palm的一些问题。 如果你的手写输入转换指示器在你刚才的程序中运行顺利的话,你就可以开始进一步了解它了。但如果它没能正常工作,那么你就有必要去判定程序中是否存在阻止文本框事件被操作系统处理的代码了。正确的做法是,可以把它们放在任何窗体中,那么当你手写输入时就可以看到它的用途了。它通常被摆在窗体右下角,因为人们习惯这样做。没有程序一定需要它,但把它放到窗体中它就能工作。 从上面的例子中你可以看到许多与文本框相关的东西在工作。我们运行的窗体中的代码(hello.c)其实并没有对窗体做什么,然而我们却可以修改,输入,查找和替换文本,甚至做其它我们想做的事情。剪切和粘贴在这里不起作用。至于菜单和快捷键,我们将在以后的章节中接触到。 注意:如果你的Palm在调试器运作后出现异常,可能是因为它没有被重启或控制台依然在运行。请把你的Palm放好,用推针或回形针的尾部去顶开P alm背面小洞中的重启按钮。 属性 你可以在资源构造器中改变很多有关文本框如何工作的内容。在构造器中调出Contact Detail窗口,选中项目中的.rsrc文件,然后在Contact Detail窗体中双击文本框,它的属性会显示在旁边。所有的Palm OS 用户界面控件都有一些公共的属性,像Left Origin和Top Origin。表3-1是所有的文本框属性和描述的列表。熟悉这些属性后,重新编译并调试程序,以试验每一个属性是如何影响文本框行为的。 表3-1 名称 描述 Object Identifier 你选定的文本框名称。 Field ID 这个数值是Palm OS 用来定义特殊的用户界面对象的。 Left Origin 定义文本框左边界的位置,整个屏幕跨越160象素。 Top Origin 定义文本框上边界的位置,整个屏幕从顶到底共160象素。 Width 文本框宽度象素。 Height 文本框高度象素,你必需增加这个数值以避免砍掉一些大字体的顶部。 Usable 定义文本框是否在窗体中显示。那些没有被标记为可用的文本框是不可见或不可选的,直到它们被某个功能命令激活。 Editable 定义文本框是否可选或接受涂写输入。对大部分文本框来说,该项已被选中。 Underline 在文本框下面画上圆点线来显示它的位置。否则,在屏幕上不能指出已存在的空文本框。 Single Line 限制输入一行文本。该文本框不会垂直滚动或接受回车或Tab键的输入。 Dynamic Size 使该文本框可以根据需要扩大,以显示所有输入的文本。你必需增加代码来实现,不用担心在改变属性时没有看到它的作用。 Left Justified 使文字左对齐排列。不用检查对数字显示的有用性,所以小数排成行放置。 Max Characters 规定的最大字符数,在Palm OS禁止输入和发声报警前可以输入的字符数。 Font 文本中的字体在文本框中显示。请注意你最好根据你所选的字体手动改变文本框的高度。 Auto Shift 把输入的第一个字母变为大写。 Has Scroll Bar 这个选项使文本框在字符太多时自动加上滚动条。这会引起Palm OS发送事件,用户可以在该事件中更新滚动条显示。 Numeric 允许输入的数字转变为字符保存。 再谈事件 从第二章可以了解到,Palm OS程序,类似于Mac OS程序和Windows程序,都是以事件驱动为基础的。程序不用做任何事情(大部分是这样),直到用户的输入到来,比如按下了按钮,用输入笔或手指接触了屏幕等。 当用到文本框的时候,我们关心以下四个事件。第一,penDownEvent和penUpEvent。每一次装置的屏幕被接触时会有PenDownEvent发生。同样地,每一次输入笔离开屏幕时会有P enUpEvent发生。 对Palm OS的用户来说,界面元素事件,包括文本框的事件,被转换成为对FrmDispatchEvent()函数的调用。对于文本框事件FrmDispatchEvent() 函数其实就是转化成了其它三个事件:fldEnterEvent,fldChangedEvent和keyDownEvent。只要文本框被敲击选中,就发送FldEnterEven t。只要文本框调整它的外观,例如,水平滚轴,就发送FldChangedEvent。只要手写输入特征被识别并发送到文本框中,就发送keyDownE vent。 如果你不处理事件的话,就应该在事件处理器中返回false值。这样的话,我刚才描述的极好的自动产生事件的特性就没用了。如果事件处理器中返回t rue值,FrmDispatchEvent()处理下一个事件;如果返回false值, DispatchEvent()就调用FrmHandleEvent(),FrmHandleEvent()函数实质是把多个简单事件转化成若干复杂事件来处理的函数。以处理文本框事件为例,F rmHandleEvent()函数调用FldHandleEvent()就产生了我们刚才讨论的文本框事件。除非是在非正常的环境中,我不推荐调用FrmHandleEv ent()函数或FldHandleEvent()函数。当你想进一步地处理特殊的事件时,请留意从你的事件处理器中返回的false值。 现在让我们来修改Contacts项目中的代码,调用标注了日期的刚创建的新资源。使用资源管理器,复制另一个Hello.c副本到Contacts项目的S rc文件夹中。把它重命名为Contacts.c,然后通过选中Project | Add Files命令把它重新加入到项目中。使用新的Contacts_res.h头文件来替代hello_res.h,在Contacts.c文件中包含它。做完这些后,为了使代码能成功编译并连接,你还必需将引用从H elloForm改到ContactDetailForm。到现在,虽然我们还在使用错误的资源文件,但程序已经可以工作了,因为表示HelloForm窗体的ID与表示C ontactDetailForm窗体的ID是相同的。构造器经常从1000开始赋窗体的ID值的。 其它的需要修改的内容是case语句,在程序中找到处理ctlSelectEvent事件的case语句。下面就是要找的,它表示按钮被按下的事件: //CH.2 The botton was pressed case ctlSelectEvent; SndPlaySystemSound(sndAlarm); return(false); 到现在为止,Contacts程序中的case语句还没有做完。由于窗体中并没有按钮,所以根本没有机会收到ctlSelectEvent事件。那么就让我们通过改变c ase语句来捕获penDownEvent事件吧,它会使你更有信心的。用下面的代码替换上面的case ctlSelectEvent代码: //CH.3 The pen touched down case penDownEvent; SndPlaySystemSound(sndAlarm); return(false); 选中Project | Make(在Project | Debug下)来调试已修改的代码。在调试窗口点击运行按钮运行已修改的应用程序,当你每次点击文本框时,它可能发出只有紧急事件产生时才有的警报声。事实上,无论你点击窗体的任何地方都会听到警报声,因为都有p enDownEvent事件被触发产生。你也可以试着把penDownEvent替换为penUpEvent,make,debug,然后运行程序。现在警报声就在输入笔点击后离开屏幕时发出了。 再试着把penUpEvent替换为fldEnterEvent,make,debug,并运行程序。可以发现直到输入笔碰到窗体的文本框部分时,警报声才响起。现在我们可以很容易地可以用精确事件去来替换未明了的事件了,像p enDownEvent和penUpEvent事件。这样做不但相当的简单,而且它还会给人一种用户界面的感觉,这与其它Palm OS应用程序的风格是一致的。所以,知道有关penDownEvent和penUpEvent事件很有益,但是我的建议只是在很多精确的事件不可用的情况下才使用它们。 试着把fldEnterEvent替换为fldChangedEvent。make,debug,并运行应用程序。这次如果你在文本框中输入了相当多的字符的话,文本框将出现水平滚动条,警报就会消失。 试着把fldEnterEvent替换为keyDownEvent。make,debug,并运行应用程序。现在只要在输入区域产生一个正确的字形,警报就会响起了。如果在输入板引擎还未决定你是否已经输入正确的内容前,你就已经抬起了笔,那么就可以看到一些很有趣地东西。这并不容易做到(至少对我来说)。 注意:在事件产生时播放声音通常是一个较好的方法,这样可以正确地看到在什么环境下什么时候一个特殊的事件会发生。这是一个很好的实验方法,用来帮助你采集操作系统的状态。 焦点 有50种以上的Palm OS函数,允许你以不同的方法操作文本框和你输入的数据。在下一部分,我们将处理大多数基本功能调用,你可能经常用到(至少对文本框来说,并不是直接连接数据库中的数据)。文本框功能中剪切,复制,删除,粘贴,和撤消最受欢迎,我们在用到菜单和快捷菜单时,将处理这些问题。 你需要先点击文本框来才能够向文本框中写入字符。换句话说,文本框需要先获得焦点才能够接收输入。 让我们调用FrmSetFocus()函数来满足点击文本框后获得焦点的需要吧。这个函数必须紧跟着画窗体的语句出现,因为在窗体出现前获得焦点函数是不起作用的。所以,对它来说最合适的位置应该是在P ilotMain()中的FrmDrawForm()后。为了用使用FrmSetFocus()函数,你必须首先调用FrmGetObjectIndex()函数得到目标对象索引。这样,在你m ake,debug,并且运行代码后,在你点击输入之前,就可以看到在文本框中有一个闪动的指针。你加入的新代码应该如下: //CH.3 Initialize our form form=FrmInitForm(ContactDetailForm); FrmSetEventHandler(form,myHandleEvent); FrmSetActiveForm(form); FrmDrawForm(form); //CH.3 Get the index of our field index=FrmGetObjectIndex(form,ContactDetailFirstNameField); //CH.3 Set the focus to our field FrmSetFocus(form,index); //CH.2 Our event loop do { 向文本框输入字符 现在我们该向文本框中输入一些字符了。在这样做之前,你有必要学习一点有关文本框使用Palm OS存储器方面的知识。一旦对文本框进行编辑后,就会相应的引起数据库内存被直接编辑,与文本框有关的一切操作都被对应到一块可以进行操作和自由改变大小的存储片。在P alm OS的术语中,这些存储片被叫做块(chunk)。 文本框需要用到这些特殊的数据结构来保存用户输入的数据,因为这种数据结构具有根据输入数据改变大小的特性。现在假定我们已经分配了两个块的内存给两个文本框了。每个块的初始大小是8 0个字节: XXXXXXXXYYYYYYYY 这时就存在这样一个问题,如果需要扩大第一个文本框的容量到100个字节,但因为第二个文本框分配的块紧挨着它的下面分布,所以就没有空间可以用来扩展第一个内存块了。此时如果我们不准备放弃,不限制文本框小一些并且不改变字符总数的话,文本框中就会丢失一些内容。 为了解决这个问题,人们扩展了内存处理(memory handle)的概念。采用把指针放在内存指针列表中的方法代替了直接返回指针给每一个被分配的存储片的方法。操作系统可以利用空闲的时间来调整块的位置,这样就可以允许块的扩展了, XXXXXXXXXXXXYYYYYYYY 当内存块正在使用的时候,我们通过锁定内存块来阻止操作系统试图移动内存块的操作。在我们还没有完成操作之前,内存块一直处于锁定的状态。 在先前的简单文本框例子中,当我们开始向文本框加入字符时,Palm OS就自动分配了一个内存块给文本框,并实现了内存块和文本框的自动绑定。如果想在程序一开始就得到一个带字符的文本框,我们就必须先给文本框分配一个内存块,向内存输入文本,并且把内存与文本框绑定。让我们在C ontacts.c中加入C代码,使我们能够在Contacts.c的开头部分分配并初始化内存块: //CH.3 Our field memory handle static Handle htext; //CH.3 Handle to the text in our edit field #define HTEXT_SIZE 81 //CH.3 Size of our edit field //CH.2 The main entry point Dword PilotMain(Word cmd,Ptr,Word) { FormPtr form; //CH.2 A pointer to our form structure CharPtr ptext; //CH.3 Points to the text in the edit field Word index //CH.3 A general purpose index FieldPtr field; //CH.3 Used for manipulating fields EventType event; //CH.2 Our event structure //CH.2 If this is not a normal launch don’t lauch if(cmd!=sysAppLaunchCmdNormalLaunch) return(0); //CH.3 Allocate our field chunk htext=MemHandleNew(HTEXT_SIZE); if(htext= =NULL) return(0); //CH.3 Lock the memory,get the pointer ptext=MemHandleLock(htext); //CH.3 Initialize it StrCopy(ptext,”hello”); //CH.3 Unlock the field’s memory MemHandleUnlock(htext); 在上面的代码中可以看到四个Palm OS的新功能。 MemHandleNew()分配一个内存块。MemHandleLock()锁定内存块,防止Palm OS移动存储器。StrCopy()就像C语言函数strcpy(),它从存储器的一个地方复制一个零界限字符串到另一个地方——这样,就可以复制一个常量的值到我们的内存块了。M emHandleUnlock()告诉操作系统,我们已经解锁了一个内存块。这条语句后,任何对ptext指针的调用都会变得危险,因为内存块在任何时候都可能被移动的。 若发生Palm设备内存溢出这样的悲惨事件的话,MemHandleNew()将返回NULL。以上的代码可以使应用程序安静的退出,但是这还远不是一种理想的解决方案。在下一章中你将会学到如何发送带有警告的错误信息给你的用户。 剩下要做的就是把我们的内存块丢给文本框了。可以在画窗体前做以下这些工作: //CH.3 Initialize our form form=FrmInitForm(ContactDetailForm); FrmSetEventHandler(form,myHandleEvent); FrmSetActiveForm(form); //CH.3 Get the index of our field index=FrmGetObjectIndex(form,ContactDetailFirstNameField); //CH.3 Get the pointer to our field field=FrmGetObjectPtr(form,index); //CH.3 Set the editable text FldSetTextHandle(field,htext); //CH.2 Draw the form FrmDrawForm(form); 这里稍有一些技巧的函数是FldSetTextHandle()。为了调用这个函数,你必须有一个指针指向文本框。你可以调用FrmGetObjectPtr()来取得这个指针。可以看到F ldSetTextHandle()必须在FrmGetObjectPtr()前被调用,否则你输入到内存块的文本就不能与窗体部分一起画上。调用后面的FldDrawFie ld()来画出整个窗体。 你把经过上面修改的代码加入到Contacts.c后,再一次make,debug,并运行你的项目。初始值”hello”就将会显示在文本框中了。 使用字符串资源 当前,我们初始化了程序中的文本框,它带有不容易编码的字符串。对于这么一个小型的应用程序来说,字符串编码也许不成问题,但对于大型应用程序来说,它就可能引起不愉快的问题了,当你准备将你的软件向海外销售时,你会发现你必须无止尽的把时间花费在代码的改变问题上,为的只是把它的显示部分翻译成其它语言。我知道,唯一一个能帮助你解脱出来的办法就是使用字符串资源。 字符串资源就是与其它界面元素一起存储在你的资源文件中的字符串。许多翻译公司都有能力把你的应用程序翻译成其它外语,只要你给他们资源文件就可以了。所以这是把你的应用程序翻译成另一种语言花代价最小的一种方法。 在资源构造器(Constructor)中创建一个字符串资源: 1.启动资源构造器并打开Contacts.rsrc文件。 2.从资源列表中选中字符串资源类型。 3.按Ctrl-K来创建一个新的字符串资源。并命名为FieldInit。 4.双击打开新的字符串资源。输入hello。 要在你的代码中使用字符串资源,必须获得它,锁定它,在想用的时候用到它,最后才释放。代码实现如下: //CH.2 If this is not a normal launch,don’t launch if(cmd!=sysAppLaunchCmdNormalLaunch) //CH.3 Get the initialization string resource handle hsrc=DmGetResource(strRsc,FieldInitString); //CH.3 Lock the resource,get the pointer psrc=MemHandleLock(hsrc); //CH.3 Allocate our field chunk htext=MemHandleNew(HTEXT_SIZE); if(htext= =NULL) return(0); //CH.3 Lock the memory,get the pointer ptext=MemHandleLock(htext); //CH.3 Initialize it StrCopy(ptext,psrc); //CH.3 Unlock the field’s memory MemHandleUnlock(htext); //CH.3 Unlock the resource’s memory MemHandleUnlock(hsrc); //CH.3 Release the string resource DmReleaseResource(hsrc); 在Contacts.c中修改以上代码。Make,debug,并运行结果。程序看起来和原来并没有什么不同,但现在你的字符已经是从字符串资源中得到了的。 菜单 现在是给Contacts程序增加一个Edit菜单的时候了。Palm OS用户界面方针定义了一个标准的Edit菜单,只要你提供一个文本框,它就可以被输入和编辑。这个菜单显示如下: 图 首先构造菜单的资源部分: 1.启动资源构造器并打开Contacts.rsrc文件。 2.从资源列表中点击菜单条资源类型。按Ctrl-K来创建一个菜单条。并命名为Contact Detail。 3.双击打开Contact Detail菜单条资源。按Ctrl-M来创建一个新菜单。 4.在菜单条上改变名称,把Untitled改为Edit。 5.按Ctrl-K来创建一个新的菜单条目。输入Undo。按TAB键到达菜单条目快捷区域。输入U。继续这个过程,创建每一个新的菜单条目,就像前面例子中显示的那样。你可以使用C trl-连接号(-)来创建Select All和Keyboard之间的分隔条。 6.当你创建完菜单,双击打开Contact Detail窗体。在Menu Bar ID属性条中输入菜单的ID号(可能1000)。 7.以上工作完成了菜单条资源的创建,它看起来像前面例子中的一个标准的Edit菜单。 为了使用你刚才创建的菜单,必须在Contacts.c中增加一些代码。为了处理菜单事件,你必须增加一个叫MemuHandleEvent()的函数调用,在事件循环中处理菜单事件。 //CH.2 Handle system events if (SysHandleEvent(&event)) continue; //CH.3 Handle menu events if(MenuHandleEvent(NULL,&event,&error)) contiue; //CH.2 Handle form events FrmDispatchEvent(&event); 在你事件处理器的循环中,以menuEvent函数调用替换ctlSelectEvent调用。使用它再去调用一个名为menuEventHandler()的函数。现在你新的事件处理器看起来像下面这样: //CH.2 Our form handler function static Boolean myHandlerEvent(EventType* event) { //CH.3 Parse menu events if(event->eType= =menuEvent) return(menuEventHandler(event)); //CH.2 We’re done return(false); } 现在来写menuEventHandler()函数。首先,你必须发信号给用户界面,报告菜单事件已经被接收: //CH.3 Handle menu events Boolean menuEventHandler(EventPtr event) { FormPtr form //CH.3 A pointer to our form structure Word index //CH.2 A general purpose control index FieldPtr field; //CH.3 Used for manipulating fields //CH.3 Get our form pointer form=FrmGetActiveForm(); //CH.3 Erase the menu status from the display MenuEraseStatus(NULL); 提供单独的输入区帮助。这是因为输入区帮助应该是可用的,不管文本框当前是否被选中。 //CH.3 Handle graffiti help if(event->data.menu.itemID= =EditGraffitiHelp) { //CH.3 Pop up the graffiti reference based on //the graffiti state SysGraffitiReferenceDialog(referenceDefault); return(true); } 下一步,你得到了文本框指针后就可以调用那些非常好的编辑命令了。下面的例子显示了一个用最普通的方法来得到文本框指针,这个方法我们在先前的c ase frmOpenEvent:中用到过。无论窗体上有多少文本框,它都可以工作。 //CH.3 Get the index of our field index=FrmGetFocus(form); //CH.3 If there is no field selected,we’re done if(index= =noFocus) return(false); //CH.3 Get the pointer of our field field=FrmGetObjectPtr(form,index); 现在我们可以执行edit命令了。调用这些编辑函数非常简单并且它们处理每一件事都比较恰当。如执行Select All命令,你只要把整个字符串传递给FldSetSelection()函数就可以了。 //CH.3 Do the edit command switch(event->data.menu.itemID) { //CH.3 Undo case EditUndo; FldUndo(field); break; //CH.3 Cut case EditCut; FldCut(field); break; //CH.3 Copy case EditCopy; FldCopy(field); break; //CH.3 Paste case EditPaste; FldPaste (field); break; //CH.3 Select All case EditSelect All; { //CH.3 Get the length of the string in the field Word length=FldGetTextLength(field); //CH.3 Select the whole string FldSetSelection(field,0,length); } break; //CH.3 Bring up the kdyboard tool case EditKeyBoard; SysKeyBoardDialog (kbdDefault); break; } //CH.3 We’re done rerurn(true); } 经过以上修改,make,debug,并运行你的应用程序。你就可以使用已创建的菜单和快捷方式了。 支持各种不同版本的Palm OS 事实上,上面的代码如果拿到Pilot 1000或者Pilot 1500这些使用Palm OS1.0版本的系统上运行的话就会使系统崩溃。这是因为在1.0版本中,SysKeyboardDialog()是一个于现在不同的函数调用。但也有好几种方法可以解决这个问题。首选就是换成调用函数S ysKeyboardDialogV10(),这是一个向后兼容的函数。除了最新的系统命令外,如果你还想了解更多的话,你就有必要检查一下OS的版本,这样可以基于O S版本来正确的调用函数。 如果我们多次遭遇由于OS版本不同带来的严重后果的话,我们在编程时将变得更加老练。现在就用SysKeyboardDialogV10()替换这个SysKe yboardDialog()函数调用。 //CH.3 Bring up the keyboard tool case EditKeyboard; SysKeyboardDialogV10(); break; 错误和警告 Contacts程序已经扩展了它的使用范围,用户对它做的某些操作可能导致错误发生。现在是一个极好的时机来讨论出错处理。 如果你执行编辑命令,就可以发现如果你做一些无意义的事,譬如在没有选中任何文本的时候复制,编辑函数也会产生一个警告声。不只是S elect All如此,因为你已经调用了一个普通函数,即便没有错误,它也会发出声音的。为了使Select All符合其它函数的发音标准,你可以加入如下的一个SndPlaySystemSound()函数: //CH.3 Select All case EditSelectAll { //CH.3 Get the length of the String in the field Word length=FldGetTextLength(field); //CH.3 Sound an error if appropriate if(legth= =0) { SndPlaySystemSound(sndError); return(false); } //CH.3 Select the whole string FldSetSelection(field,0,length); } 当用户的输入超越了被调函数的职能时,就应该提示用户他们可能做错了什么。对此,有一个很好的解决方法,就是使用Alerts。Alerts `是由操作系统控制的袖珍型窗体,创建和使用都非常的方便。 作一个试验:创建一个Alert资源,来显示Select All命令的错误信息: 1.启动资源构造器。 2.从资源类型列表中选中Alert。 3.按Ctrl-K来创建一个新的警告。 4.双击打开警告。 5.改变消息属性像“There was no text to select。” 6.改变Error的标题属性。 7.改变Error的警告类型属性。 为了调用这些Alert,我们加入FrmAlert()函数。你可以从Contacts_res.h文件中得到警告ID的变量名。 //CH.3 Pop up an error if appropriate if(length= =0) { SndPlaySystemSound(sndError); FrmAlert(SelectAllErrorAlert); return(false); } 加入上面的代码后,make,debug,并运行应用程序。可以发现,自从我们给错误设了警告类型,就有两次嘟嘟声:一个来自SndPlaySyst emSound(),一个来自警告。 另一个我们要放置Alert,至少是一次系统嘟嘟声的地方,就是当我们检查焦点的时候。事实上,当休眠状态的应用程序被唤醒的时候,文本框是获得焦点的首选控件。在一般情况下我们就想要在此时发一个经典的系统嘟嘟声来通知用户。 由于Select All的错误提示并不是经常会出现的,就让我们移走这个Alert资源。在以后的章节中,我们再使用Alert通知用户各种不同的错误信号吧。 下一步是什么? 在下一章中,我们将研究如何在你的应用程序中拥有多个窗体。 程序清单 这里是完整的Contacts.c程序清单。从hello.c改变的行都用//CH.3注解。 //CH.2 The super-include for the Palm OS #include //CH.3 Our resource file #include “Contacts_res.h” // CH.2 prototypes for our event handler functions static Boolean myHandleEvent(EventPtr enent); static Boolean menuEventHandler(EventPtr event); //CH.3 Our field memory handle static Handle htext; //CH.3 Handle to the text in our edit field #define HTEXT_SIZE 81 // CH.3 Size of our edit field //CH.2 The main entry point Dword PilotMain (Word emd,Ptr,Word) { FormPtr form; //CH.2 A pointer to our form structure*/ Handle hsrc; //CH.3 Handle to the string resource Charptr psrc; //CH.3 Points to the text in the resource Charptr ptext; //CH.3 Points to the text in the edit field Word index; //CH.3 A general purpose index FieldPtr field; //CH.3 Used for manipulating fields EventType event; //CH.2 Our eent structure Word error; //CH.3 Error word for menu event handler //CH.2 If this in not a normal launch,don’t launch if (cmd!=sysAppLaunchCmdNormalLaunch) return(0); //CH.3 Get the initialization string resource handle hsrc=DmGetResource(strRsc, FieldInitString); //CH.3 Lock the resource, get the pointer psrc= MemHandleLock(hsrc); //CH.3 Allocate our field chunk htext=MemHandleNew(HTEXT_SIZE); if (htext= =NULL) return(0); //CH.3 Lock the resource, get the pointer ptext= MemHandleLock(htext); //CH.3 Initialize it StrCopy(ptext,psrc); //CH.3 Unlock the field’s memory MemHandleUnlock(htext); //CH.3 Unlock the resource’s memory MemHandleUnlock(hsrc); //CH.3 Release the string resource DmReleaseResource(hsrc); //CH.2 Initialize our form form=FrmInitForm(ContactDetailForm); FrmSetEventHandler(form,myHandleEvent); FrmSetActiveForm(form); //CH.3 Get the index of our field index=FrmGetObjectIndex(form,ContactDetailFirstNameField); //CH.3 Get the pointer to our field field =FrmGetObjectPtr(form,index); //CH.3 Set the editable text FldSetTextHandle(field,htext); //CH.2 Draw the form FrmDrawForm(form); //CH.3 Set the focus to our field FrmSetFocus(form,index); //CH.2 Our event loop do { //CH.2 Get the next event EvtGetEvent(&envet,-1); //CH.2 Handle system events if(SysHandleEvent(&event)) continue; //CH.3 Handle menu events if (MenuHandleEvent(NULL,&event,&error)) continue; //CH.2 Handle form events FrmDispatchEvent(&event); //CH.2 If it’s a stop event,exit }while(event,eType!=appStopEvent); //CH.2 We’re done return(0); } //CH.2 Our form handler function static Boolean myHandleEvent(EvenType* event) { //CH.3 Parse menu events if (event->eType= =menuEvent) return(menuEventHandler(event)); //CH.2 We’re done return(false); } //CH.3 Handle menu events Boolean menuEventHandler(EventPtr event) { FormPtr form; //CH.3 A pointer to our form structure Word index; //CH.3 A general purpose control index FieldPtr field; //CH.3 Used for manipulating fields //CH.3 Get our form pointer form=FrmGetActiveForm(); //CH.3 Erase the menu status from the display MenuEraseStatus(NULL); //CH.3 Handle graffiti help if(event->data.menu.itemID= = EditGraffitiHelp) { //CH.3 Pop up the graffiti reference based on //the graffiti state SysGraffitiReferenceDialog(referenceDefault); Return(true); } //CH.3 Get the index of our field index=FrmGetFocus(form); //CH.3 If there is no field selected,we’re done if (index= =noFocus) return(false); //CH.3 Get the pointer of our fiedl field =FrmGetObjectPtr(form,index); //CH.3 Do the edit command switch(event->data.menu.itemID) { //CH.3 Undo case EditUndo; FldUndo(field); break; //CH.3 Cut case EditCut; FldCut(field); break; //CH.3 Copy case EditCopy; FldCopy(field); break; //CH.3 Paste case EditPaste; FldPaste (field); break; //CH.3 Select All case EditSelect All; { //CH.3 Get the length of the string in the field Word length=FldGetTextLength(field); //CH.3 Pop up an error if appropriate if(length= =0) { SndPlaySystemsound(sndError); Return(false); } //CH.3 Select the whole string FldSetSelection(field,0,length); } break; //CH.3 Bring up the kdyboard tool case EditKeyBoard; SysKeyBoardDialogv10(): Break; } //CH.3 We’re done rerurn(true); } |
本文地址:http://www.45fan.com/a/question/71334.html