閉じるボタンでアイコン表示

[戻る]
あき 2003/09/04(木) 14:46:22
こんにちは。VC初心者のゴリといいます。
環境はWin2000,VC++6.0,MFC です。

MDIで子ウィンドウに複数のビューを作成しています。
これらのビューをツールバーのアイコンから表示し、
ビューが表示されたら、そのアイコンを非表示。

また、閉じるボタンでビューを閉じたら、
それぞれに対応しているアイコンを表示する。

というようなものを作りたいのですが・・・

初心者なもので、出来るだけ詳しく教えて頂けるとありがたいです。
よろしくお願いします。

あき 2003/09/04(木) 14:50:37
あきです。
すいません。

名前を間違えました・・・

dairygoods 2003/09/04(木) 15:38:11
CChildFrameにWM_CREATEおよびWM_DESTROYのハンドラを追加することにより、
MDI子ウィンドウを開いた時と閉じた時の処理を記述することができます。

あき 2003/09/04(木) 16:30:54
dairygoodsさん、レスありがとうございました。

WM_DESTROYのハンドラ内に

m_wndToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON1, TRUE);

というのを入れてみたのですが、これではエラーが出てしまいました。
申し訳ございませんがアドバイスをお願いします。

ca18 2003/09/04(木) 17:42:09
なんで肝心のエラー内容を書かないんですか?

あき 2003/09/04(木) 18:07:05
確かに、ca18さんのおっしゃる通りですね。

以下にエラー内容を書きます。

子ウィンドウの閉じるボタンを押下したときに
以下のような実行時エラーとなってしまいました。

For information on how your program cause an assertion failure,
see the Visual C++ documentation on asserts.

(Press Retry to debug the application)


閉じるボタンが押されたことは認識してるみたいなのですが、
なぜこのようなエラーが出てしまったのかわかりません。

すいませんが、よろしくお願いします。

dairygoods 2003/09/05(金) 08:27:42
同時にエラーとなったソースファイル名と行番号が出ているはずですが?

> (Press Retry to debug the application)

再試行キーを押してデバッグしてみましたか?

あき 2003/09/05(金) 09:13:22
おはようございます。
dairygoodsさんレスありがとうございました

デバックしてみたところ、以下の部分でのエラー発生でした。

_AFXCMN_INLINE CToolBarCtrl::CToolBarCtrl()
    { }
_AFXCMN_INLINE BOOL CToolBarCtrl::EnableButton(int nID, BOOL bEnable)
    { ASSERT(::IsWindow(m_hWnd)); return (BOOL) ::SendMessage(m_hWnd, 
TB_ENABLEBUTTON, nID, MAKELPARAM(bEnable, 0)); }

あき 2003/09/05(金) 09:16:07
すいません。
途中で送ってしまいました・・

_AFXCMN_INLINE CToolBarCtrl::CToolBarCtrl()
    { }
_AFXCMN_INLINE BOOL CToolBarCtrl::EnableButton(int nID, BOOL bEnable)
    { ASSERT(::IsWindow(m_hWnd)); return (BOOL) ::SendMessage(m_hWnd, 
TB_ENABLEBUTTON, nID, MAKELPARAM(bEnable, 0)); }

最後の2行でのエラー発生ということでした。

デバックしてもあまり意味がわかりませんでした。
ヒントとなるようなことを教えて頂くとありがたいです。
よろしくお願いします。

n [E-Mail] 2003/09/05(金) 10:23:18
CToolBarCtrl変数のm_hWndがもう存在していないということでは。

ということはCChildFrameでなくCMainFrameのWM_DESTROYハンドラに入れたのでは?

あき 2003/09/05(金) 10:50:47
nさん お返事ありがとうございます。

CChildFrameのWM_DESTROYハンドラに入っていますが・・・

n [E-Mail] 2003/09/05(金) 11:16:15
もしかしてCChildFrameにCToolBarCtrl変数を追加したのですか?
それはCMainFrameのm_wndToolBarと無関係なやつなのでは?

PATIO 2003/09/05(金) 11:19:02
もしかして、CChildFrameからCMainFrameのツールバーを制御しようとしてませんか?
インスタンスはCMainFrameにしかないはずなので
CChildFrameにインスタンスだけを宣言してもだめだと思うのですけれど。

外していたらごめんなさい。

PATIO 2003/09/05(金) 11:20:09
ああっ、ニアミス。

あき 2003/09/05(金) 12:02:12
nさん、PATIOさん お返事ありがとうございます。

指摘された通りのことをしようとしていました。
では、この方法では出来ないということですか?

なにか良い方法がありましたら、教えて下さい。
よろしくお願いします。

n [E-Mail] 2003/09/05(金) 15:24:38
とりあえず、
CMainFrameとCChildFrameに、
特定のボタンIDに対応するON_UPDATE_COMMAND_UIハンドラをClassWizardで
それぞれ追加し、

CMainFrame側に
 pCmdUI->Enable(TRUE);
CChildFrame側に
 pCmdUI->Enable(FALSE);
と書くと、

CMainFrameの中に一つもCChildFrameが無い場合はボタンが有効になり、
一つ以上CChildFrameがあるとボタンが無効になります。

n [E-Mail] 2003/09/05(金) 16:53:06
あっ、有効/無効じゃなくて表示/非表示ですね;;

ユーザー定義メッセージハンドラを使ってこのようにしては:

//MainFrame.h
class CMainFrame {
 afx_msg LRESULT OnApp(WPARAM,LPARAM);
};

//MainFrame.cpp
BEGIN_MESSAGE_MAP(CMainFrame,CMDIFrameWnd)
 ON_MESSAGE(WM_APP,OnApp)
END_MESSAGE_MAP()

LRESULT CMainFrame::OnApp(WPARAM wParam,LPARAM lParam)
{
 m_wndToolBar.GetToolBarCtrl().HideButton((int)wParam,(BOOL)lParam);
 return TRUE;
}

//CChildFrame::OnCreate()で:
 AfxGetMainWnd->SendMessage(WM_APP,(WPARAM)ID_BUTTON1,(LPARAM)TRUE);
//CChildFrame::OnDestroy()で:
 AfxGetMainWnd->SendMessage(WM_APP,(WPARAM)ID_BUTTON1,(LPARAM)FALSE);

あき 2003/09/08(月) 10:25:59
nさんお返事ありがとうございます。

nさんの方法で試したのですが、上手くいきませんでした。
デバックしてみたところ以下の部分が通っていませんでした。

LRESULT CMainFrame::OnApp(WPARAM wParam,LPARAM lParam)
{
 m_wndToolBar.GetToolBarCtrl().HideButton((int)wParam,(BOOL)lParam);
 return TRUE;
}

追加の仕方が間違っているのかな〜・・・くらいしか分かりません。
どのようにしたらよろしいのでしょうか?
よろしくお願いします。

PATIO 2003/09/08(月) 10:31:06
この部分の実装は出来ていますか?
ここがないとメッセージの振り分けがうまく行かないはずですが。

//MainFrame.cpp
BEGIN_MESSAGE_MAP(CMainFrame,CMDIFrameWnd)
 ON_MESSAGE(WM_APP,OnApp)
END_MESSAGE_MAP()

あき 2003/09/08(月) 10:49:35
PATIOさんお返事ありがとうございます。
実装は出来ているはずですが、
確認方法というのはあるのですか?

PATIO 2003/09/08(月) 11:26:59
はて?
手元にある簡単なテストプログラムではうまく行っていますけれど。
入れている場所に間違いはありませんか?

ON_MESSAGE(WM_APP,OnApp)という行を

MainFrame.cpp内に既に存在するはずの
BEGIN_MESSAGE_MAP(CMainFrame,CMDIFrameWnd)
        ・
        ・
        ・
END_MESSAGE_MAP()

という部分の間に入れればいいはずなんですが。

あき 2003/09/08(月) 11:40:17
PATIOさんお返事ありがとうございます。
基本的なことを聞いてすいませんでした。

BEGIN_MESSAGE_MAP(CMainFrame,CMDIFrameWnd)
        ・
        ・
END_MESSAGE_MAP()

にはしかっりと入っています。

初心者だからって失礼かもしれませんが、
もう少しアイコンを有効にする部分を
詳しく教えていただきますでしょうか?

PATIO 2003/09/08(月) 11:52:21
お話では、CMainFrame::OnApp自体に入ってきていないように
書いておられたのでここに入ってこないと根本的な問題の解決に
なら内容に感じたのでそこを突っ込んだんですが、
そこは問題ではないのですか?

nさんのコード見てもらうとわかると思いますが、
ツールバーをコントロールするにはCToolBarCtrlクラスが必要です。
ですから、m_wndToolBar.GetToolBarCtrl()で取り出しています。
CToolBarCtrlクラスにはツールバー上のコントロールを制御する
メソッドが用意されています。
これらは、HELPを参照すれば説明が載っているはずです。
ご自分で確認してみてください。

nさんが示してくれている方法は、MainFrameで行う事はMainFrameで行い、
ChildFrameはMainFrameに実行をお願いすると言うパターンの実装例です。

nさんが最初に示していたのが有効/無効の切り替え方法で、
次に示してくれているのが表示/非表示の切り替え方法です。

ON_UPDATE_COMMAND_UIハンドラについても
一度、HELPを参照されることをお勧めします。

あき 2003/09/08(月) 12:06:12
PATIOさんお返事ありがとうございます。
そうですね。
もう少し自分で調べてみたいと思います。

行き詰まったときには、またご教授ください。

あき 2003/09/08(月) 18:29:30
ツールバーのアイコンに対応するビューを表示するのに
以下のような処理を行っています。

void CAdminApp::OnButton3() 
{
    // TODO: この位置にコマンド ハンドラ用のコードを追加してください
    // 最初のドキュメントトンプレートの位置を取得
    POSITION posTemplate = GetFirstDocTemplatePosition();
    if (posTemplate != NULL)    // 成功?
    {
        // ドキュメントテンプレートへのポインタを取得
        CDocTemplate* pTemplate = GetNextDocTemplate(posTemplate);
        if (pTemplate != NULL)      // 成功?
        {
            // ドキュメントの作成
            pTemplate = GetNextDocTemplate(posTemplate);
            if (pTemplate != NULL) {
                pTemplate = GetNextDocTemplate
(posTemplate);
                if (pTemplate != NULL) {
                pTemplate->OpenDocumentFile(NULL);
                m_bView = false;
                }
            }
        }
    }
    else
    {
        m_bView = true;
    }
}

このようにビューを表示するときはテンプレートを順々に見ていきます。
このときMainFrameでN_UPDATE_COMMAND_UIハンドラを使い、
アイコンの有効/無効の切り替えをしようとしたのですが、
最初は上手くいくのですが、子ウィンドウに1つのビューを表示すると
全てのアイコンが無効となってしまいます。
上にあるソースが原因だと考えたのですがどのようにしたら
1つ1つが対応してくれるのでしょうか?
どなたかアドバイスをよろしくお願いします。

PATIO 2003/09/09(火) 10:19:49
ON_UPDATE_COMMAND_UIでどういう判断をしているかを見ないとなんとも言えないです。
画面とボタンが一対一で対応しているのであれば、今どの画面が開いているのかを
管理しないとボタンの有効/無効制御が出来ないと思うのですが、
どのようにしているのでしょう?

単純に考えるならば、BOOLの配列を用意しておいて、それぞれに対応する画面を決めてお
き、
画面が表示されたらFALSE、閉じられたらTRUEという風に設定するようにして
ON_UPDATE_COMMAND_UI側で対応するBOOL値を参照して制御と言うことぐらいです。
m_bViewという変数一つで数種類の画面を処理しようとしているのであれば、
それはもともと無理な話ではと思います。

あき 2003/09/09(火) 10:43:24
ON_UPDATE_COMMAND_UIでは、

if (m_bView == FALSE)
    {
        pCmdUI->Enable(false);
    }
    else
    {
        pCmdUI->Enable(true);
    }
とし、今アイコンが3つあるのでそれぞれm_aView、m_bView、m_cView
という変数で処理しようとしています。

PATIOさんがおっしゃられている
ON_UPDATE_COMMAND_UI側で対応するBOOL値を参照して制御
というのはどのようにしたらよろしいのでしょうか?

PATIO 2003/09/10(水) 09:28:42
あらら、m_bViewってboolの意味じゃなかったのか。
私が変数名をつけるときにそうするので勘違いしてました。
出来れば、変数名の頭に型を連想させるプリフィックスをつけた方が
コードの可読性があがると思いますよ。

あと、TRUE、FALSEとtrue、falseは意味合いが違うので
混在させない方が良いです。
変数の型がBOOLならTRUE、FALSEを使い、
boolならtrue、falseを使います。
私的には、直接

pCmdUI->Enable(m_bView);

とした方が簡単なような。(m_bViewがBOOLだとして)
m_bViewを他の用途にも使うのであれば、話は別ですけれど。
pCmdUI->Enable(BOOL bOn = TRUE);ですから、TRUE、FALSEを使いましょう。

三つの変数を使って制御しようとしていれば、
うまく行きそうな気がしますけれど、
デバッガで追ってみましたか?

全て無効になると言う事は、
pCmdUI->Enable(false);
の方に入ってきていると言うことなので
判定に使っている変数が間違っているか、
変数が正しくて中身がFALSEになっているかだと思います。
その場合は、どこかでFALSEにしているはずなのでそれを追いかけましょう。
そのためのデバッガです。

古いスレッドには追加発言できません。