Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.12.04;
Скачать: CL | DM;

Вниз

Динамический OnClick   Найти похожие ветки 

 
msguns ©   (2005-11-02 18:28) [0]

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

Спасибо за помощь


 
DiamondShark ©   (2005-11-02 18:37) [1]

z: TMethod;

z.Code := @MyRegularProc;
z.Data := nil;
Item.OnClick := TNotifyEvent(z);

procedure MyRegularProc(Self: TObject; Sender: TObject)


 
Leonid Troyanovsky ©   (2005-11-02 18:40) [2]


> DiamondShark ©   (02.11.05 18:37) [1]

> z.Data := nil;


IMHO, все же,  не nil.

--
Regards, LVT.


 
DiamondShark ©   (2005-11-02 18:43) [3]


> IMHO, все же,  не nil.

Можно и не nil.
Разницы-то...


 
Leonid Troyanovsky ©   (2005-11-03 09:22) [4]


> DiamondShark ©   (02.11.05 18:43) [3]

> Можно и не nil.
> Разницы-то...


Небольшая разница, все же, есть.
Для обработчиков методы и свойства содержащего их класса привычно
доступны. Противные же случаи толкают на грех глобализма.

--
Regards, LVT.


 
msguns ©   (2005-11-03 10:12) [5]

Вот такой код:

procedure DBGridEx_MenuClick(Self: TObject; Sender: TObject);
begin
Досюда дело не доходит вообще  
 ShowMessage(TMenuItem(Sender).Caption);
end;

procedure DBGridEx_ShowHideColumn(Grid: TDBGrid; TitleColumn: TColumn);
// Позволяет добавить или убрать колонку грида
// Вызов этой процедуры следует выполнять по событию OnTitleClick грида
var
 pmn: TPopupMenu;
 pmi: TMenuItem;
 X,Y: integer;
 i: integer;
 e: TMethod;
begin
 // Определить текущую колонку грида
 pmn := TPopupMenu.Create(nil);    // Создать всплывающее меню
 e.Code := @DBGridEx_MenuClick;
 e.Data := nil;
 with pmn do
   begin
    pmi := TMenuItem.Create(pmn);
    pmi.Caption := "Убрать колонку";
    pmi.OnClick := TNotifyEvent(e);
    Items.Add(pmi);
    pmi := TMenuItem.Create(pmn);
    pmi.Caption := "Добавить колонку";
    pmi.OnClick := TNotifyEvent(e);
    Items.Add(pmi);
    TitleColumn.PopupMenu := pmn;
    // Открыть меню под (над) заголовком колонки
    with TDBGridCrack(Grid) do
      Y := Grid.ClientOrigin.Y+CellRect(Col,Row).Bottom-CellRect(Col,Row).Top;
    X := Grid.ClientOrigin.X;
    for i := 0 to TitleColumn.Index-1 do
      if Grid.Columns[i].Visible then
         X := X+Grid.Columns[i].Width+1;
    pmn.Popup(X,Y);
    Free;
   end;
end;


не работает.
Вернее, нет реакции на клик в меню.


 
Игорь Шевченко ©   (2005-11-03 10:33) [6]

msguns ©   (03.11.05 10:12) [5]

Вот такой код, например, работает:

unit main;

interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 Menus, StdCtrls;

type
 TForm1 = class(TForm)
   PopupMenu: TPopupMenu;
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
 end;

var
 Form1: TForm1;

implementation

{$R *.DFM}

procedure MenuEventHandler (Self: TObject; Sender: TObject);
begin
 ShowMessage ("Event handler traps an event");
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 mi: TMenuItem;
 method: TMethod;
begin
 mi := TMenuItem.Create (PopupMenu);
 mi.Caption := "Catch me!";
 PopupMenu.Items.Add (mi);
 method.Code := @MenuEventHandler;
 method.Data := nil;
 mi.OnClick := TNotifyEvent(method);
end;

end.


 
msguns ©   (2005-11-03 11:18) [7]

Ну да !
В твоем примере Button1Click является методом класса, мне же надо "засунуть" в событие регулярную процедуру. Ибо в юните, отрывок из которого я привел, нет формы (это одна из процедур библиотечного юнита универсального назначения).

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

