Украинския баннерная

Контекстное меню в приложениях MFC

ПРО ЭТО от Хитрого Фоголя

На центральную Что нового Что интересного Структура сайта...
ПРО ЭТО... Что здесь нового Что здесь интересного
Українська :: Русская :: English :: Deutsch

Тут сначала при создании мной одной утилиты у меня возник один вопрос, который затем несколько раз ставился на страницах форума RSDN. Ответ на вопрос я нашел и теперь хочу поделиться им с другими, дабы "хитрости стало больше"...

Вопрос такой: как заставить обновляться команды контекстного меню аналогично командам главного меню? Для демонстрации решения я опишу создание тестового проекта и внедрение механизма в него, а затем перенос отработанного механизма в реальный проект. Кстати, таким образом я обычно и реализую различные новые "примочки" для рабочих проектов.

Итак, создаем с помощью Колдуна простое MFC приложение, основанное на технологии "документ-вид", многодокументное, без поддержки баз данных и ActiveX, со строкой состояния и инструментальной панелью, без справочной системы и вообще каких бы то ни было дополнительных наворотов, которые только замедлят компиляцию и испортят нам жизнь. Я назвал проект просто UpdateUI. Создали! Компилируем, запускаем, работает: странно, да? Дальше что?

Дальше в целях все того же эксперимента создадим парочку собственных команд меню, которые только то и делают, что ничего не делают. Как это делается я, так уж и быть, описывать не буду. Я вставлю свои команды в меню Edit и назову их... Опять думать начал! "Command 1" и "Command 2"!

Вы не забываете вставлять описания команд в поле Prompt? Это хорошо, а я иногда забываю...

Ну и добавим их обработчики: один, скажем, в документ, второй - в вид. Туда же добавим и обработчики UPDATE_COMMAND_UI. Все, кажется? Ну ладно, пропишем еще и немного кода, чтобы видеть "результат" выполнения наших команд:

void CUpdateUIDoc::OnEditCommand1() 
{
	// TODO: Add your command handler code here
	AfxMessageBox("CUpdateUIDoc::OnEditCommand1()", MB_ICONINFORMATION);
}

void CUpdateUIView::OnEditCommand2() 
{
	// TODO: Add your command handler code here
	AfxMessageBox("CUpdateUIView::OnEditCommand2()", MB_ICONINFORMATION);
}

Что там еще? А ради чего мы все это затеваем? Ах да! Контекстное меню! Тогда добавляем в вид контекстное меню. Как делается? До просто добавляется классу нашего вида CUpdateUIView обработчик сообщения WM_CONTEXTMENU Windows. Как это делается? Хм... Мышкой... У нас тут Visual C++, а не кабы что... :)

Ха! А это еще не все, оказывается. Хорошо: создаем новый ресурс меню, который, не мудрствуя лукаво, обзываем IDR_CONTEXTMENU, создаем в этом меню одно подменю, обозванное мной "1" - это никакого значения не имеет - и добавляем в это подменю команды "Command 1" и "Command 2" !ВНИМАНИЕ! с теми же идентификаторами, что и в главном меню, а именно ID_EDIT_COMMAND1 и ID_EDIT_COMMAND2 соответственно. Затем создаем собственно код, который будет отвечать за показ этого контекстного меню. У меня готовый пример на страничке лежит, поэтому я его просто оттудова скопирую и изменю соответствующим образом. Хм... Надо же, и менять ничего не надо. Ну разве я не умница? :)

Надо бы, конечно, эти две статьи немного объединить, но это как-нибудь потом...

Компилируем и запускаем. Работает. Контекстное меню есть, однако. У вас не работает? Контекстного меню нет, однако? Хм... Тогда напишите мне и вышлите целиком ваш проект, заархивированный желательно ZIP, а я посмотрю и скажу, что же не так. Адрес? Ну да, конечно же: alex_fogol@ukr.net.

