关于 Modal Dialogs(Modal Form) 的三种编程方法
translated and modified by Carol
原文:http://www.palmos.com/dev/support/docs/recipes/modal_dialogs.html
在 tompda 上有更多讨论: http://www.tompda.com/bbs/display.asp?luntan=2&forumID=1660213
由简至繁,依次为:
1) 使用 FrmDoDialog, 不需要自己写事件处理程序(event handler)
这种方法最简单,但是灵活性比较差
2) 使用 FrmDoDialog, 自己要写一部分的事件处理程序
3) 使用 FrmPopupForm, 自己写独立的事件处理程序,事件循环函数
根据 form/dialog 不同复杂度,选择不同的方式来处理,事半功倍
上述三种编程方法虽然不同,但是它们的资源文件是没有什么差别的,都是选用 modal form 来建造的
假设我们要做的是在 MainForm 上按 "Details", 驱动打开 DetailsForm
Option 1: 不需要写事件驱动的 FrmDoDialog
在 MainFormHandleEvent 里面加上这一段:
if (eventP->data.ctlSelect.controlID == MainDetailsButton) {
frmP = FrmInitForm(DetailsForm);
/* initialize your controls on the form here */
在这里加上form的控件初始化
// open the dialog, and wait until a button is pressed to close it.
// 这一句 FrmDoDialog 打开了 DetailsForm,form 会在你按了上面的 button 以后退出来, // 返回的值 whichButton 告诉你按的是哪个 button.
whichButton = FrmDoDialog(frmP);
if (whichButton == DetailsOKButton) {
/* get data from controls on the form to save/apply changes */
// 根据不同的返回值,获取处理DetailsForm里的改变
}
FrmDeleteForm(frmP);
handled=true;
}
Option 2: 带事件处理的 FrmDoDialog
这种方式的 modal form,可以使开发者依赖FrmDoDialog处理大部分工作,同时处理一些自己比较关心的事件。这种方法使用和 option 1 一样的代码,只是在 FrmDoDialog 调用前再加上下面这句:
FrmSetEventHandler(frmP, DetailsPartialEventHandler);
FrmDoDialog 会在处理事件之前,调用DetailsPartialEventHandler来处理你定义的事件
static Boolean DetailsPartialEventHandler(EventPtr eventP)
{
Boolean handled = false;
if ((eventP->eType == ctlSelectEvent) &&
(eventP->data.ctlSelect.controlID == DetailsOKButton)) {
if (ValidationFailsOnTheInput()) {
FrmAlert(PleaseFixYourInputAlert);
// tell FrmDoDialog we handled this event, so it
// shouldn't do anything
handled=true;
}
}
return handled;
}
注意,如果在这个form里要进行通讯,或为处理某些后台事件而要延迟, 就不要是用 FrmDoDialog.
FrmDoDialog 的好处就是你可以少写代码,而这些代码都集中在程序的一处。(还是看英文原文比较好:FrmDoDialog runs in its own event loop instead of yours, giving you slightly less control over event handling than you normally have. If you are doing communications or are using event timeouts for some background processing or updating, FrmDoDialog is not a good implementation for your dialog. The benefit of FrmDoDialog is that you need to write less code and that code is located in only one place in your application. )
Option 3: 使用完整独立的事件处理
这种方法具有最大程度的灵活性也最复杂,可以把它当作一个通常的 form 来处理。
建立事件处理函数:
static Boolean DetailsHandleEvent(EventPtr eventP)
{
Boolean handled = false;
FormPtr frmP;
switch (eventP->eType) {
case frmOpenEvent:
frmP = FrmGetActiveForm();
/* initialize UI objects here */
// 我还在这里加了个函数, 进行初始化的处理,就像 MainFormInit() 那样。
FrmDrawForm(frmP);
handled = true;
break;
case ctlSelectEvent:
if (eventP->data.ctlSelect.controlID == DetailsOKButton) {
/* save or apply the changes now */
FrmReturnToForm(0);
handled = true;
break;
}
else if (eventP->data.ctlSelect.controlID == DetailsCancelButton) {
/* don't save changes, just return. */
FrmReturnToForm(0);
handled = true;
break;
}
default:
break;
}
return handled;
}
注意点:要使用 FrmReturnToForm() 来关闭这个form, 并返回到之前的活动form, 因此保存数据等工作也记得在这个 handleEvent 里一并完成(而不是在 main form 的代码里)。
上面这个函数,是在 AppHandleEvent 里面被调用的:
case DetailsForm:
FrmSetEventHandler(frmP, DetailsHandleEvent);
break;
把它加在和 case MainForm: 并列的位置上。
最后,要显示这个 form, 在处理 button 事件时,要使用 FrmPopupForm,而不是 FrmDoDialog
FrmPopupForm 会产生 loadEvent 和 openEvent 两个事件。
if (eventP->data.ctlSelect.controlID == MainDetailsButton) {
MenuEraseStatus(0); // in case the status is still active
// Some apps might want to save the current focus here.
FrmPopupForm(DetailsForm);
handled=true;
}