タブページから開くモーダルウインドウ

ActiveBasicでのプログラミングでわからないこと、困ったことなどがあったら、ここで質問してみましょう(質問を行う場合は、過去ログやWeb上であらかじめ問題を整理するようにしましょう☆)。
メッセージ
作成者
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

タブページから開くモーダルウインドウ

#1 投稿記事 by jacoby »

タブウインドウ(タブコントロール)のテストをしています。

「タブウインドウのページ上に配置したボタンを押すと、
モーダル・ダイアログを開く」、ということを実装したいのですが、
このモーダルダイアログを閉じた後、戻ったタブページ上のコントロールが
全く効かなくなってしまいます。
(つまりこの「モーダル・ダイアログを開くボタン」も再び押せなくなってしまいます。)

テストしてみたところでは、ダイアログを閉じた後では、
戻っているはずのタブページのダイアログ・プロシージャに
以降メッセージが殆ど送られなくなるようです。
(例えばマウス・ポインタを上にかざして動かした時に送られるマウスムーブなども
飛んでこなくなります。)

モーダルダイアログの代わりに、メッセージボックスやOpenFileDialogなどの
コモンダイアログなどを開いた場合にはその後の動作に問題はありません。

ダイアログを閉じた後、再びページ上のコントロールが効くようにするには
どうしたら良いのでしょうか?
お知りの方がおられましたら教えて下さい。(AB4.24/Windows7)



長くなってしまいますが以下にコードを書いておきます。
(プロジェクト・ファイル"TabTest.pj")

タブウインドウを乗せるベース・ウインドウ(MainWnd)、
そのクリエイト内でタブウインドウ(TabWnd)を作り(2ページ分)、
1ページ目(Tab_Page1)にボタンを配置。
ボタンを押すとモーダルダイアログ(ModalDlg)を開く
という内容になっています。

新規プロジェクトで自動的に生成されるMainWndの他には
タブページ1用のウインドウ(Tab_Page1)を「モードレス」、「チャイルド」、「枠無し」で追加。
モーダル用のダイアログ(ModalDlg)を「モーダル」で追加しています。


MainWnd.sbp

コード: 全て選択


'-----------------------------------------------------------------------------
'  イベント プロシージャ
'-----------------------------------------------------------------------------
' このファイルには、ウィンドウ [MainWnd] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hMainWnd

' TODO: この位置にグローバルな変数、構造体、定数、関数を定義します。

Const TCIF_TEXT             = &H0001

Const TCM_FIRST             = &H1300
Const TCM_INSERTITEM        = (TCM_FIRST + 7)
Const TCM_GETCURSEL         = (TCM_FIRST + 11)
Const TCM_SETCURSEL         = (TCM_FIRST + &H0C) '=&H130C
Const TCM_ADJUSTRECT        = (TCM_FIRST + &H28) '=&H1328

Const TCN_FIRST             = (0-550)         As DWord
Const TCN_SELCHANGE         = (TCN_FIRST - 1) As DWord
Const TCN_SELCHANGING       = (TCN_FIRST - 2) As DWord

Const TCS_FLATBUTTONS       = &H0080
Const TCS_BUTTONS           = &H0100

Type TCITEM
    mask As DWord
    dwState As DWord
    dwStateMask As DWord
    pszText As BytePtr
    cchTextMax As Long
    iImage As Long
    lParam As DWord
End Type

'タブページ用文字列。
Const Tab_Page1_Str="Page1"
Const Tab_Page2_Str="Page2"

'タブウインドウのハンドル、開いているページ番号、サイズ。
Dim hTabWnd As HWND
Dim tabPageNum As Long
Dim tabWndRc As RECT

'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数

Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
	' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。

	' イベントプロシージャの呼び出しを行います。
	MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function


'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。

'
'デストロイ・イベント。
'
Sub MainWnd_Destroy()
	TabTest_DestroyObjects()
	PostQuitMessage(0)
End Sub

