Modalなフォームを使ってみる


HPのTOPに戻る
Basic BASIC別館のTOPに戻る

現在Modal Formとメニューで不具合が発生する事が確認されています
とりあえず、サンプルプログラムの方には修正したモノを収録しています

まずどんなサンプルアプリを作るのかというと、
親フォームにSelectorオブジェクトListオブジェクトFieldオブジェクトを配置し\ます。

Selectorオブジェクトをタップすると、今回の肝であるModalな子フォームを表示して
Selectorオブジェクトに貼り付けるデータを選択するようにします。

Listオブジェクトは適当なダミーデータを表示するようにします。
Fieldオブジェクトは今日の日付を表示するようにします。

もひとつオマケにメニューボタンを押すと、またまたModalなInfomationフォームを表示するようにしてみます。

この親フォルダは、Parentとプログラム中では命名する事にします
次に2つの子フォームですが、
Selectorオブジェクトをタップする事で呼ばれる子フォームは、Childとプログラム中で命名します。
もう1つのメニューボタンから呼ばれる子フォームは、Infoとプログラム中で命名します。

画面イメージ
親フォーム
Name:Parent
子フォーム1
Name:Child
子フォーム2
Name:Info

これらの1つの親フォームと、2つの子フォームでModalフォームのテストをしてみます。

※親フォーム:Modalなフォームを呼ぶ元になるフォームなので、便宜上「親」とします。
 子フォーム:Modalなフォームです。上記で定義した親から呼ばれるので「子」とします。

サンプルプログラム
ビットマップはbitmapフォルダに入れて下さいな。


早速、コードの説明です。

Startup Code
    Global PopMode as Integer    '---- ModalFormの状態チェック用
    Global PopForm as Short      '---- Modal表示させるFormのIDを格納(必ずShort型)
    
    Global theData as Integer    '---- テスト用のデータを格納する変数
    theData=10                   '---- テスト用データの初期値
ここで、必要な変数を定義しています。

ここで定義されているPopMode変数が重要でしょうか?
変数の中身によって以下の状態を判定します
PopMode:0 子フォームが表示されていない時
PopMode:1 子フォームから親フォームに戻ってくる時
PopMode:2 子フォームを表示している時

親フォームのBeforeコード
    Select Case PopMode               '---- 現在のPopModeによって処理を振り分けます
    Case 2                            '---- PopMode=2
        If PopForm=1005 Then          '---- 現在表示中の子フォームを判定します
            Call Child_Before()       '---- ChildフォームのBeforeをCallします
        End If
        Exit Sub                      '---- このParent_beforeを抜けます
    Case 1                            '---- PopMode=1
        Exit Sub                      '---- 処理せずにこのParent_Beforeを抜けます
    End Select

    '---- 以下はダミーデータをDummyListに追加する処理です
    Dim I as Integer
    
    DummyList.Clear
    For I=1 to 200
        DummyList.Add "TestData"+Format(I,"000"),,NoDisplay
    Next
    DummyList.Selected=1

呼び出された子フォーム上では、
子フォームのBefore,After,Eventsのイベントが発生した時でも
子フォームのコードが呼ばれる事がありません。

子の親となるフォームのBefore,After,Eventsのイベントが発生したと見なされ、
親フォームのイベントが呼ばれます。

その為、子フォームを呼ぶ可能性のある親フォームでは、
Before,After,Eventsイベントに子フォームのコードを記述する必要があります。
ただ、親フォームに子フォームのコードもダラダラと書いてしまうと、
見づらくなる可能性があるのでこのサンプルの場合には、
親フォームのイベントから子フォームのイベントをCall文で呼び出しています。

PopMode:2の時は子フォームが表示される時のBeforeイベントになるので、
    子フォームのBeforeイベント「Child_Before()」を呼び出しています
    その後、Child_Before()が終了すると戻ってきますが、
    これ以上このイベントを実行するとおかしくなるので
    Exit Subでこのイベントを抜けています。

PopMode:1の時は子フォームから親フォームへ戻って来た時の処理です。
    何もせずにExit Subしています。
    なぜ、こんな事をしているのかというと...
    このExit Subに注釈をつけると良く判ると思います。
    当然、子フォームのAfterイベントに何かイベントがあれば、
    ここから飛ばしてやる必要があります。

