| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1253 人关注过本帖, 2 人收藏
标题:VFP学习、开发漫谈 (12)
取消只看楼主 加入收藏
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:658
专家分:2180
注 册:2014-2-7
结帖率:96.88%
收藏(2)
已结贴  问题点数:20 回复次数:1 
VFP学习、开发漫谈 (12)
今天与大伙聊一聊有关“表单集”的使用问题。

处理表单集要比处理单个表单复杂,并且表单的某些属性,如:WindowType、WindowState等,与单个表单的对应属性不同,所以能用一个表单处理的,就尽可能不用表单集。

比如,我在设计较复杂的查询表单时,通过在表单上添加一个 PageFrame,在 Page1 上设置查询条件,在 Page2 上显示查询结果,这样就可以不使用表单集了。

我在开发应用程序时,经常使用表单集。使用表单集的最大好处是:所有表单共享一个数据环境,数据处理起来比较方便。并且将相关功能的表单归集在一起,修改起来也比较方便。

比如,我要设计一个采购计划表单,如下所示:
图片附件: 游客没有浏览图片的权限,请 登录注册

该表单集包含2个表单:frmPlan(主表单)和 frmPlanEdit(副表单)。主表单的上半部分显示计划的整体信息,下半部分是一个表格,用于显示计划中的材料明细,这是一个典型的“一对多”表单。在向计划添加材料时,若直接在表格中输入,由于版面所限,很难在表格中设计一个友好的输入界面。若采用表单集,在另一个表单中输入材料(上图中右边窗口),就容易多了。该方法可以连续输入,且输入的每一条记录,都及时显示在主表单中,利于掌握输入进度。

初始化时,仅显示主表单,而隐藏副表单(设置表单的 Visible=.f.)。在关闭主表单时,释放表单集,但在关闭副表单时,应该取消关闭操作,改为隐藏表单。

要实现在关闭主表单时释放表单集,可在主表单的 Destroy 事件中输入:THISFORMSET.Release。

要实现在关闭副表单时不释放表单,而是隐藏表单,可在副表单的 QueryUnload 事件中,输入:
    NODEFAULT
    THIS.Hide

表单的 QueryUnload 事件比较特殊。当执行命令 THISFORM.Release 时并不激活 QueryUnload 事件,只有在单击窗口右上角的“关闭”按钮时,才激活该事件。在 QueryUnload 事件中输入NODEFAULT 可以阻止窗口被释放。

一般情况下,我们在操作副表单时,不希望用户去操作主表单。有用户可能会说:“这还不简单!将副表单设为模式表单,即:WindowType=1就行了”。如果你这样认为,就错了。表单集中的所有单个表单,都是无模式的,它会忽略 WindowType 设置。表单集也有 WindowType 属性,可将整个表单集设为模式。这样设置后,可以在表单集中的单个表单之间切换,但不可以切换到其他表单。所以,我一般通过设置主表单的 Enabled=.f.,来禁用主表单。当关闭副表单时,再将主表单的 Enabled 设为 .t.

当主表单和副表单同时显示在屏幕上时,调整二者之间的位置很重要。为了防止主表单最大化时,遮挡住副表单,我一般设置副表单的 AlwaysOnTop=.t.,使其显示在顶层,同时将副表单移至屏幕左上角(THISFORMSET.frmPlanEdit.Move(0,0)),主表单居中(AutoCenter=.t.)。如果要将主表单oFrm移至屏幕右下角,可输入:oFrm.Move(_Screen.Width-oFrm.Width-8,_Screen.Height-oFrm.Height-32)。

我们知道,对于单个表单若要接收参数,需将 LPARAMETERS 语句放在表单的 Init 事件中,若表单要返回值,需要设置表单为模式表单,同时在表单的 Unload 事件中返回值。调用表单时,使用“DO FORM 表单名 WITH 参数 TO 变量”命令格式。但使用表单集时,若表单要接收参数,必须将 LPARAMETERS 放在表单集的 Init 事件中。同理,若要返回值,必须将“Return <值>”放在表单集的 Unload 事件中(需设置表单集为模式)。