А как быть в этом случае ? У Тексейры с Пачеко такого я не нашел, поиск в хэлпе и MSDN (TMethod) ничего не дал ;(


 
Игорь Шевченко ©   (2005-11-03 11:30) [8]

msguns ©   (03.11.05 11:18) [7]

В моем примере ButtonClick, как ты сам понимаешь, является создателем элемента меню, поэтому является он методом объекта, не является - какая нафиг разница ? Обработчик нажатия на меню не является методом класса, как ты, опять же, можешь видеть из моего кода, так что я не совсем понимаю, в чем разница между твоим и моим кодом.


 
Leonid Troyanovsky ©   (2005-11-03 11:47) [9]


> msguns ©   (03.11.05 11:18) [7]

> Такой способ я успешно использовал в процедурах другого
> библ.юнита, который динамически создает формы-диалоги. Там,
>  ессно, есть объявление форм и все работает !


Ну, ты ходил-ходил и заслуженно наступил на грабли.
Если procedure DBGridEx_ShowHideColumn объявлена в dll,
то я сильно сомневаюсь в работоспособности тамошнего меню.
Как минимум, создать его следует в exe.
А как максимум - даже неохота язык мозолить.

--
Regards, LVT.


 
Игорь Шевченко ©   (2005-11-03 12:03) [10]

Leonid Troyanovsky ©   (03.11.05 11:47) [9]


> Если procedure DBGridEx_ShowHideColumn объявлена в dll,
> то я сильно сомневаюсь в работоспособности тамошнего меню.
>
> Как минимум, создать его следует в exe.


Я очень извиняюсь, а разве GExperts не подобным образом работает ? В смысле, с процедурами обработки меню в DLL.


 
Leonid Troyanovsky ©   (2005-11-03 12:19) [11]


> Игорь Шевченко ©   (03.11.05 12:03) [10]

> > Как минимум, создать его следует в exe.

> Я очень извиняюсь, а разве GExperts не подобным образом
> работает ? В смысле, с процедурами обработки меню в DLL.
>


А чего ты извиняешься? :)
Это мне неудобно, что я не щупал GExperts.

Но, думаю, что дело не в месте размещения кода,
а в банальных дубликактах глобальных переменных,
в т.ч. и, скажем, для окна popup menues.

--
Regards, LVT.


 
jack128 ©   (2005-11-03 12:27) [12]

Leonid Troyanovsky ©   (03.11.05 12:19) [11]
> Я очень извиняюсь, а разве GExperts не подобным образом
> работает ? В смысле, с процедурами обработки меню в DLL.


Его dll"ки скомпилированы с ран-тайм пакетами, со всеми втекающими и вытекающими..


 
Игорь Шевченко ©   (2005-11-03 12:33) [13]

jack128 ©   (03.11.05 12:27) [12]


> Его dll"ки скомпилированы с ран-тайм пакетами


Кстати, да :)


 
msguns ©   (2005-11-03 12:36) [14]

>Игорь Шевченко ©   (03.11.05 11:30) [8]

Прошу прощения, невнимательно посмотрел (почта задрала !)
В твоем примере само TPopupMenu объявлено в интерфейсе, у меня же такого объявления быть не может ;(

Сейчас плюнул на универсальность и делаю все в самом приложении. И та же картина ! На клик реакции нету !

Уже пробовал в форме объявить обработчик клика на пункте меню и его назначить динамически созданным пунктам динамически созданного же попменю - реакции НОЛЬ ! При клике в меню просто оно закрывается, а до моего обработчика дело не доходит ;((


 
msguns ©   (2005-11-03 12:43) [15]

В общем получается интересная весчь.
Если я кидаю на форму попменю, в нем определяю оно подменю и ему создаю OnClick, а затем в процедуре чищу, заполняю нужными итемами и назначаю им этот же обработчик, то все лихо пляшет !
СтОит же мне попытаться создать попменю динамически и проделать с ним то же самое, но с регулярной процедурой (или даже тем же самым обработчиком), как реакции на клик просто нетути.

Блин, явно не хватает понимания чего-то фундаментального ;))


 
msguns ©   (2005-11-03 12:51) [16]

Видимо все же дело в "перехватчике" сообщения клика. Стоп !!! Щас попробую...


 
Игорь Шевченко ©   (2005-11-03 12:56) [17]


> СтОит же мне попытаться создать попменю динамически


И назначить его тому компоненту, при клике на который должно всплывать меню....


 
msguns ©   (2005-11-03 13:14) [18]

>Игорь Шевченко ©   (03.11.05 12:56) [17]
>И назначить его тому компоненту, при клике на который должно всплывать меню....

Все значительно интереснее...
Значицца такочки:
1. Кидаю на форму пупменю, определяю в ем один пункт и для это пункта в OI даблкликом определяю обработчик Click1.
В процедуре чищу и заполняю это меню, назначая пунктам OnClick := Click1.
Запускаю - на ура !
2. В процедуре вместо "кинутого" пупменю создаю пупменю2, для него проделываю то же самое. Запускаю - лейся, песня !

Ага !, подумал Штирлиц, нащупав наган. Создаю ручками обработчик Click2, полностью идентичный Click1.
В проце заменяю OnClick := Click1 на OnClick := Click2
Запускаю - болт ! Убираю пупап2, вставляю вместо него "кинутый" пупап.
Запускаю - БОЛТ !

В OI заменяю (выбором из списка) Click1 на Click2 - БОЛТ ! В обоих вариантах !!!. Возвращаю Click1 - БОЛТ

Что ж это делается-то, люди добрые ? А ?


 
Игорь Шевченко ©   (2005-11-03 13:20) [19]

msguns ©   (03.11.05 13:14) [18]

Если не трудно, тоже самое, но без слесарных терминов


 
msguns ©   (2005-11-03 13:35) [20]

>Игорь Шевченко ©   (03.11.05 13:20) [19]
>Если не трудно, тоже самое, но без слесарных терминов

Вот какие мы серьезные ;))

Дело в том, что все работает (имеется в виду то, что нажатие на левую кл.мыши благополучно попадает в обработчик OnClick пункта меню), если я сам это обработчик получил "даблкликом" в OI
При этом не играет роли, какой объект TPopupMenu я задействую в своей процедуре: тот, который визуально "положен" на форму либо созданный динамически в самой процедуре.
Но как только я сменил обработчик (неважно как, то ли в OI, то ли в коде), обработчик на это событие не реагирует.


 
Игорь Шевченко ©   (2005-11-03 13:47) [21]

msguns ©   (03.11.05 13:35) [20]

Что в моем коде отличается от твоего ? Я создаю динамически элемент PopupMenu (MenuItem), динамически же присваиваю ему событие на свойство OnClick, все работает.

Может быть, ты по шагам будешь двигаться, так оно и выяснится, что и где ты делаешь не так.


 
jack128 ©   (2005-11-03 13:52) [22]

Приведи dfm и pas файлы с НЕрабочем вариантом.


 
msguns ©   (2005-11-03 13:54) [23]

Вот полный текст процедуры в варианте с визуальным меню:

procedure TfrmChild.ShowHideGridColumn(Grid: TDBGrid; TitleColumn: TColumn);
// Позволяет добавить или убрать колонку грида
// Вызов этой процедуры следует выполнять по событию OnTitleClick грида
var
 pmi: TMenuItem;
 X,Y: integer;
 i: integer;
 m: TMethod;
begin
 // Определить текущую колонку грида
 LoadHideColumns(Grid,ListColumns);
 with pmnuGridCol do
   begin
    Items.Clear;
    pmi := TMenuItem.Create(pmnuGridCol);
    pmi.Caption := "Убрать колонку";
    pmi.OnClick := mniDelColClick;
    Items.Add(pmi);
    if ListColumns.Count>0 then
      begin  // Создать второй пункт меню
       pmi := TMenuItem.Create(pmnuGridCol);
       pmi.Caption := "Добавить колонку";
       Items.Add(pmi);
       for i := 0 to ListColumns.Count-1 do
         begin
          pmi := TMenuItem.Create(pmnuGridCol);
          pmi.Caption := PColDef(ListColumns[i])^.ColName;
          pmi.OnClick := mniDelColClick;
          Items[Items.Count-1].Add(pmi);
         end;
      end;
    // Открыть меню под (над) заголовком колонки
    with TDBGridCrack(Grid) do
      Y := Grid.ClientOrigin.Y+CellRect(Col,Row).Bottom-CellRect(Col,Row).Top;
    X := Grid.ClientOrigin.X;
    for i := 0 to TitleColumn.Index-1 do
      if Grid.Columns[i].Visible then
         X := X+Grid.Columns[i].Width+1;
    Popup(X,Y);
    //Free;
   end;
end;


Строка Free как ты понимаешь, осталась от варианта с динамическим меню. Если ее откомментарить, то до AV меню раскроется и на клик реакции не будет. В приведенном же виде реакция есть.

Это один из непонятных мне моментов. Другой заключался в отказе при замене обработчика на аналогичный. Все равно когда, в дизайне или в рантайме.


 
jack128 ©   (2005-11-03 14:03) [24]

Ты можешь сделать такой вариант, чтобы я всавил код, нажал f9 и выледз твой глюк ??
msguns ©   (03.11.05 13:54) [23]
то до AV меню раскроется и на клик реакции не будет

Естественно не будет. Меню то уже будет уничтожено.


 
msguns ©   (2005-11-03 14:05) [25]

>jack128 ©   (03.11.05 13:52) [22]
>Приведи dfm и pas файлы с НЕрабочем вариантом.

Они шибко большие.

Ладно, спасибо всем за участие и сочувствие. Перенес процедуру в рабочий проект и решил проблему. Жаль, что не вышло с укиверсализацией, позже подумаю..


 
Leonid Troyanovsky ©   (2005-11-03 15:08) [26]


> msguns ©   (03.11.05 14:05) [25]

> Перенес процедуру
> в рабочий проект и решил проблему. Жаль, что не вышло с
> укиверсализацией, позже подумаю..


И где тут универсализация?

Если используемые гриды нуждаются в симбиозе с меню,
то, IMHO, следует сделать их наследниками одного прообраза.

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

--
Regards, LVT.


 
msguns ©   (2005-11-03 16:34) [27]

>Leonid Troyanovsky ©   (03.11.05 15:08) [26]
>И где тут универсализация?

В данном случае ее сделать и не получилось

>А сделать универсальный обработчик меню на все случаи жизни,
все равно, особого смысла не имеет.

Это не обработчик меню, а набор функций (процедур) для манипулирования гридами и данными в этих гридах. В частности, процедуры удаления-добавления колонок в дбгрид, сохранения-восстановления текущей конфигурации грида, поиска-сортировки, вывода Excel содержимого отображенных полей датасета, подсчета сумм по колонке и т.д., т.п.
В одной из процедур я хотел сделать "прятание"-показ колонок через меню непосредственно под/над "кликнутым" заголовком, с помощью которого юзер может либо спрятать текущую (кликнутую) колонку, либо добавить в этом месте одну из "спрятанных".


 
evvcom ©   (2005-11-03 16:42) [28]

У тебя похоже где-то что-то перетирается или не туда записывается. Включи использование отладочных DCU, поставь бряк на if Assigned(FOnClick) и посмотри, чему он там равен. Наверняка nil.


 
Leonid Troyanovsky ©   (2005-11-03 16:45) [29]


> msguns ©   (03.11.05 16:34) [27]

> Это не обработчик меню, а набор функций (процедур) для манипулирования
> гридами и данными в этих гридах. В частности, процедуры
> удаления-добавления колонок в дбгрид, сохранения-восстановления
> текущей конфигурации грида, поиска-сортировки, вывода Excel
> содержимого отображенных полей датасета, подсчета сумм по
> колонке и т.д., т.п.


Все перечисленное свидельствует лишь в пользу создания некоего
прообраза, от которого и будут наследоваться все используемые гриды.


> В одной из процедур я хотел сделать "прятание"-показ колонок
> через меню непосредственно под/над "кликнутым" заголовком,
>  с помощью которого юзер может либо спрятать текущую (кликнутую)
> колонку, либо добавить в этом месте одну из "спрятанных".


<нудно>
Для этого упомянутый прообраз снабжается неким индексированным
свойством (постой-ка, а почему это не TColumn.Visible?), которое
устанавливается посредством некоего action, назначаемоего некоему мню.
</нудно>

--
Regards, LVT.


 
jack128 ©   (2005-11-03 16:51) [30]

evvcom ©   (03.11.05 16:42) [28]
if Assigned(FOnClick) и посмотри, чему он там равен. Наверняка nil.


До туда  код тоже не дойдет. Когда придет WM_COMMAND форма полезет искать меню с соответствующим ID"шником, чтобы вызвать его Click, но так как меню уже будет уничтожено, то форма ничего не найдет.  На этом дело и заглохнет..


 
Leonid Troyanovsky ©   (2005-11-03 16:51) [31]


> Leonid Troyanovsky ©   (03.11.05 16:45) [29]

> Для этого упомянутый прообраз снабжается неким индексированным
> свойством (постой-ка, а почему это не TColumn.Visible?),
>  которое
> устанавливается посредством некоего action, назначаемоего


А.. да. Еще необходимо сделать обработчик клика на заголовке.
Создание симбиоза с меню, которое будет всплывать при
клике на оном - факультативно.
 Ну, и , конечно, свойство - ShowPopupMenuBelow(Above).

--
Regards, LVT.


 
Экспериментатор   (2005-11-05 12:03) [32]

Сделай в initialization модуля создание PopupMenu
в finalization - его Free
а в своих функциях используй его следующим образом:
сначала Очищай Items , потой его заполняй новыми пунктами и присваивай им обработчики, никаких Free после при вызова Popup нельзя делать.
Посмотри внимательно на procedure TPopupMenu.Popup(X, Y: Integer);
в нем вызывается
 TrackPopupMenu(FItems.Handle, AFlags, X, Y, 0 { reserved }, PopupList.Window, nil);
как ты думаешь для чего?


 
Экспериментатор   (2005-11-05 12:20) [33]

в догонку:
при создании Menu добавляется в PopupList
при уничтожении - удаляется,
И вот часть оконной процедуры объекта TPopupList

procedure TPopupList.WndProc(var Message: TMessage);
var
 I, Item: Integer;
 MenuItem: TMenuItem;
 FindKind: TFindItemKind;
 ContextID: Integer;
 Canvas: TCanvas;
 SaveIndex: Integer;
 DC: HDC;
begin
 case Message.Msg of
   WM_COMMAND:
     for I := 0 to Count - 1 do
       if TPopupMenu(Items[I]).DispatchCommand(Message.wParam) then Exit;
   WM_INITMENUPOPUP:
     for I := 0 to Count - 1 do
       with TWMInitMenuPopup(Message) do
         if TPopupMenu(Items[I]).DispatchPopup(MenuPopup) then Exit;
   WM_MENUSELECT:
     with TWMMenuSelect(Message) do
     begin
       FindKind := fkCommand;
       if MenuFlag and MF_POPUP <> 0 then FindKind := fkHandle;
       for I := 0 to Count - 1 do
       begin
         if FindKind = fkHandle then
         begin
           if Menu <> 0 then
             Item := GetSubMenu(Menu, IDItem) else
             Item := -1;
         end
         else
           Item := IDItem;
         MenuItem := TPopupMenu(Items[I]).FindItem(Item, FindKind);
         if MenuItem <> nil then
         begin
           Application.Hint := GetLongHint(MenuItem.Hint);
           Exit;
         end;
       end;
       Application.Hint := "";
     end;
и т.д.

Делай выводы



Страницы: 1 вся ветка

Текущий архив: 2005.12.04;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.117 c
4-1128321981
Alex870
2005-10-03 10:46
2005.12.04
Критическая служба


3-1129685269
antoxa2005
2005-10-19 05:27
2005.12.04
А можно ли сохранить запрос, как хранимую процедуру в БазеДанных


14-1131997607
genek84
2005-11-14 22:46
2005.12.04
Формирование сложного отчета в Фоксе 9.0


4-1128064513
nevalex
2005-09-30 11:15
2005.12.04
КАК МНЕ ОРГАНИЗОВАТЬ МНОГОПОТОЧНЫЙ ПОИСК


14-1132130543
Igorek
2005-11-16 11:42
2005.12.04
Ищу хороший задачник по сложным SQL запросам