'
'クリエイト・イベント。
'
Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)

 'タブウインドウのサイズセット。
 GetClientRect(hMainWnd, tabWndRc)
 Dim tabWndMaginV=12 As Long 'タブウインドウ周囲の(MainWnd上での)余白の大きさ。
 tabWndRc.left=tabWndRc.left+tabWndMaginV
 tabWndRc.top=tabWndRc.top+tabWndMaginV
 tabWndRc.right=tabWndRc.right-tabWndMaginV-tabWndRc.left
 tabWndRc.bottom=tabWndRc.bottom-tabWndMaginV-tabWndRc.top-48 '「-48」の意味はOKボタンを表示するのウインドウ下部領域を空けるため。

 'タブウインドウを作成する。
 hTabWnd=CreateWindowEx(NULL,_
	"SysTabControl32",_
	"",_					'タイトル(使用しない)
	WS_CHILD Or WS_VISIBLE,_'スタイル
	tabWndRc.left, tabWndRc.top, tabWndRc.right, tabWndRc.bottom,_		'位置とサイズ
	hMainWnd,_		'親ウィンドウのハンドル
	NULL,_			'ID
	GetModuleHandle(0),_	'インスタンスハンドル
	NULL)

 'フォントを設定(タブの初期フォントはMainWndとは違うのでここでセットが必要)
 SendMessage(hTabWnd, WM_SETFONT, hFont_MainWnd, 0)

 'タブ項目の追加。
 Dim tci As TCITEM
 tci.mask=TCIF_TEXT 
 tci.pszText=Tab_Page1_Str
 SendMessage(hTabWnd, TCM_INSERTITEM, 0, VarPtr(tci))
 tci.pszText=Tab_Page2_Str
 SendMessage(hTabWnd, TCM_INSERTITEM, 1, VarPtr(tci))

 'タブ表示領域の取得。
 tabWndRc.left=0 '上でセットしたウインドウ左上頂角座標が残っているので
 tabWndRc.top=0  'TCM_ADJUSTRECTを呼ぶ前にそれらをクリア
 SendMessage(hTabWnd,TCM_ADJUSTRECT,FALSE,VarPtr(tabWndRc))
 tabWndRc.right=tabWndRc.right-tabWndRc.left
 tabWndRc.bottom=tabWndRc.bottom-tabWndRc.top

 '開くタブページ番号のセット。
 tabPageNum=0
 SendMessage(hTabWnd, TCM_SETCURSEL, tabPageNum, 0)

 Select Case tabPageNum
   Case 0
     'ページ1のクリエイト。
     hTab_Page1=CreateDialog(hTabWnd,"Tab_Page1")
     ShowWindow(hTab_Page1,SW_SHOW)
   Case 1
     'ページ2のクリエイト。

 End Select

End Sub

'
'タブコントロール通知イベント
'
Sub MainWnd_Notify(ByRef nmHdr As NMHDR)

 Select Case nmHdr.code
    Case TCN_SELCHANGING 'タブがこれから切り替わる時。
       Select Case TabCtrl_GetCurSel(nmHdr.hwndFrom)
          Case 0
              'ページ1を隠す。
              ShowWindow(hTab_Page1,SW_HIDE)
           Case 1
                 'ページ2を隠す。
       End Select

    Case TCN_SELCHANGE 'タブが切り替わった時。
       Select Case TabCtrl_GetCurSel(nmHdr.hwndFrom)
          Case 0
              'ページ1のタブに替わったときの処理。
              if hTab_Page1=0 Then hTab_Page1=CreateDialog(hTabWnd,"Tab_Page1")
              ShowWindow(hTab_Page1,SW_SHOW)
              tabPageNum=0
          Case 1
               'ページ2のタブに替わったときの処理。
       End Select
 End Select

End Sub

Function TabCtrl_GetCurSel(hWnd As HWND) As Long

 TabCtrl_GetCurSel=SendMessage(hWnd, TCM_GETCURSEL, 0, 0)

End Function
Tab_Page1.sbp

コード: 全て選択


'-----------------------------------------------------------------------------
'  イベント プロシージャ
'-----------------------------------------------------------------------------
' このファイルには、ウィンドウ [Tab_Page1] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hTab_Page1

' TODO: この位置にグローバルな変数、構造体、定数、関数を定義します。


'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数

Function Tab_Page1Proc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
	' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。

	' イベントプロシージャの呼び出しを行います。
	Tab_Page1Proc=EventCall_Tab_Page1(hWnd,dwMsg,wParam,lParam)
End Function


'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。
'
'クリエイト・イベント
'
Sub Tab_Page1_Create(ByRef CreateStruct As CREATESTRUCT)
  'タブページのウインドウ位置セット。
 MoveWindow(hTab_Page1, tabWndRc.left,tabWndRc.top,tabWndRc.right,tabWndRc.bottom,FALSE)

End Sub