再看一个问题:保持表单集的 WindowType=0(无模式),设置表单的 WindowState=2(最大化),运行表单,我们发现表单并没有最大化。

现在,我们将表单集的 WindowType 设为 1(模式),再运行一下表单,我们发现表单被最大化了。

结论:当表单集为非模式时,若要将表单最大化,可在表单的 Init 事件中输入“ZOOM WINDOW (THIS.Name) MAX”。

在 VFP下建立一个“窗口”菜单,对于大多数用户来说应该不难。操作步骤:

1. 指定菜单的 Prompt 为“窗口(\<W)”,Result 为“Submenu”,单击“Create”打开建立子菜单窗口
2. 选择菜单“View”-“Menu Option”,在 Name 文本框中输入“_MWINDOW”,单击“OK”按钮
3. 单击“Insert Bar”按钮,选择其中的“Cascade(层叠窗口)”、“Arrange All(平铺窗口)”等,单击“Insert”

上述操作的关键之处是:将菜单的 POPUP 命名为 _MWINDOW,这样,所有打开的窗口以列表的形式自动显示在“窗口”菜单的下方。

采用上述方法建立的窗口菜单有一个缺陷:表单集中被隐藏的表单,也显示在窗口菜单中,这显然不符合我们的要求。如何在窗口菜单中仅显示可见窗口呢?

我的解决方案稍显苯拙,期待高手们能给出更好的解决方法:

1. 利用菜单设计器在主菜单中添加“窗口(\<W)”
2. 将窗口菜单的 POPUP 命名为“_win”
3. 通过“Insert Bar”按钮,为窗口菜单添加 2 个菜单项,一个是“层叠窗口(\<C)”,另一个是“平铺窗口(\<A)”
4. 在过程文件中,添加一个自定义函数 ActivateMenu():
程序代码:
* 功能:激活指定的窗口
* 参数:tcCaption - 窗口标题
FUNCTION ActivateWindow(tcCaption)
    LOCAL cWName,oForm
    * 提取窗口标题
    tcCaption = SUBSTR(tcCaption,AT(' ',tcCaption)+1)
    * 找到对应的窗口名称
    FOR EACH oForm IN _Screen.Forms
        IF oForm.BaseClass = 'Form' AND oForm.Visible AND oForm.Enabled AND oForm.Caption == tcCaption
            cWName = oForm.Name
            EXIT
        ENDIF
    NEXT
    * 根据名称激活窗口
    IF !EMPTY(cWName)
        ACTIVATE WINDOW (cWName) TOP
        IF WMINIMUM(cWName)
            ZOOM WINDOW (cWName) NORM
        ENDIF
    ENDIF
ENDFUNC

5. 建立一个基于 Custom 的类,命名为:SetMenu
6. 为类新建一个方法:AddMenu,用于添加窗口菜单项,该方法包含的代码如下:
程序代码:
* SetMenu.AddMenu

* 添加分隔线
IF CNTBAR('_win') = 2
    DEFINE BAR 3 OF _win PROMPT '\-'
ENDIF

* 标记当前窗口菜单项
LOCAL i,cPrompt,lFlag,nBar
FOR i = 4 TO CNTBAR('_win')
    cPrompt = PRMBAR('_win',i)
    cPrompt = SUBSTR(cPrompt,AT(' ',cPrompt)+1)
    IF cPrompt == THISFORM.Caption
        SET MARK OF BAR i OF _win TO .t.
        lFlag = .t.
    ELSE
        SET MARK OF BAR i OF _win TO .f.
    ENDIF
NEXT

* 菜单项不存在时,添加之
IF !lFlag
    nBar = CNTBAR('_win') + 1
    cPrompt = '\<' + TRAN(nBar-3) + ' ' + THISFORM.Caption
    DEFINE BAR nBar OF _win PROMPT cPrompt
    ON SELECTION BAR nBar OF _win ActivateWindow(PROMPT())
    SET MARK OF BAR nBar OF _win .t.