Что теперь делаем? Теперь нам бы надо создать некоторые условия, когда некоторые команды, а именно наши, должны были бы быть недоступными. Как бы это сделать? Ага! Добавим еще две команды меню: "Enable Command 1" и "Enable Command 2". Добавим их как в главное меню - подменю "View" как раз подходит, так и в контекстное - чего уж расслабляться? Обработчики COMMAND и UPDATE_COMMAND_UI добавляем в классы документа для "Enable Command 1" и вида для "Enable Command 2".

Фу-у... Я уже даже устал немного. В класс документа добавим переменную BOOL m_bEnableCommand_1, а в класс вида - BOOL m_bEnableCommand_2. Это чтоб никто не догадался, куда они относятся и для чего предназначены. Прописываем в конструкторах их инициализацию в истину (TRUE). В деструкторах писать ничего не надо. В обработчиках наших "Enable Command" пишем так:

void CUpdateUIDoc::OnViewEnablecommand1() 
{
	// TODO: Add your command handler code here
	m_bEnableCommand1 = !m_bEnableCommand1;
}

void CUpdateUIView::OnViewEnablecommand2() 
{
	// TODO: Add your command handler code here
	m_bEnableCommand2 = !m_bEnableCommand2;
}

А в обработчиках их UPDATE_COMMAND_UI так:

void CUpdateUIDoc::OnUpdateViewEnablecommand1(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	pCmdUI->SetCheck(m_bEnableCommand1 ? 1 : 0);
}

void CUpdateUIView::OnUpdateViewEnablecommand2(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	pCmdUI->SetCheck(m_bEnableCommand2 ? 1 : 0);
}

Сразу же, не расслабляясь, пишем обработчики UPDATE_COMMAND_UI для команд "Command 1/2":

void CUpdateUIDoc::OnUpdateEditCommand1(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	pCmdUI->Enable(m_bEnableCommand1);
}

void CUpdateUIView::OnUpdateEditCommand2(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	pCmdUI->Enable(m_bEnableCommand2);
}

Теперь запускаем и тестируем. Что мы видим? В главном меню все работает так, как и замышлялось. А вот в контекстном меню ни тебе галочек, ни тебе сереньких пунктиков. "Нехорошо", подумал я и решил все это исправить. Посмотрел, почитал, попробовал, нашел. Теперь и вам расскажу.

Очевидно, что почему-то наше контекстное меню выпадает из того механизма, который и предназначен для вызовов соответствующих обработчиков UPDATE_COMMAND_UI. Как это поправить? Оказывается, можно откликнуться на сообщение WM_INITMENUPOPUP и собственноручно наставить свое меню на путь истинный:

void CUpdateUIView::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) 
{
	CView::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
	
	// TODO: Add your message handler code here
	CWnd* pWnd = AfxGetMainWnd();
	ASSERT(pWnd);
	LPARAM lParam;
	lParam = nIndex;
	pWnd->SendMessage(WM_INITMENUPOPUP, (WPARAM) pPopupMenu->m_hMenu, lParam);
}

Компилируем, запускаем, смотрим... Уря! Заряботало!

Что? Не заработало? Хм... Ну, тогда все же пришлите мне архив своего проекта на alex_fogol@ukr.net, а я уж постараюсь посмотреть, чего же там не так... И не все сразу, пожалуйста, а в порядке очереди.

Ну, а как перенести этот код в свой проект - об этом мне как-то даже и писать неудобно. Честно говоря, я даже не представляю, чтобы можно было об этом написать. Ведь вы, господа программисты, и сами с усами. :)


P.S. Это такая себе, навскидку набранная версия статьи, предназначенная для того, чтобы постараться ответить конкретному человеку на конкретный вопрос. В дальнейшем надеюсь ее причесать, может дополнить. Если у Вас есть какие-то предложения или замечания - милости прошу писать об этом все туда же, на alex_fogol@ukr.net. Заранее благодарен...


Почтовый ящичек : Гостевая книга   Alpha-Counter
Страничка создана Хитрым Фоголем. Copyright (c) by Alex Fogol, AFD Software 2001-2010.