Jenter Алекс
Шрифт:
A1 Контекстное (правокнопочное) меню создать довольно просто:
1. Добавляем в графическом редакторе новое пустое меню.
2. Для крайнего слева элемента верхнего уровня вводим какое-нить имя и в полученное раскрывающееся меню добавляем команды.
3. Вставляем обработчик сообщения WM_CONTEXTMENU в класс "вид" или в класс другого окна, получающего сообщения от кнопок мыши, ну, например, в CMyDialog. Обработчик этот программируем так:
Вот и все. TrackPopupMenu и занимается выводом контекстного меню на экран. Правда, объект класса CMenu лучше сделать мембером класса, тогда в любой функции можно будет удалять, добавлять, запрещать etc. элементы меню. Конечно, в этом случае m_Menu.LoadMenu(IDR_MYMENU); надо написать в OnInitDialog. Заметте, OnContextMenu получает координаты курсора, т.е. можем для разных областей окна элементарно выводить разные меню, просто проверяя координаты.
Sergey PochechuevA2 В Windows все файлы и папки входят в иерархию объектов оболочки. В неё также входят и объекты, не имеющие отношение к файловой системе: корзина, рабочий стол и т. п. Windows Explorer является по сути программой для просмотра этой иерархии объектов.
Каждый объект оболочки обязан реализовывать COM-интерфейс IShellFolder. Многие объекты реализуют и ряд других интерфейсов. Так, IExtractIcon отвечает за иконку объекта, а IContextMenu – за его контекстное меню. Эксплорер использует эти (и другие) интерфейсы, чтобы корректно отображать элементы иерархии объектов и позволять пользователю манипулировать ими. Мы также можем воспользоваться этими интерфейсами.
Итак, нам требуется функция, которая выводила бы контекстное меню для заданного файла (каталога), который передавался бы ей в качестве параметра. Эта функция должна выполнить следующие действия:
• Получить интерфейс IContextMenu для этого файла (каталога).
• Создать всплывающее меню (посредством CreatePopupMenu).
• Заполнить его элементами с помощью IContextMenu::QueryContextMenu.
• Показать меню пользователю (TrackPopupMenu).
• Выполнить выбранную команду посредством IContextMenu::InvokeCommand.
Основную сложность на самом деле представляет первый этап. Получить указатель на IContextMenu мы можем только, имея указатель на базовый интерфейс IShellFolder, но Windows не предоставляет простого способа получить этот указатель. Выполнение этой задачи в свою очередь распадается на несколько шагов:
– Получить интерфейс IShellFolder рабочего стола посредством SHGetDesktopFolder.
– Построить LPITEMIDLIST для заданного файла (каталога), используя IShellFolder::ParseDisplayName.
– Получить IShellFolder для этого файла вызовом IShellFolder::BindToObject.
Функция, которая отображает контекстное меню, может выглядеть примерно так (я снабдил её подробными комментариями).
Эту функцию можно вызывать, например, из обработчика OnContextMenu. Делается это так:
За дополнительной информацией следует обратиться к следующим статьям в MSDN:
– Periodicals 1997, Microsoft Systems Journal, April, Wicked Code
– Knowledge Base, статья ID: Q198288
– Описание IShellFolder и IContextMenu
Что касается второго вопроса (о создании собственных пунктов меню), мы имеем полный контроль над процессом создания меню, а значит можем делать с ним всё, что угодно. Нужно только иметь в виду 2 момента.
Во-первых, поскольку функция TrackPopupMenu вызывается с флагом TPM_RETURNCMD, она на будет отправлять окну сообщение WM_COMMAND. Поэтому нужно анализировать значение nCmd, возвращённое функцией TrackPopupMenu и вызывать нужный обработчик вручную. Например:
Во-вторых, функция IContextMenu::QueryContextMenu получает параметры idCmdFirst, idCmdLast (в примере выше они равны 1 и 0x7FFF соответственно). Идентификаторы для стандартных пунктов меню выбираются именно в диапазоне от idCmdFirst до idCmdLast. Поэтому нужно проследить, чтобы идентификаторы пользовательских пунктов меню в этот диапазон не попали.
Alexander SharginКогда начал читать вашу статью на тему мерцания, подумал было, что вы обязательно упомянете тот метод, который использовал я в своей программе. На мой взгляд, он достаточно известен, и, кажется, является самым самым лучшим.
Нужно просто создать обработчик события WM_ERASEBKGND с одной-единственной строчкой:
Т.е., по-русски говоря, программа фон не очистила, рисуйтесь полностью.
VladНа ответ A1 из прошлого выпуска:
Теперь практика. Пусть имеется готовое SDI приложение (с технологией Документ/Представление). Создаем дополнительное Представление. Это делается в функции CFrameWnd::OnCreateClient примерно так:
Этот код работает неправильно, причём это видно даже невооружённым взглядом. В последней строчке функции CMainFrame::OnCreateClient вызывается функция базового класса. Но ведь поле pContext->pNewViewClass уже изменилось! В результате вместо двух разных видов будет создано два одинаковых. Ошибка лечится переносом вызова функции из базового класса в начало переопределённой функции:
Кроме того, неясно, как использовать функцию SwitchView. Указатель на созданный нами вид хранится в m_pNewView, но для получения указателя на вид, созданный самой MFC, не видно удобного способа. Вероятно, лучший вариант – также сохранить его в члене класса CMainFrame.
Alexander SharginQ. У меня dialog-base приложение, живет в systray. Необходимо, чтобы приложение при повторном запуске находило уже запущеный экземпляр программы и активизировало его. Я пытался сделать это через FindWindow, в которую передается имя зарегистрированного класса окна, и заголовок окна, которое разыскивается. По заголовку я искать не могу, так как он все время у меня меняется. Следовательно, нужно искать по зарегистрированному имени класса окна. Вот тут то и начинается проблема. Я его не знаю. MFC сама их раздает dialog-based приложениям. А переопределить это имя можно было бы в PreCreateWindow, но этот метод CDialog не наследует из CWnd. Во всех остальных методах, имя класса уже зарегистрированно, т.е. менять его поздно. Как быть?
el-fЭто все на сегодня. До новых встреч!
Алекс Jenter jenter@mail.ru Красноярск, 2000.Программирование на Visual C++
Выпуск №26 от 3 декабря 2000 г.
Здравствуйте!
Итак, вот уж и зима на дворе… Время, когда отходить от компьютера не хочется даже ненадолго ;) Правда, это если у вас в комнате достаточно тепло. В другом случае не хочется вылезать из-под трех одеял;)
В прошлом выпуске мы с вами говорили о профилировании программ. Некоторые читатели просили также рассказать обо всех богатых возможностях отладки, которые предлагает Visual C++. Александр Шаргин, наш постоянный автор, любезно предложил написать в рассылку статью на эту тему. Думаю, что даже умеющие пользоваться отладчиком программисты найдут в ней для себя много интересного.
В этой статье я очень кратко расскажу о возможностях встроенного отладчика Visual C++.
Запуск отладки
Чтобы запустить программу на отладку, нужно выбрать одну из команд меню Build->Start Debug. Обратите внимание, что команда Attach to Process позволяет подключиться к уже запущенному процессу.
Точки останова (Breakpoints)
Точки останова служат для прерывания программы, выполняемой в отладчике. Их можно привязывать к конкретной строке в коде программы, к переменной или к сообщению Windows. После того как программа прервана, её можно выполнять в пошаговом режиме или просто проанализировать значения переменных, после чего продолжить выполнение.