親フォームのAfterコード
    Select Case PopMode                 '---- 現在のPopModeによって処理を振り分けます
    Case 2                              '---- PopMode=2
        If PopForm=1014 Then            '---- 現在表示中の子フォームを判定します
            Call Info_After()           '---- InfoフォームのAfterをCallします
        End If
        Exit Sub                        '---- 処理せずにこのParent_Afterを抜けます
    Case 1                              '---- PopMode=1
        If PopForm=1005 Then            '---- 現在表示中の子フォームを判定します
            Selector.Text=Str(theData)  '---- 子フォームで選択されたデータの値を/
                                        '---- Selectorオブジェクトにセットします
        End If
        PopMode=0                       '---- PopModeを0にクリアします
        Exit Sub                        '---- このParent_Afterを抜けます
    End Select
    Selector.Text=Str(theData)          '---- Selectorに値をセットします(初期処理)
    DummyDate.Text=DateMMDDYY(ToDay())  '---- 現在の日付をDummyDateにセットします(初期処理)
このイベントでも、親フォームのBeforeイベントと同じように
子フォームのイベントへ割り振る為にPopFormの値によって処理の振り分けを行っています。


PopMode:2の時は何もせずにこのイベントを抜けます。
    なぜかというと...
    このExit Subに注釈を付けると判りますが、
    子フォームに無いハズのSelectorオブジェクトが表示されたりしてしまいます。

PopMode:1の時は子フォームで選択した値を、Selectorオブジェクトにセットします。
    その後、子フォームから親フォームへの復帰が終了したとしてPopModeを0にクリアしています。
    そしてその後、余計な処理をしないようにExit Subしています。

親フォームのEventコード
    Select Case PopMode                 '---- 現在のPopModeによって処理を振り分けます
    Case 2                              '---- PopMode=2
        If PopForm=1014 Then            '---- 現在表示中の子フォームを判定します
            Call Info_Event()           '---- InfoフォームのEventをCallします
        End If
        Exit Sub                        '---- 処理せずにこのParent_Eventを抜けます
    Case 1                              '---- PopMode=1
        Exit Sub                        '---- 処理せずにこのParent_Eventを抜けます
    End Select
    If GetEventType()=1 Then            '---- 親フォーム上でのイベントを判定
        If Asc(GetKey())=5 Then         '---- メニューボタンがタップされたか判定
            PopMode=2                   '---- PopModeに2をセットします
            PopForm=1014                '---- これから呼び出すModalフォームのIDをセットします
            SysTrapSub 412,1,PopForm    '---- APIの412を呼び出して、子フォームを表示します
        End If
    End If
このイベントでも、親フォームのBeforeイベントと同じように
子フォームのイベントへ割り振る為にPopFormの値によって処理の振り分けを行っています。

Infoフォーム表示中のイベントもここで判定されます。
その場合、Info_EventをCallします。

親フォーム上のSelectorオブジェクトのイベント
※ココで子フォーム(Modalなフォーム)を呼び出します。
    PopMode=2                  '---- PopModeに2をセットします
    PopForm=1005               '---- これから呼び出すModalフォームのIDをセットします
    SysTrapSub 412,1,PopForm   '---- APIの412を呼び出して、子フォームを表示します
ここで、子フォームを呼び出しています。

PopModeに2をセットして、子フォームが表示中であるというフラグを立てます。

PopFormには呼び出す子フォームのIDをセットします。
このPopForm変数はShort型で無いと駄目なようです。

SysTrapSubで実際にAPIを使って子フォームを呼び出します。
※NextFormで呼び出しても子フォームは表示されますが、この場合、Modalなフォームにはなりません。

子フォーム(Child)のBeforeコード(通常では呼び出される事が無い部分です)
    Dim I as Integer
    
    DataList.Clear
    For I=1 to 20 
        DataList.Add Format(I,"00"),,NoDisplay
    Next
    DataList.Selected=theData
