Поиск:

Ответ в темуСоздание новой темы Создание опроса
> сохранить динамическое меню - как . 
:(
    Опции темы
suvolod
Дата 1.11.2010, 11:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Всем доброе время суток. Вопрос такой. У меня главное окно программы - диалого. К нему прицеплено меню, которое я создал в ресурсах. 

В этом меню одно из подменю имеет динамически создаваемые итемы. Т.е оно представлет из 
себя менюшку вида

Пользователи=>
                           Новый пользователь
                           Удалить пользователя
                           ---------------------------- (сепаратор) 
                               Иван
                           v  Максим
                               Женя
                           ... и т.д. 


Т.е по кнопке "Новый пользователь" появляется новый элемент меню, по кнопке "Удалить пользователя" удаляется текущий. Текущий выбранный пользователь помечается флажком. 

В общем-то, у меня пока два вопроса. 

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

2. Не совсем понятно, как работать с таким динамическим элементом. Мне надо пометить "галочкой" элемент, на котором щелкнули, и соответственно снять галочку с предыдущего. Но ведь элементы-то динамические. Во первых, я не могу заранее задать для них обработчик для вызова CheckMenuItem, потому-что на момент загрузки программы может не быть вообще ни одного динамически созданного элемента меню. А во вторых, как-то неправильно наверное на каждый элемент (а их может быть и 20 штук) писать один и тот-же обработчик, который фактически делает лишь одно - ставит галочку на текущем элементе и проверяет/снимает галочку с остальных. 

Это сообщение отредактировал(а) suvolod - 1.11.2010, 11:54
PM MAIL   Вверх
Earnest
Дата 1.11.2010, 13:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



1. Да, именно так. Или в реестр.
2. Почему же не можешь? Другое дело, что нужно заранее выделить пл команд для этих итемов - ты же не будешь присваивать идентификаторы какие попало? Выдели диапазон команд заранее, скажем :
Код

#define ID_USER_FIRST 20000 
#define MAX_USERS      100
#define ID_USER_LAST  (ID_USER_FIRST+MAX_USERS-1)

А потом используй цикл по этим элементам: если элемент с заданным номером существует, делай с ним что-нибудь.
Хотя гораздо удобнее использовать обработчики команд с макросами ON_COMMAND_RANGE и ON_UPDATE_COMMAND_UI.
Соответственно, обработчик для установки галки может выглядеть так:
Код

void CMyCoolFrame::OnUpdateUser (CCmdUI* pCmdUI)
{
     // предполагается, что член m_CurUser хранит ид-р текущего юзера)
     pCmdUI->SetCheck (pCmdUI->m_nID == m_CurUser);
}



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


Шустрый
*


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

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



Earnest, большое спасибо. Теперь хоть в голове стало проясняться smile. А по первому вопросу хотел все-таки уточнить, хотя может и глупый будет вопрос - а через сериализацию объекты типа меню никак нельзя сохранять... или CMenu - это не потомок CWnd?
PM MAIL   Вверх
Earnest
Дата 2.11.2010, 09:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Сериализация - это по-определению лишь "расплющивание" динамических структур данных. Как бы ты не записывал свои меню в файл - это по любому будет сериализация.
CMenu - не потомок CWnd, но потомок CObject, так что метод Serialize у него тоже есть. Только толку-то. В оболочку вызов (для меню) не встроен, так что верхнюю голову напрячь придется. И придумать, куда это дело вставить. Кроме того, стандартная сериализация удобна для сохранения\загрузки документов. Вот если состав меню нужно записать именно в документ, тогда другое дело. Но и тогда удобнее делать "руками": в документ включить список этих юзеров, обновлять и поддерживать его именно в документе, а меню модифицировать, скажем, на OnUpdate, тем более что технология в MFC отработана: проще всего списать слова в MFC, где модифицируется меню MRU-документов. Дело в том, что там есть некоторые неочевидные хитрости, так что чем ковыряться самому, проще слова списать.


--------------------
...
PM   Вверх
suvolod
Дата 2.11.2010, 14:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Earnest, добрый вечер. Наконец-то все заработало через ON_COMMAND_RANGE и ON_UPDATE_COMMAND_UI_RANGE

Два вопроса еще есть, подскажи, очень нужно. 

1. Во первых, я так и не понял, как сделать это-же без этих макросов. 

ты писала...

Цитата

А потом используй цикл по этим элементам: если элемент с заданным номером существует, делай с ним что-нибудь.
Хотя гораздо удобнее использовать обработчики команд с макросами ON_COMMAND_RANGE и ON_UPDATE_COMMAND_UI.


... поясни подробнее, как это происходит.  Создавая новый элемент меню, я вызываю что-то типа

cMenu->AppendMenuW(MF_STRING,ID_USER_FIRST+count, s);

... то есть идентификатор элементу я присваиваю, а функцию обработки - нет. Если заранее прописать идентификаторы в ON_COMMAND_RANGE - тут все ясно, но я понял, что есть возможность назначить обработчик сообщения без этого макроса. Т.е каждому новому элементу меню будет присваиваться некая фция (одна?), в ней крутиться цикл (хотя, имхо тут уместнее будет switch), и в зависимости от id текущего итема выполняются те или иные действия.. Я правильно понял?

2. Я прошу совета - как лучше организовать такой функционал. Каждый пункт меню (с именем юзера)должен хранить какие-то доп. данные. Если быть точнее - в списке может быть один и тот-же юзер, но работающий с разными базами. Соответственно по выбору другого пункта должны произойти изменения - юзер остаться прежним, а база - старая закрыться и загрузиться новая.. Как это лучше сделать?  На ум приходит пока только задавать текстовое значение итема вида "Юзер  База", а затем читать и разбирать эту строку... но может есть способ лучше? Так как имя базы, например, может быть коротким, а путь до нее - очень длинным и выводить полный путь в менюшке нереально.. 




Это сообщение отредактировал(а) suvolod - 2.11.2010, 14:54
PM MAIL   Вверх
Earnest
Дата 2.11.2010, 21:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(suvolod @  2.11.2010,  15:52 Найти цитируемый пост)
но я понял, что есть возможность назначить обработчик сообщения без этого макроса.

Нет, динамически добавить функцию-обработчик нельзя.
Можно без макросов, переопределив OnCmdMsg. Примерно то же самое, что в обработчике, но нужно проверять, что пришло обновление и номер ид-ра. А про цикл я имела в виду, когда ты спрашивал про CheckMenuItem: можно переопределить обработчик OnInitMenuPopup (откуда все это вызывается), и там в цикле пройтись по пунктам меню и всем поставить Check. Но это самый плохой способ. Через макросы и обработчике лучше, да и проще в данном случае. Самый гибкий - через OnCmdMsg, но пока это для тебя сложновато, да и задача вполне решаема через макросы.

Цитата(suvolod @  2.11.2010,  15:52 Найти цитируемый пост)
Я прошу совета - как лучше организовать такой функционал.

С пунктом меню можно связать некоторое значение целого типа, не больше. Это может быть индекс данных или указатель. Но сами данные должны жить где-то еще. Хранить динамические данные только в пункте меню не стоит - задолбаешься за временем жизни следить.
Я бы сделала так:
Где-то - в приложении, документе или как-то еще - зависит от структуры приложения - хранится список или массив пар "юзер" + "данные". Юзер, разумеется, должен представляться не именем, а каким-то ид-ром, чтобы не дублировать бесконечно строки. 
Код


typedef  DWORD USERID;   // например так

// инфа о конкретном пользователе
struct CUserInfo
{
   CString m_sName;
   ...   // возможно, что-то еще
};

typedef std::map <USERID, CUserInfo> CUsers;  // где-то в приложении должен жить такой контейнер, где будет все о эзерах

// данные, с которыми работает пользователь
struct CData
{
   CString m_sDataBase;   // как-то храним инфу о базах, с которыми можно работать
   ...
};

typedef std::vector <std::pair<USERID, CData> > CUserData;  // так храним то о чем ты говорил - кто с кем работает
// если элементы UserData можно динамически переставлять или удалить, то лучше использовать список или multimap



Меню составляем из данных контейнера CUserData. Добавлять строки меню лучше через InsertMenuItem, это гибче. Как формировать строку, чтобы они отличались для одинаковых юзеров - дело хозяйское, но не в коем случае не для того, чтобы впоследствие ее разбирать - нафик, у нас ведь вся инфа есть. Смысл в том, чтобы получив определенную команду из нашего пула найти соответствующий элемент данных - CData.
Это можно делать по-разному, в зависимости от задачи и желания:
1) Самое простое, если элементы никогда не удаляются, а их порядок безразличен - в качестве контейнера использовать вектор, а связь команды меню с элементом иметь неявную, т.е. индекс в векторе = CmdID - ID_USER_FIRST. Элементы-то мы добавляем в меню по-порядку, вот пусть так и живет. Однако, если элементы CData могут удаляться, то это уже не прокатит без дополнительных наворотов.
2) И вот один из них: меню заполняем не один раз при загрузке, а на OnUpdate (как я писала в самом первом посте). Хотя можно и по специальному пинку просто перезаполнять меню - при изменени списка CUserData. Т.е. при изменении контейнера CUserData переинициализируем меню.
3) Можно и по-другому извратиться - например, хранить данные не в массиве, а в списке, а при добавлении элементов меню прописывать в них указатели на соотв. элементы списка
4) Или напротив, в меню ничего дополнительно не писать, а в CData добавить поле - ид-р команды, куда заносить номер команды при создании соответствующего пункта меню.
И т.д.
Но я бы остановилась на первом варианте, как самом простом, с возможным добавлением п. 2, если нужно.  



--------------------
...
PM   Вверх
suvolod
Дата 3.11.2010, 06:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Большое спасибо за точные и подробные ответы smile. Жаль, что у меня на этом форуме нет еще 100 постов и я не могу поставить тебе заслуженный плюсик.

Это сообщение отредактировал(а) suvolod - 3.11.2010, 06:08
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




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


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

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