Bạn không thể thay đổi trạng thái của một mục từ lệnh giao diện người dùng xử lý nếu menu được gắn vào một hộp thoại Visual C++


Lưu ý Microsoft Visual C++ .NET 2002 và Microsoft Visual C++ .NET 2003 hỗ trợ cả quản lý mã mẫu được cung cấp bởi Microsoft .NET Framework và Microsoft Windows không được quản lý riêng mã mẫu. Thông tin trong bài viết này chỉ áp dụng cho không được quản lý mã Visual C++. Microsoft Visual C++ 2005 hỗ trợ mô hình quản lý mã được cung cấp bởi Microsoft .NET Framework và mô hình mã Microsoft Windows không được quản lý riêng.

Triệu chứng


Thay đổi trạng thái (bật/tắt đánh, thay đổi văn bản) của một mục từ lệnh giao diện người dùng (UI) xử lý không hoạt động đúng nếu menu được gắn vào một hộp thoại:
void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI) {    pCmdUI->Enable(FALSE); //Not calling the command handler, but does not show as disabled.    pCmdUI->SetCheck(TRUE); // Does not show check mark before the text.    pCmdUI->SetRadio(TRUE); // Does not show dot before the text.    pCmdUI->SetText("Close"); //Does not change the text.}

Nguyên nhân


Trình đơn thả xuống được hiển thị, WM_INITMENUPOPUP thư được gửi trước khi hiển thị các mục menu. Chức năng MFC CFrameWnd::OnInitMenuPopup lặp lại thông qua các menu và gọi Cập Nhật lệnh giao diện xử lý mục, nếu có. Giao diện của mỗi mục được Cập Nhật để phản ánh trạng thái (bật/tắt, kiểm tra/đánh dấu). Bản Cập Nhật giao diện người dùng cơ chế không hoạt động cho các ứng dụng dựa trên hộp thoại vì CDialog đã không xử lý OnInitMenuPopup sử dụng CWndxử lý mặc định, gọi Cập Nhật lệnh xử lý giao diện người dùng cho các mục menu.

Giải pháp


Sử dụng các bước sau để khắc phục sự cố này:
  1. Thêm mục ON_WM_INITMENUPOPUP đồ thư:
    BEGIN_MESSAGE_MAP(CTestDlg, CDialog)//}}AFX_MSG_MAPON_WM_INITMENUPOPUP()END_MESSAGE_MAP()
  2. Thêm một chức năng thành viên OnInitMenuPopup lớp hộp thoại và sao chép mã sau (lưu ý rằng mã này được lấy từ CFrameWnd::OnInitMenuPopup trong WinFrm.cpp):
    void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu){    ASSERT(pPopupMenu != NULL);    // Check the enabled state of various menu items.    CCmdUI state;    state.m_pMenu = pPopupMenu;    ASSERT(state.m_pOther == NULL);    ASSERT(state.m_pParentMenu == NULL);    // Determine if menu is popup in top-level menu and set m_pOther to    // it if so (m_pParentMenu == NULL indicates that it is secondary popup).    HMENU hParentMenu;    if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)        state.m_pParentMenu = pPopupMenu;    // Parent == child for tracking popup.    else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)    {        CWnd* pParent = this;           // Child windows don't have menus--need to go to the top!        if (pParent != NULL &&           (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)        {           int nIndexMax = ::GetMenuItemCount(hParentMenu);           for (int nIndex = 0; nIndex < nIndexMax; nIndex++)           {            if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)            {                // When popup is found, m_pParentMenu is containing menu.                state.m_pParentMenu = CMenu::FromHandle(hParentMenu);                break;            }           }        }    }    state.m_nIndexMax = pPopupMenu->GetMenuItemCount();    for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;      state.m_nIndex++)    {        state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);        if (state.m_nID == 0)           continue; // Menu separator or invalid cmd - ignore it.        ASSERT(state.m_pOther == NULL);        ASSERT(state.m_pMenu != NULL);        if (state.m_nID == (UINT)-1)        {           // Possibly a popup menu, route to first item of that popup.           state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);           if (state.m_pSubMenu == NULL ||            (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||            state.m_nID == (UINT)-1)           {            continue;       // First item of popup can't be routed to.           }           state.DoUpdate(this, TRUE);   // Popups are never auto disabled.        }        else        {           // Normal menu item.           // Auto enable/disable if frame window has m_bAutoMenuEnable           // set and command is _not_ a system command.           state.m_pSubMenu = NULL;           state.DoUpdate(this, FALSE);        }        // Adjust for menu deletions and additions.        UINT nCount = pPopupMenu->GetMenuItemCount();        if (nCount < state.m_nIndexMax)        {           state.m_nIndex -= (state.m_nIndexMax - nCount);           while (state.m_nIndex < nCount &&            pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)           {            state.m_nIndex++;           }        }        state.m_nIndexMax = nCount;    }}

Trạng thái


Hoạt động này là theo thiết kế.

Thông tin Bổ sung


Xử lý giao diện người dùng lệnh Cập Nhật được gọi là từ CWnd::OnCommand để đảm bảo rằng lệnh đã không bị vô hiệu hoá trước khi định tuyến. Đó là lý do tại sao xử lý lệnh không phải là cho mục bị vô hiệu hoá ngay cả khi nó không xa (không). Khoản mục menu không được vẽ để phản ánh trạng thái của họ trong trường hợp này. Đây là mã liên quan từ các tập tin Wincore.cpp:
   // Make sure command has not become disabled before routing.   CTestCmdUI state;   state.m_nID = nID;   OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);   if (!state.m_bEnabled)   {      TRACE1("Warning: not executing disabled command %d\n", nID);      return TRUE;   }

Các bước để tái tạo hiện tượng

Làm theo các bước để tái tạo hiện tượng này trong Visual C++ .NET:
  1. Tạo một ứng dụng MFC dựa trên hộp thoại bằng cách sử dụng AppWizard.
  2. Tạo tài nguyên đơn mới và thêm các mục menu tệpTệp/thoát .
  3. Thiết lập này đơn như menu cho hộp trong cửa sổ hộp thoại thuộc tính. Để thực hiện việc này, mở hộp thoại tài nguyên trong hộp thoại trình soạn thảo. Trong cửa sổ thuộc tính, bấm chọn Menu. ID tài nguyên đơn mới được hiển thị trong danh sách thả xuống Menu thuộc tính editor.
  4. Thêm một bộ xử lý UPDATE_COMMAND_UI cho Tệp/thoát mục. Để thực hiện việc này, bấm chuột phải vào Tệp/thoát trong trình chỉnh sửa trình đơn, sau đó bấm Thêm xử lý sự kiện. Hướng dẫn xử lý sự kiện, thêm xử lý UPDATE_COMMAND_UI dự án CDialog lấy lớp. Bấm Thêm và chỉnh sửa để tạo bộ xử lý và thêm một báo cáo các phương pháp xử lý được tạo ra:
    pCmdUI->Enable(FALSE); //Not calling the handler, but does not show as disabledpCmdUI->SetCheck(TRUE); // Does not show check mark before the text.pCmdUI->SetRadio(TRUE); // Does not show dot before the text.pCmdUI->SetText("Close"); //Does not change the text.
  5. Tạo và chạy ứng dụng.

Tham khảo


Để biết thêm thông tin, hãy bấm vào số bài viết sau đây để xem bài viết trong Cơ sở kiến thức Microsoft:
141751 thêm điều khiển thanh vào hộp trong MFC