Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Создание меню, динамически 
:(
    Опции темы
decembrist
  Дата 15.12.2005, 14:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 238
Регистрация: 1.4.2004

Репутация: нет
Всего: нет



Как динамически создавать пункты главного меню?
Кто знает, поделитесь опытом smile
PM MAIL   Вверх
Любитель
Дата 16.12.2005, 10:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 7
Всего: 92



1. Создаём объект класса CMenu (в контексте класса окна, куда надо приаттачить меню).
2. Пользуемся методом ApendMenu.
3. Для добавления подменю, создаём для него объект (локально) и затем ставим в AppendMenu флаг MF_POPUP, а в качестве ID задаём хендл нашей менюхи.
4. Вызываем SetMenu для окна, к которому приделываем меню.
5 .Всё. smile

ЗЫ. Могу прислать пример динамической загрузки меню из реестра (просто относительно недавно сам такое делал).


--------------------
PM MAIL ICQ Skype   Вверх
decembrist
Дата 16.12.2005, 19:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 238
Регистрация: 1.4.2004

Репутация: нет
Всего: нет



да примерчик нужен очень smile
пожалуйста.
Добавлено @ 20:04
у меня диалог(Dialog Application), к которому я прицепил меню и мне нужно к определенным пунктам цеплять динамически созданные подпункты smile
PM MAIL   Вверх
tigger
Дата 17.12.2005, 17:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Awaiting Authorisation
Сообщений: 456
Регистрация: 30.4.2005

Репутация: 3
Всего: 4



Лови пример по динамическому созданию меню и добавлению в пункт "импортировать" подменю.

Надеюсь это то что нужно!!!

smile

Присоединённый файл ( Кол-во скачиваний: 25 )
Присоединённый файл  0101.zip 31,88 Kb


--------------------
user posted image
PM MAIL   Вверх
decembrist
Дата 19.12.2005, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 238
Регистрация: 1.4.2004

Репутация: нет
Всего: нет



Спасибочки большое smile
Щас посмотрю
PM MAIL   Вверх
decembrist
Дата 19.12.2005, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 238
Регистрация: 1.4.2004

Репутация: нет
Всего: нет



Вопрос: по какому принципу описываются пункты меню в файле ресурсов?
Код

#define ID_1                            800
#define ID_2                            801
#define ID_3                            802
#define ID_4                            803
#define ID_5                            804
#define ID_6                            805
#define ID_7                            806
#define ID_8                            807

Это добавляется вручную?
А если я заранее не знаю количество айтемов в сабменю?
PM MAIL   Вверх
Denn
Дата 19.12.2005, 16:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 143
Регистрация: 6.8.2005

Репутация: 1
Всего: 2



Цитата(decembrist @ 19.12.2005, 15:53)
Вопрос: по какому принципу описываются пункты меню в файле ресурсов?

просто числовые идентификаторы. генерятся визардом если делать в нем.
Цитата(decembrist @ 19.12.2005, 15:53)
Это добавляется вручную?

да, если делаешь меню руками.
PM MAIL ICQ   Вверх
tigger
Дата 19.12.2005, 16:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Awaiting Authorisation
Сообщений: 456
Регистрация: 30.4.2005

Репутация: 3
Всего: 4



Да это добавляется вручную.

Если заранее не известно количество пунктов в подменю то вместо:

Код

m_NewSubMenu.AppendMenu(MF_STRING, ID_5, (LPCTSTR)"bmp");    
m_NewSubMenu.AppendMenu(MF_STRING, ID_6, (LPCTSTR)"jpg");
m_NewSubMenu.AppendMenu(MF_STRING, ID_7, (LPCTSTR)"png");


используй:

Код

m_NewSubMenu.AppendMenu(MF_STRING, 5, (LPCTSTR)"bmp");    
m_NewSubMenu.AppendMenu(MF_STRING, 6, (LPCTSTR)"jpg");
m_NewSubMenu.AppendMenu(MF_STRING, 7, (LPCTSTR)"png");


smile


--------------------
user posted image
PM MAIL   Вверх
Coocky
Дата 19.12.2005, 16:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


GUI гуру
****


Профиль
Группа: Участник Клуба
Сообщений: 2879
Регистрация: 16.2.2004
Где: Украина. Запорожь е

Репутация: 25
Всего: 62



decembrist
Блин, ну есть же целая тема!!!!!!!!
Когда поиском начнем пользоваться!!!????????????
Я кому выкладываю?????
Цитата

В библиотеке MFC реализован класс CMenu, который инкапсулирует в себе объект Windows, определяемый дескриптором HMENU. Другими словами, если Вы хотите работать с меню, то у Вас есть как минимум два варианта: реализовать всю работу с меню через API используя непосредственно HMENU или же работать с меню через класс CMenu и не знать о многих неприятностях которые могли бы Вас ожидаь при программирование на "чистом" WinAPI. Учитывая тот факт, что одной из наших с Вами главных задач является изучение MFC, то мы выбираем второй вариант. Кто же является родителем CMenu? Непосредственно сам CObject - базовый класс в иерархии MFC.


Естественно, понимание того, что для работы с меню нам необходим класс CMenu - это уже серьезный шаг, однако необходио разобраться и с методами которые предоставляет нам данный класс. Чем мы сейчас и займемся...

Я уже упоминал об описателе меню. В классе CMenu определено поле CMenu::m_hMenu, которое определяет дескриптор меню, присоединенного к экземпляру класса CMenu. Как всегда, пару слов о конструкторе. Конструктор CMenu( ); позволяет создать экземпляр класса CMenu, но не само меню! Когда будет создан объект CMenu, Вы можете воспользоваться либо функциями которые создают непосредственно само меню, либо же функциями которые загружают меню в созданный объект.

Следующий ряд функций-членов класса CMenu посвящен инициализации объкта меню.

BOOL Attach( HMENU hMenu ); - присоединяет уже существующее меню hMenu к объекту класса CMenu. Причем, не рекомендуется присоединять одно и тоже меню к нескольким объектам класса CMenu. Функция возвращает не ноль, если отработала успешно.

HMENU Detach( ); - отсоединяет меню Windows от объекта и устанавливает значение поля m_hMenu в NULL. Возвращаемое значение - описатель отсоединенного меню.

static CMenu* PASCAL FromHandle( HMENU hMenu ); - статическая функция, возвращает указатель на объект CMenu сопоставленный описателю меню hMenu. Если hMenu не был присоединен к объекту CMenu, то создастся временный объект CMenu. Так как указатель временный, то не нужно запоминать его значение для дальнейшего успользования.

static void PASCAL DeleteTempMap( ); - статическая функция удаляет все временные объекты класса CMenu созданные методом FromHandle. Функция вызывается автоматически объектом CWinApp, при "простоях" программы, тоесть когда освобождается очередь сообщений.

HMENU GetSafeHmenu( ) const; - возвращает описатель меню Windows, всязанный с объектом класса CMenu.

BOOL CreateMenu( ); - создает пустое меню и присоединяет его к объекту CMenu. Функция возвращает не ноль, если меню было создано успешно.

BOOL CreatePopupMenu( ); - создает пустое выпадающее меню и присоединяет его к объекту CMenu. Это может быть как "самостоятельное" меню, так и меню присоединяемое к уже существующему меню. Меню уничтожается автоматически, если уничтожается окно (или меню) к которому оно присоединено.

BOOL LoadMenu( LPCTSTR lpszResourceName ); и BOOL LoadMenu( UINT nIDResource ); - два метода, которые позволяют загрузить меню из ресурсов выполняемого файла и присоединить его к объекту CMenu. Первый метод позволяет загрузить ресурс по строковому имени, а второй - по числовому идентификатору. И первый, и второй методы возвращают не ноль, если отработали успешно.

BOOL DestroyMenu( ); - уничтожает меню и все, связанные с ним, ресурсы. Данный метод автоматически вызывается при вызове деструктора CMenu.

Уффф! И это только методы посвященные инициализации? Таки да, но это не обозначает, что Вам необходимо будет использовать все указанные методы при работе с меню. Конечно, все зависит от конкретной ситуации. Итак, как проинициализировать меню мы с Вами выяснили. Теперь поговорим о методах, которые предназначены для работы, непосредственно, с самим меню и с пунктами меню.

BOOL DeleteMenu( UINT nPosition, UINT nFlags ); - удаляет пункт меню, задаваемый параметром nPosition. Параметр nFlags дополнительно используется для определения позиции: MF_BYCOMMAND - параметр удаляемого пункта задается через идентификатор элемента меню; MF_BYPOSITION - определяет, что параметр задает позицию текущего элемента меню относительно первого элемента меню (номер 0). Возвращаемое значение не ноль, елси функция успешно отработала.

BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL ); - функция отображает контекстное меню в любой части экрана. Параметр nFlags может принимать одно из нижеследующих значений: TPM_CENTERALIGN - центрирует горизонтально меню относительно координаты задаваемой Х, TPM_LEFTALIGN - размещает меню таким образом, что левый край совпадает с параметром Х, TPM_RIGHTALIGN - размещает меню таким образом, что правая сторона совпадает с координатой определяемой X,TPM_LEFTBUTTON - указывает на использование левой кнопки мыши, TPM_RIGHTBUTTON - указывает на использование правой кнопки мыши. X и Y определяют горизонтальное и вертикальное положение (в экранных координатах!) выпадающего меню. Параметр pWnd указывает на родительское окно, то есть окно, которое будет получать все WM_COMMAND сообщения от меню. Последний параметр определяет координаты прямоугольной области, в которой пользователь может выполнить выбор из меню. Если щелчок будет произведен вне этой области, это будет воспринято как отказ от работы с контекстным меню. По умолчанию (значение NULL) размер и расположение прямоугольной области будут совпадать с размерами контекстного меню.

BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL); - функция добовляет новый элемент в конец меню. Состояние элемента задается параметром nFlags, который может принимать одно или несколько из следующих значений: MF_CHECKED (MF_UNCHECKED) - при отображении элеменет меню отмечается (не отмечается) галочкой; MF_DISABLED (MF_ENABLED) - элемент меню доступен (блокирован), но отображается обычным цветом; MF_GRAYED - элемент меню блокировани и отображается серым цветом; MF_MENUBREAK - элемент меню верхнего уровня выводится с новой строки, а элемент подменю - в новомо столбце без раздилительной вертикальной линии; MF_MENBARUBREAK - аналогично MF_MENUBREAK, но дополнительно новый столбец отделяется вертикальной линией; MF_OWNERDRAW - меню само отвечает за изображение своего элемента; MF_POPUP - элемент меню имеет ассоциированное с ним подменю, дескриптор которого задается в парамете nIDNewItem; MF_SEPARATOR - элемент меню представляет собою горизонтальную раздилительную линию; этот флаг не может комбнироваться с MF_POPUP и MF_DISABLED; MF_STRING - элемент предоставляет собою текстовуюстроку.

Параметр nIDNewItem определяет идентификатор команды нового элемента, а если nFlags установлен в MF_POPUP, то дескриптор подменю.

BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ); - вставляет новый элемент меню в позицию, определяемую параметром nPosition, и сдвигают вниз существующие элементы. Значения всехпараметров, кроме nFlags, такие же, как и для предыдущей функции (AppendMenu). Параметр nFlags дополнительно используется для интерпретации значения nPosition (аналогично такому же параметру из метода DeleteMenu).

BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ); - параметры данного метода схожи с параметрами двух предыдущих методов, за исключением того, что на этот раз пунт меню не добавляется, а заменяет тот, на который указывает параметр nPosition.

BOOL RemoveMenu( UINT nPosition, UINT nFlags ); - метод во многом аналогичен ранее рассмотреному нами методу DeleteMenu. Принципиальная разница между указанными методами состоит в том, что при удалении подменю функцией DeleteMenu все связанные с ним (подменю) ресурсы освобождаются, а в варианте RemoveMenu - нет, и можно снова воспользоваться удаленным элементом по его идентификатору.

UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable ); - устанавливает элемент меню nIDEnableItem в одно из трех состояний определемое параметром nEnable: MF_ENABLED - доступное, MF_DISABLED - запрещенное и MF_GRAYED - заблокированное. Указанные флаги должны комбинироваться с одним из флагов MF_BYCOMMAND или MF_BYPOSITION, которые указывают на способ задания элемента - через идентификатор или порядковый номер в меню. Возвращаемое значение - предыдущее состояние элемента или -1, если ижентификатор не существует.

UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck ); - устанавливает или сбрасывает галочку у элемента меню. Параметр nCheck может приимать значение MF_CHECKED (MF_UNCHECKED) - пункт меню отмечен (неотменчен), в комбинации с флагами MF_BYCOMMAND или MF_BYPOSITION.

BOOL CheckMenuRadioItem( UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags); - помечает определенный элемент меню в группе переключателей, одновременно сбрасывая метку у остальных элементов группы. Для отметки элемента вместо галочки используется кружок. Параметр nIDFirst и nIDLast определяют первый и последний элемент в группе переключателей. Параметр nIDItem задает непосредственно выбранный элемент. Ну и последний параметр nFlags может принимать одно из рассмотренных ранее нами значений MF_BYCOMMAND или MF_BYPOSITION.

UINT GetMenuState( UINT nID, UINT nFlags ) const; - функция позволяет получить информацию о состоянии пункта меню nID. Как всегда, параметр nFlags принимает или значение MF_BYCOMMAND, или MF_BYPOSITION. Если указанный пункт не существует, то выозвращаемое значение равно 0xFFFFFFFF.

UINT GetMenuItemCount( ) const; - функция возвращает количетсво пунктов в выпадающем меню, или меню верхнего уровня, или -1 - в случае неудачной отработки функции.

UINT GetMenuItemID( int nPos ) const; - позволяет по номеру nPos пункта меню получит его идентификатор. Если это выпадающее меню, то возвращаемое значение -1, если же это разделитель (separator) - функция возврщает ноль.

CMenu* GetSubMenu( int nPos ) const; - возвращает указатель на выпадающее (pop-up) меню, которое задается позицией nPos. Если указанный пункт меню не существует возвращается NULL.

В заключении парочка методов CWnd.

BOOL SetMenu(CMenu* pMenu)-позволяет установить меню для окна. Функция возвращает не ноль, если меню было установлено, иначе ноль. Если pMenu равно NULL, то текущее меню удаляется.

void DrawMenuBar() - перерисовывает меню окна. Вызывайте эту функцию после любого изменения меню.




--------------------
Верю в смерть после жизни, в любовь после секса ,в крем после бритья smile        
PM ICQ   Вверх
tigger
Дата 19.12.2005, 16:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Awaiting Authorisation
Сообщений: 456
Регистрация: 30.4.2005

Репутация: 3
Всего: 4



Или еще динамичней чем предыдущий пример
в котором нет предела совершенству:

Код

for (register int i = 5000; i < 5011; i++)
{
char buf[5];
itoa(i,buf,10);
m_NewSubMenu.AppendMenu(MF_STRING, i, (LPCTSTR)buf);    
}


Единственное что прийдется еще сделать так это прописать события при выборе на пунктов меню...

smile



--------------------
user posted image
PM MAIL   Вверх
decembrist
Дата 21.12.2005, 13:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 238
Регистрация: 1.4.2004

Репутация: нет
Всего: нет



Вот-вот, события....Меню у меня создалось! Ура! smile
Как теперь к пункту меню прицепить событие, если созданы подпункты по последнему принципу с помощью цикла?
PM MAIL   Вверх
tigger
Дата 21.12.2005, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Awaiting Authorisation
Сообщений: 456
Регистрация: 30.4.2005