子フォームのBeforeイベント部分ですが、
Modalなフォームとして呼ばれた場合には、
このイベントは通常呼ばれる事がありません。
※子フォームが呼ばれてもこのBeforeイベントは呼ばれずに、
 親フォームのBeforeイベントが呼ばれてしまいます。

 その為、このプログラムの場合には、親フォームのBeforeイベント上で
 子フォーム表示の時に呼ばれたのかを判定して
 このイベントを呼び出すようにしています。

※別にやっている処理自体は何でも無い処理なので説明は省きます。

子フォーム(Child)上のButtonイベント
※ココで子フォームから親フォームに戻ります
    If DataList.Selected=0 Then Exit Sub           '---- リストが選択されていない場合はサブルーチンを抜けます
    theData=Val(DataList.Text(DataList.Selected))  '---- リストの選択された値を変数theDataに保存します
    PopMode=1                                      '---- PopModeに1をセットします
    NextForm "Parent"                              '---- 親フォームを表示します。
このボタンを押す事で、選択されたデータを変数theDataに格納します
そして子フォームから親フォームに戻るのを、識別する為にPopModeに1をセットします
最後にNextFormで親フォームを呼び出します。
※その後、親フォームのBeforeイベント→親フォームのAfterイベントへと進みます。

子フォーム(Info)のAfterイベント(通常では呼び出される事が無い部分です)
    DrawBitmap 1016,12,18           '---- BitマップをChildフォームは貼り付けます
    DrawChars "ソフト(笑)",10,40    '---- 文字列をChildフォームに貼り付けます
何て事は、無い処理です。
何も処理しないと、サンプルになりませんので...
※ちなみにModalフォームを使えば、こんな画像を使ったAbout画面を作る事も出来ますね。

子フォーム(Info)のEventイベント
    If GetEventType()=2 Then    '---- スクリーンのタップを判定
        PopMode=1               '---- PopModeに1をセット
        NextForm "Parent"       '---- 親フォームを表示します
    End If
スクリーンをタップすると親フォームに戻ります。
※この場合だと、どこをタップしても反応します。
 (子フォーム以外の場所でもです。)
 子フォームをタップして親フォームに戻るような処理を組む時には
 タップした位置を判定してあげるとよいでしょう

子フォームから親フォームに戻るのを、識別する為にPopModeに1をセットします
そしてNextFormで親フォームを呼び出します
※その後、親フォームのBeforeイベント→親フォームのAfterイベントへと進みます。

ややこしいようですが、基本はこうです。

子フォームがModalフォームとして呼ばれると
Before、After、Eventのイベントは子フォームのイベントとして処理されずに
親フォームのイベントとして認識されてしまう。
その為、親フォームの各イベントで子フォームのイベント発生時のコーディングを行う必要がある。

このプログラムでは、子フォームが開いているかどうかを、PopModeという変数を使って識別しています。

ならば、子フォームは開いていないので、通常の親フォームの処理を行う。
ならば、子フォームの終了処理中なので、対応するコードを実行する。
ならば、子フォームが開いているので、子フォームの各イベントを実行させる。

というような感じで処理しています。

表にしてみると以下のような感じでしょうか?
親フォームオープン親フォームのBeforeイベント発生
親フォームのAfterイベント発生
親フォームでイベント発生親フォームのEventイベント発生
子フォームオープン親フォームのBeforeイベント発生
親フォームのAfterイベント発生
子フォームでイベント発生親フォームのEventイベント発生

見てもらえれば判るように、子フォームでイベントが発生しても、まったく子フォームのイベントが呼ばれる事がありません。
これが、Modalフォームを使った時の厄介毎の全てです。
そこで、サンプルプログラムでは以下のように子フォームの各イベントを親フォームからCallする事で対応しています

親フォームオープン親フォームのBeforeイベント発生
親フォームのAfterイベント発生
親フォームでイベント発生親フォームのEventイベント発生
子フォームオープン親フォームのBeforeイベント発生→ 子フォームのBeforeイベントをCall
親フォームのAfterイベント発生→ 子フォームのAfterイベントをCall
子フォームでイベント発生親フォームのEventイベント発生→ 子フォームのEventイベントをCall

※別に子フォームのコードを親フォームに記述しても良いのですが、親フォームのイベントだけグチャグチャになって見難くないですか?
 各フォーム毎にコードを分散する事で見易くなっていると思うのですが、どうでしょう?

- Page End -