ENDIF

7. 为类再新建一个方法:RemoveMenu,用于移除窗口菜单项,包含的代码如下:
程序代码:
* SetMenu.RemoveMenu

* 找到移除位置 nBar
LOCAL nCntBar,i,nBar,cPrompt
nCntBar = CNTBAR('_win')
FOR i = 4 TO nCntBar
    cPrompt = PRMBAR('_win',i)
    IF SUBSTR(cPrompt,AT(' ',cPrompt)+1) == THISFORM.Caption
        nBar = i
        EXIT
    ENDIF
NEXT

* 移除菜单项,并调整其他菜单项之序号
IF !EMPTY(nBar)
    FOR i = nBar TO nCntBar-1        && 将后面的菜单项序号前移
        cPrompt = PRMBAR('_win',i+1)
        cPrompt = SUBSTR(cPrompt,AT(' ',cPrompt)+1)
        DEFINE BAR i OF _win PROMPT '\<'+TRAN(i-3)+' '+cPrompt
        ON SELECTION BAR i OF _win ActivateWindow(PROMPT())
    NEXT
    RELEASE BAR nCntBar OF _win        && 移除最后一个菜单项
ENDIF

* 移除分隔条
IF CNTBAR('_win') = 3
    RELEASE BAR 3 OF _win
ENDIF

8. 在类的 Init 事件中,输入以下代码,当表单激活时添加菜单,当隐藏表单时移除菜单:
程序代码:
* SetMenu.Init

* 当激活表单时添加窗口菜单项,隐藏表单时移除窗口菜单项
IF TYPE("CNTBAR('_win')") = 'N'
    BINDEVENT(THISFORM,'Activate',THIS,'AddMenu')
    BINDEVENT(THISFORM,'Hide',THIS,'RemoveMenu')
ENDIF

9. 在类的 Destroy 事件中,输入以下代码,当释放表单时,移除菜单项:
程序代码:
* SetMenu.Destory

* 释放菜单项
IF THISFORM.Visible AND TYPE("CNTBAR('_win')") = 'N'
    THIS.RemoveMenu
ENDIF

10.将类添加到所有表单中。在我开发的应用程序中,由于表单均基于自定义类,所以我只需将 SetMenu 类添加到表单类即可。

特别说明:

1.之所以将代码封装在类中,是为了使用方便,无需对表单做任何修改,只需将类添加到表单即可。
2.第4步中之所以对 SCREEN 中 Form 的基类进行判断,是因为工具栏也是窗口,但其基类是 Toolbar 而不是 Form。另外,只激活可见的、没有禁用的窗口。
3.第8步和第9步中,对“_win”菜单是否存在进行了判断,这是为了在开发环境中对表单进行调试时系统不报错。


[ 本帖最后由 liuxingang28 于 2014-3-21 15:19 编辑 ]
搜索更多相关主题的帖子: 应用程序 开发 
2014-03-21 13:27
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:658
专家分:2180
注 册:2014-2-7
收藏
得分:0 
有时候,多窗口是必须的,并不是页框能解决的。页框有它的局限性,主要是只能同时显示一页。若各页的内容相对独立,使用页框是合适的。如果在某一页面输入内容时,需实时查看到另一页面的内容,就必须使用多窗口。

在使用多窗口时,确实可以用相对独立的多个表单来代替表单集。我认为,如果相关的表单仅用于实现某个单一功能,这些表单并不被其他模块调用,还是将这些表单放在一个表单集中好些。如果某个表单用于多个功能模块,则不将其放在表单集中,而是将其独立出来比较好。VFP 既然推出表单集,肯定有它的道理。

泉城飞狐
2014-03-22 14:07
快速回复:VFP学习、开发漫谈 (12)
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.020260 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved