64006
お世話になります。プログラミング初心者レベルの者です。  
MFCのダイアログベースプロジェクトで、ダイアログ上のタブコントロールに表示する、子ダイアログのボタンコントロールのイベントハンドラが呼ばれない問題で困っています。  
原因または解決策を教えて頂けるとありがたいです。

原因を確かめるために仮説を立ててネットでその解決策を探しましたが、見つかりませんでした。  
仮説①：子ダイアログへメッセージが何らかの理由で押されていない。  
仮説②：イベントハンドラに何か問題がある。  
→ クラスウィザードでは、イベントハンドラと紐づいているコマンドIDはボタンコントロールのID、メッセージはBN_CLICKEDであることを確認した。

作成したソースコードの一部：

```

    // LearningPokectDlg.h : ヘッダー ファイル
    //
    
    #pragma once
    
    
    // CLearningPokectDlg ダイアログ
    class CLearningPokectDlg : public CDialogEx
    {
    // コンストラクション
    public:
        CLearningPokectDlg(CWnd* pParent = nullptr);    // 標準コンストラクター
    
    // ダイアログ データ
    #ifdef AFX_DESIGN_TIME
        enum { IDD = IDD_LEARNINGPOKECT_DIALOG };
    #endif
    
        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート
    
    
    // 実装
    protected:
        HICON m_hIcon;
    
        // 生成された、メッセージ割り当て関数
        virtual BOOL OnInitDialog();
        afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
        afx_msg void OnPaint();
        afx_msg HCURSOR OnQueryDragIcon();
        DECLARE_MESSAGE_MAP()
    private:
        // タブを制御するコントロール変数
        CTabCtrl m_tab;
    
        // To Doタブ(m_child_dlg[0])と完了タブ(m_child_dlg[1])
        CDialog m_child_dlg[2];
    public:
        afx_msg void OnTcnSelchangeTab(NMHDR *pNMHDR, LRESULT *pResult);
    };
    
```

```

    // LearningPokectDlg.cpp : 実装ファイル
    //
    
    #include "stdafx.h"
    #include "LearningPokect.h"
    #include "LearningPokectDlg.h"
    #include "afxdialogex.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    // アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ
    
    class CAboutDlg : public CDialogEx
    {
    public:
        CAboutDlg();
    
    // ダイアログ データ
    #ifdef AFX_DESIGN_TIME
        enum { IDD = IDD_ABOUTBOX };
    #endif
    
        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート
    
    // 実装
    protected:
        DECLARE_MESSAGE_MAP()
    };
    
    CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
    {
    }
    
    void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
    }
    
    BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
    END_MESSAGE_MAP()
    
    
    // CLearningPokectDlg ダイアログ
    
    
    
    CLearningPokectDlg::CLearningPokectDlg(CWnd* pParent /*=nullptr*/)
        : CDialogEx(IDD_LEARNINGPOKECT_DIALOG, pParent)
    {
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    
    void CLearningPokectDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
        DDX_Control(pDX, IDC_TAB, m_tab);
    }
    
    BEGIN_MESSAGE_MAP(CLearningPokectDlg, CDialogEx)
        ON_WM_SYSCOMMAND()
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_NOTIFY(TCN_SELCHANGE, IDC_TAB, &CLearningPokectDlg::OnTcnSelchangeTab)
    END_MESSAGE_MAP()
    
    
    // CLearningPokectDlg メッセージ ハンドラー
    
    BOOL CLearningPokectDlg::OnInitDialog()
    {
        CDialogEx::OnInitDialog();
    
        // "バージョン情報..." メニューをシステム メニューに追加します。
    
        // IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);
    
        CMenu* pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != nullptr)
        {
            BOOL bNameValid;
            CString strAboutMenu;
            bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
            ASSERT(bNameValid);
            if (!strAboutMenu.IsEmpty())
            {
                pSysMenu->AppendMenu(MF_SEPARATOR);
                pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
            }
        }
    
        // このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
        //  Framework は、この設定を自動的に行います。
        SetIcon(m_hIcon, TRUE);         // 大きいアイコンの設定
        SetIcon(m_hIcon, FALSE);        // 小さいアイコンの設定
    
        // TODO: 初期化をここに追加します。
    
        // 各タブを作成する
        m_tab.InsertItem(0, _T("To Do"));
        m_tab.InsertItem(1, _T("完了"));
    
        // 各タブの中に表示する子ダイアログを作成する
        m_child_dlg[0].Create(IDD_TODO_DIALOG, &m_tab);
        m_child_dlg[1].Create(IDD_DONE_DIALOG, &m_tab);
    
        // To Doリストの子ダイアログの位置を調整する
        CRect m_tab_position;
        m_tab.GetWindowRect(m_tab_position);
        m_tab.AdjustRect(FALSE, m_tab_position);
        m_tab.ScreenToClient(m_tab_position);
        m_child_dlg[0].MoveWindow(m_tab_position);
        m_child_dlg[1].MoveWindow(m_tab_position);
    
        // To Doリストの子ダイアログを表示する
        m_child_dlg[0].ShowWindow(SW_SHOW);
    
        return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
    }
    
    void CLearningPokectDlg::OnSysCommand(UINT nID, LPARAM lParam)
    {
        if ((nID & 0xFFF0) == IDM_ABOUTBOX)
        {
            CAboutDlg dlgAbout;
            dlgAbout.DoModal();
        }
        else
        {
            CDialogEx::OnSysCommand(nID, lParam);
        }
    }
    
    // ダイアログに最小化ボタンを追加する場合、アイコンを描画するための
    //  下のコードが必要です。ドキュメント/ビュー モデルを使う MFC アプリケーションの場合、
    //  これは、Framework によって自動的に設定されます。
    
    void CLearningPokectDlg::OnPaint()
    {
        if (IsIconic())
        {
            CPaintDC dc(this); // 描画のデバイス コンテキスト
    
            SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    
            // クライアントの四角形領域内の中央
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon + 1) / 2;
            int y = (rect.Height() - cyIcon + 1) / 2;
    
            // アイコンの描画
            dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
            CDialogEx::OnPaint();
        }
    }
    
    // ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、
    //  システムがこの関数を呼び出します。
    HCURSOR CLearningPokectDlg::OnQueryDragIcon()
    {
        return static_cast<HCURSOR>(m_hIcon);
    }
    
    
    
    void CLearningPokectDlg::OnTcnSelchangeTab(NMHDR *pNMHDR, LRESULT *pResult)
    {
        // TODO: ここにコントロール通知ハンドラー コードを追加します。
        auto cur = m_tab.GetCurSel();
        for (int i = 0; i < 2; i++) {
            m_child_dlg[i].ShowWindow(i == cur ? SW_SHOW : SW_HIDE);
        }
    
        *pResult = 0;
    }
    
    
```

```

    #pragma once
    
    
    // ToDoDlg ダイアログ
    
    class ToDoDlg : public CDialogEx
    {
        DECLARE_DYNAMIC(ToDoDlg)
    
    public:
        ToDoDlg(CWnd* pParent = nullptr);   // 標準コンストラクター
        virtual ~ToDoDlg();
    
    // ダイアログ データ
    #ifdef AFX_DESIGN_TIME
        enum { IDD = IDD_TODO_DIALOG };
    #endif
    
    protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート
    
        DECLARE_MESSAGE_MAP()
    
    private:
        // To Do項目チェックボックスコントロール変数
        CButton m_check_item;
        // To Do項目表示文字列入力変数
        CString m_add_edit;
    public:
        afx_msg void OnBnClickedAddButton();
    };
    
```

```

    // ToDoDlg.cpp : 実装ファイル
    //
    
    #include "stdafx.h"
    #include "LearningPokect.h"
    #include "ToDoDlg.h"
    #include "afxdialogex.h"
    
    
    // ToDoDlg ダイアログ
    
    IMPLEMENT_DYNAMIC(ToDoDlg, CDialogEx)
    
    ToDoDlg::ToDoDlg(CWnd* pParent /*=nullptr*/)
        : CDialogEx(IDD_TODO_DIALOG, pParent)
        , m_add_edit(_T(""))
    {
    
    }
    
    ToDoDlg::~ToDoDlg()
    {
    }
    
    void ToDoDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
        DDX_Control(pDX, IDC_CHECK_ITEM, m_check_item);
        DDX_Text(pDX, IDC_CAPTION_EDIT, m_add_edit);
    }
    
    
    BEGIN_MESSAGE_MAP(ToDoDlg, CDialogEx)
        ON_BN_CLICKED(IDC_ADD_BUTTON, &ToDoDlg::OnBnClickedAddButton)
    END_MESSAGE_MAP()
    
    
    // ToDoDlg メッセージ ハンドラー
    
    
    void ToDoDlg::OnBnClickedAddButton()
    {
        // TODO: ここにコントロール通知ハンドラー コードを追加します。
    
        /*エディットコントロールから入力文字を取得し、 
        チェックボックスコントロールの表示文字に設定する*/
    
        // エディットコントロールの値変数にエディットコントロールで
        // 入力された文字列を代入する(UpdateData関数で)
        UpdateData(TRUE);
        CString edit_CString = m_add_edit;
    
        // 取得した文字列をチェックボックスコントロールに設定する
        // →　まずstaticコントロールで表示文字列を変更する基本を学ぶ
        m_check_item.SetWindowTextW(edit_CString);
    
        /// チェックボックスコントロールを表示して
        /// 追加ボタンがまた押されたら、複製できるようにする
        //CButton check_item1 = m_check_item;
        //check_item1.Create(IDC_CHECK_ITEM, this->classToDoDlg);
    }
    
```
CC BY-SA 4.0
2020-03-19T23:45:00.577
63982
2020-03-21T02:17:09.427
2020-03-20T21:58:41.000
21034
21034
question
mfc
子ダイアログ上のボタンコントロールのクリックイベントハンドラが呼ばれない
ソースを眺めただけで動作確認はしていませんが、

```

    // To Doタブ(m_child_dlg[0])と完了タブ(m_child_dlg[1])
    CDialog m_child_dlg[2];
    
```

これだと、

```

    // 各タブの中に表示する子ダイアログを作成する
    m_child_dlg[0].Create(IDD_TODO_DIALOG, &m_tab);
    m_child_dlg[1].Create(IDD_DONE_DIALOG, &m_tab);
    
```

してしまうと、外見はIDD_TODO_DIALOGのウィンドウになりますが、CDialogクラスのインスタンスにしかならないので、ToDoDlgクラス固有のイベントハンドラなどは使えないことになります。

管理がちょっと面倒になりますが、CDialogクラスのポインタ配列をメンバに持って、

```

    // To Doタブ(m_child_dlg[0])と完了タブ(m_child_dlg[1])
    CDialog* m_p_child_dlg[2];
    
```

として、

```

    // 各タブの中に表示する子ダイアログを作成する
    m_p_child_dlg[0] = new ToDodlg(&m_tab);
    
```

こんな感じでToDoDlgのポインタを使えば解決すると思います(もう一枚のタブのダイアログも同様に)。
CC BY-SA 4.0
2020-03-21T02:17:09.427
64006
2020-03-21T02:17:09.427
9515
63982
answer