'
'ページ1上のボタンが押されたときの処理。
'  (モーダルダイアログを表示。)
'
Sub Tab_Page1_CommandButton1_Click()

 DialogBox(hTab_Page1, "ModalDlg")
 'ここでモーダルダイアログから戻ってきた後、
 'その後ページ1上の全てのコントロールが効かなくなる。
 '(例えばここのボタンも再び押せなくなる。)

 'MessageBox(hTab_Page1, "test", "test",MB_OK)
 'モーダルダイアログの代わりに、上記のようにメッセージボックスや
 'OpenFileDialogなどのコモンダイアログなどを表示した場合には
 'その後の動作に問題は無い。

End Sub
ModalDlg.sbp (これについては実質、空です。)

コード: 全て選択


'-----------------------------------------------------------------------------
'  イベント プロシージャ
'-----------------------------------------------------------------------------
' このファイルには、ウィンドウ [ModalDlg] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hModalDlg

' TODO: この位置にグローバルな変数、構造体、定数、関数を定義します。


'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数

Function ModalDlgProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
	' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。

	' イベントプロシージャの呼び出しを行います。
	ModalDlgProc=EventCall_ModalDlg(hWnd,dwMsg,wParam,lParam)
End Function


'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。
たかせ
記事: 217
登録日時: 2006年2月05日(日) 17:10
お住まい: 東京都

Re: タブページから開くモーダルウインドウ

#2 投稿記事 by たかせ »

お久しぶりです。

上記の現象を私のPC(Windows 7 SP1/X86)にて確認しました。
上記の現象についてなぜそうなるか私にはわかりません。
またネットで検索すると何らかの解決方法があるようですが私にとっては難しい内容でした。
そこでモーダル用のダイアログ(ModalDlg)を「モーダル」から「モードレス」に変更します。
特に理由がなければ「モードレス」のダイアログで良いと私は思いますがいかがでしょうか?
よろしくお願いいたします。
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

Re: タブページから開くモーダルウインドウ

#3 投稿記事 by jacoby »

たかせさん、返信ありがとうございます。
随分とここの板ともご無沙汰してしまっていたので
まだ質問しても大丈夫なのかな?と思っていたのですが
たかせさんやイグトランスさんの名前を見つけて、ホッとしつつ
質問を書かせてもらった次第です。こちらの返信が遅れてしまい
申し訳ありません。お久しぶりです。

 そうですか、少し難しい問題のようですね。
あんまりタブページからモーダルって一般的には開かないものなのかな
と思ってみたり、そんなこともないよな、とも思い直したり。
タブコントロール自体は自分でも今まで何度か作っていたのですが
実際にこのようなケースに出会うことは無かったのでどうにも
まごついてしまいました。
おっしゃられる様にモードレスでの対処というのも
現実的な解決かもしれませんね。

 久し振りにたかせさんのレクを嬉しく読ませてもらい
ました。また何かお分かりのことがありましたら
教えて下さい。ありがとうございました。
redfox

Re: タブページから開くモーダルウインドウ

#4 投稿記事 by redfox »

モーダルダイアログの生成時に与えるhOwnerWnd引数に hTabPage1を与えているのが原因のようです

DailogBoxの実行コードの中で GetWindowでオーナを探すのですが これに失敗しているため
ダイアログを閉じた段階でTabPage1がDisable状態になるためです

対処としては DialogBoxへは hMainWndを与えてやるか EnableWindow(hTabPage1,1)を実行してやるかになるでしょう
たかせ
記事: 217
登録日時: 2006年2月05日(日) 17:10
お住まい: 東京都

Re: タブページから開くモーダルウインドウ

#5 投稿記事 by たかせ »

redfox様
DialogBox(hTab_Page1, "ModalDlg")をDialogBox(hMainWnd, "ModalDlg")に変更することにより、
期待どおりに動作しました。
ありがとうございました。
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

Re: タブページから開くモーダルウインドウ

#6 投稿記事 by jacoby »

redfoxさん、ご回答ありがとうございます。
返信が遅くなりすみません。
教えていただいた方法でこちらでもきちんと動作しました。
DailogBoxの実行コードの中で GetWindowでオーナを探すのですが これに失敗しているため
ダイアログを閉じた段階でTabPage1がDisable状態になるためです
自分はTabPage1が直近のオーナーだろうと単純に考えていたんですがこれでは
ダメなんですね。
これはモーダルダイアログとモードレスダイアログのメッセージループにおける
動作の違いということなんでしょうか。あわてて調べているのでちょっとまだ理解が怪しいのですが。
いずれにしてもこの方法で綺麗に動作してくれます。助かりました。

それからたかせさん、自分が立てたスレなのにご返信頂いて申し訳ありません。
お二方ともどうもありがとうございました。