Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Проблемы с работой динамического меню


Автор: EnergoHokum 8.1.2010, 11:40
На форме определён обработчик события OnMouseUp. Внутри этого обработчика я пытаюсь создать всплывающее меню, и привязать его элемент(ы) к функции(ям) этой-же формы:

Код


void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int x, int y)
{
  std::auto_ptr<TPopupMenu> puMenu(new TPopupMenu(this));
  std::auto_ptr<TMenuItem> item(new TMenuItem(puMenu.get()));
  item->OnClick=ItemSelected;
  item->Caption="Form mouse up";
  item->Enabled=true;
  puMenu->Items->Add(item.get());
  TPoint pt(x,y);
  pt=ClientToScreen(Point(x,y));
  puMenu->Popup(pt.x,pt.y);
  return;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ItemSelected(TObject* Sender)
{
  //Какое-нить действие
  return;
}



Но в этом варианте при выборе пункта меню не происходит вызова ItemSelected. В случае же создания puMenu и item глобально для класса (т. е. объявление в заголовочном файле, puMenu=new TPopupMenu(this); в конструкторе...) ItemSelected вызывается нормально. Это какое-либо принципиальное ограничение VCL, или я что-то недопонял?
Очень хочется не размазывать меню (а их будет много) по пространству имён класса, а компактно создавать/уничтожать только там, где они действительно нужны.

Автор: SVN74 8.1.2010, 13:04
А почему бы вам просто не воспользоваться (мышкой),  благо Builder дает большие возможности  для этого...
Создаете на форме нужное количество PopupMenu(шек) с нужными действиями и затем привязываете их к конкретным компонентам на форме (для каждого компонента в свойствах есть LINKADE) там есть пункт PopupMenu, в котором выбираете номер нужного меню и все...
Ничего писать вообще не надо. 
У каждого PopupMenu есть свои события , - там и пишите нужные действия. 

Автор: EnergoHokum 8.1.2010, 13:42
Цитата

А почему бы вам просто не воспользоваться (мышкой)

Не, мышкой -- не вариант. Не получится. smile

Автор: SVN74 8.1.2010, 13:43
Цитата(EnergoHokum @  8.1.2010,  13:42 Найти цитируемый пост)
Не, мышкой -- не вариант. Не получится. 

Почему?

Автор: EnergoHokum 8.1.2010, 18:59
Цитата

Почему?

1. Консепся такая (не люблю программировать мышью)
2. Уже из принципа хочется разобраться, почему не получился вариант с созданием TPopupMenu "на месте".

Автор: xvr 8.1.2010, 19:43
Вот это
Код

  std::auto_ptr<TPopupMenu> puMenu(new TPopupMenu(this));
  std::auto_ptr<TMenuItem> item(new TMenuItem(puMenu.get()));
должно быть так (это как минимум)
Код

  std::auto_ptr<TPopupMenu> puMenu(new TPopupMenu(NULL));
  TMenuItem* item = new TMenuItem(puMenu.get());

Автор: EnergoHokum 8.1.2010, 20:28
Цитата

должно быть так (это как минимум)
Код

std::auto_ptr<TPopupMenu> puMenu(new TPopupMenu(NULL));
TMenuItem* item = new TMenuItem(puMenu.get());


А можно подробнее? Отчего TPopupMenu(NULL), а не TPopupMenu(this), почему item нельзя затолкать в автоуказатель (его удалением займётся puMenu?), и если это "как минимум", то каким должен быть "как максимум"? Что я упустил/неправильно_понял в изначальном варианте?

Автор: xvr 8.1.2010, 20:44
Цитата(EnergoHokum @ 8.1.2010,  20:28)
Отчего TPopupMenu(NULL), а не TPopupMenu(this), 

Потому что параметр конструктора TPopupMenu - это его owner. Т.е. тот, кто его будет удалять. Вы же его удаляете сами (через auto_ptr), так что не надо его отдавать форме
Цитата

почему item нельзя затолкать в автоуказатель (его удалением займётся puMenu?), 
Именно
Цитата

и если это "как минимум", то каким должен быть "как максимум"?
'как минимум' эначит, что это то, что мне сразу бросилось в глаза. Возможно там есть еще косяки (а может и нету)  smile 

Автор: EnergoHokum 8.1.2010, 20:56
Цитата

параметр конструктора TPopupMenu - это его owner. Т.е. тот, кто его будет удалять.

А-а-а! Вот, блин, сила привычки! Оказывается, иногда и подумать надо! smile В VCL-е всегда owner удаляет явных потомков, или есть исключения из правил?

Цитата

Возможно там есть еще косяки (а может и нету)

Блин, вот хоть прям щас всё бросай, и беги на работу -- так охота проверить предложенный вариант! smile Завтра с утра побегу, а ночью ворочаться буду. smile

Автор: xvr 9.1.2010, 13:10
Цитата(EnergoHokum @ 8.1.2010,  20:56)
В VCL-е всегда owner удаляет явных потомков, или есть исключения из правил?

Всегда, на то он и owner. 
Однако, при явном удалении объекта, он себя удаляет из списка объектов owner'а. Но я бы на это надеятся не стал  smile 

Автор: EnergoHokum 11.1.2010, 11:04
Вышеприведённые изменения на работу не повлияли, по прежнему в случае объявления TPopupMenu в теле обработчика MouseUp не вызываются связанные с TMenuItem-ами обработчики.
Но если объявить TPopupMenu глобально для класса, и проинициализировать в конструкторе -- всё работает корректно.
Открытым остаётся вопрос: с чем связано такое поведение, и можно ли его забороть?

Автор: mrbrooks 11.1.2010, 11:12
Цитата(EnergoHokum @  11.1.2010,  11:04 Найти цитируемый пост)
Открытым остаётся вопрос: с чем связано такое поведение, и можно ли его забороть? 

никак. у тебя объект живет во время обработки события OnMouseUp - создается и уничтожается. Причем настолько быстро - что ты даже этого не замечаешь. Выход - либо сделать указатель на контрол  глобальным (не айс), либо как член класса формы (айс).

Автор: EnergoHokum 11.1.2010, 11:28
Цитата

у тебя объект живет во время обработки события OnMouseUp - создается и уничтожается. Причем настолько быстро - что ты даже этого не замечаешь.

Непонятно. Для отображения меню я вызываю у TPopupMenu* метод Popup, собственно.
Сразу после выбора пункта меню, когда запускается обработчик -- происходит возврат из Popup-а? Но отчего, в таком случае, не случается access violation, если само меню уже уничтожено?

Автор: mrbrooks 11.1.2010, 12:02
EnergoHokum, здесь все связанно с жизненным циклом объекта. К примеру кнопка никогда не появится:
Код

#include <memory>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 std::auto_ptr<TButton>btn(new TButton(this));
 btn->Parent = Form1;
 btn->Caption = "Hello";
 btn->Show();
}


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

если btn сделать членом класса (или глобально) и выделять и высвобождать память вручную - то все ок - в данном случае, имхо, использовать умные указатели не совсем удобно - но можно. 

Автор: xvr 11.1.2010, 15:06
Цитата

Для отображения меню я вызываю у TPopupMenu* метод Popup, собственно
Popup не блокирует исполнение. Он просто показывает меню и немедленно возвращается.

Автор: EnergoHokum 11.1.2010, 15:31
Цитата

Popup не блокирует исполнение. Он просто показывает меню и немедленно возвращается.

У меня вообще такое нехорошее ощущение, что в Builder-е не поддерживается вложенность обработчиков событий, а реализуется очередь по типу "выполнен возврат из одного обработчика -- запускаем следующий".

Автор: xvr 11.1.2010, 17:33
Цитата(EnergoHokum @ 11.1.2010,  15:31)
У меня вообще такое нехорошее ощущение, что в Builder-е не поддерживается вложенность обработчиков событий, а реализуется очередь по типу "выполнен возврат из одного обработчика -- запускаем следующий".

Поддерживает, и не Builder, а сама Винда.
Позови ShowModal от какой нибудь формы в обработчике - получишь по полной программе  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)