Репутация: 3
Всего: 4



Единственное что могу предложить так это заранее продумать что должно будет делаться при выборе пунктов меню и прописать события:

в секции файл (....cpp)
BEGIN_MESSAGE_MAP(CMainFrm, CDialog)

добавить
ON_COMMAND(5000, OnKill)
ON_COMMAND(5001, OnKill1)
----------------------------------
ON_COMMAND(5010, OnKill10)

а в файле (.....h)

afx_msg void OnKill();
afx_msg void OnKill1();
---------------------------------
afx_msg void OnKill10();

Ну вот пожалуй и все.


--------------------
user posted image
PM MAIL   Вверх
Earnest
Дата 21.12.2005, 18:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата
Единственное что могу предложить так это заранее продумать что должно будет делаться при выборе пунктов меню и прописать события:

Какая уж тут динамика...
Чтобы сделать "совсем" динамически, нужно следующее:
1) Структура, что-то типа
Код

struct CCmdInfo
{
   typedef void (CCmdTarget::*PFNONCOMMAND)(UINT nID);
   typedef void (CCmdTarget::*PFNONUPDATE)(CCmdUI*);

   CString m_sTitle;  // можно и не хранить а брать из реестра по ID
   UINT     m_nID;
   PFNONCOMMAND m_OnCommand;
   PFNONUPDATE     m_OnUpdate;

   void Execute(CCmdTarget* pObj,UINT nID)
   {
      (pObj->*m_OnCommand)(nID);
   }
   void Update(CCmdTarget* pObj,CCmdUI* pCmdUI)
   {
      (pObj->*m_OnUpdate)(pCmdUI);
   }
};

2) Где-нибудь объявить реестр (список, массив, etc) таких структур.
Как ты их будешь набивать - дело твое, вариантов много.

3) При инициализации создать динамически меню по этому реестру - ну, это как уже показывали.

4) Обработка: переопределить виртуальную функцию OnCmdMsg у того объекта, который будет динамически обрабатывать команды. Примерно так:

Код

BOOL CMyDoc::OnCmdMsg (UINT nID,int nCode,void* pExtra,AFX_CMDHANDLERINFO* pInfo) 
{
   BOOL bHandled = false;

   CCmdInfo* pCmd = FindCmd(nID);  // ищем команду в нашем реестре
   if (pCmd) 
   {
      switch (nCode)
      {
         case CN_COMMAND: 
            if (!pInfo) pCmd->Execute(this,nID); 
            break;

         case CN_UPDATE_COMMAND_UI: 
            if (pExtra) 
            {
               CCmdUI* pCmdUI = (CCmdUI*)pExtra;
               pCmd->Update(this,pCmdUI);
            }
            break;
      }
      bHandled = TRUE;
   }

   // стандартная обработка - message map и т.д.
   return bHandled ? TRUE : base_class::OnCmdMsg(nID,nCode,pExtra,pInfo);
}

Это самая примитивная схема. Недостаток - нужно следить, чтобы в команде m_OnCommand и m_OnUpdate соответствовали объекту, для которого их будут вызывать. Можно пойти дальше, и вместо того, чтобы хранить в команде указатели на обработчики, сделать Execute и Update виртуальными, и добавить им еще один параметр - CCmdTarget - и пусть делают что хотят. Тогда можно использовать один и тот же реестр комманд и для документа, и для вида. Таким образом, обработку команд можно вообще отделить от документа/вида.


--------------------
...
PM   Вверх
JoyEx
Дата 21.12.2005, 19:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Дядя
**


Профиль
Группа: Участник
Сообщений: 323
Регистрация: 21.6.2005
Где: Москва, РФ

Репутация: 5
Всего: 12



Избегайте ID < 0x100, выбирайте ID меню в диапазоне 256 - 64Кб


--------------------
Если ты уже никого не боишься, значит ты самый страшный 
PM IM ICQ AOL YIM MSN   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.0680 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.