Коллеги!
Извините за тупой вопрос...
Пробую назначать обработчик событий какому-либо компоненту. В случае нотации
Button.OnClick:= Control_OnClick;
...
procedure Control_OnClick(Sender: TObject);
begin
//код процедуры
end;
выводится сообщение компилятора "Incompatible types: method pointer and regular procedure"
Если же процедура описана как метод формы
Button.OnClick:= Control_OnClick;
...
procedure TForm1.Control_OnClick(Sender: TObject);
begin
//код процедуры
end;
то все получается. Как можно назначить событию произвольный обработчик, в т.ч. из другого Unit'а?
Почему спрашиваю: хочу перенести часть кода обработчиков в отдельный модуль, чтобы не мозолил глаза.
Спасибо за ответы.
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
26-08-2009 06:37 | Сообщение от автора вопроса
Коллеги, всем спасибо за ответы, особенно 777, Александру Алексееву и, конечно, неподражаемому Geo, весьма терпеливо пытавшемуся прочитать мои мысли.
Сделал, в общем, так, как советовал 777: создал класс, а его методами сделал те самые обработчики событий, после чего все начало присваиваться, как положено.
Для чего это нужно: делаю проект, в котором реализованы скрипты, реагирующие на события. При этом события назначаются так:
а уж затем в коде скрипта описывается поведение контрола на скриптовом языке.
Поэтому, когда контролов много и они разного типа, назначаемые событиям обработчики серьезно засоряют текст модуля и ухудшают читаемость.
Ну, я (как обычно) вылезу с перпендикулярным поворотом темы обсуждения. А зачем, собственно, нужно так делать?
Вот автор говорит, что "хочу перенести часть кода обработчиков в отдельный модуль, чтобы не мозолил глаза". А почему они мозолят глаза?
Первое, что мне приходит в голову (из опыта общения на Круглом Столе), автор пытается реализовать сложное поведение какого-то контрола. Ну, что-то вроде, на PaintBox что-то сложное рисуется, каким-то образом реагирует на нажатия мышки и т.п. В результате, модуль с формой оказывается загажен обработчиками событий, реализующих специфику поведения отдельного контрола, а не самой формы (как это должно быть). В этом случае имеет смысл подумать не о переносе обработчиков в другой юнит, а о включении этих обработчиков в сам класс. То есть, взять имеющийся контрол, вывести из него потомка, а вэтот потомок включить реализацию того самог специфического поведения. Звучит для человека неподготовленного страшновато, но на деле не так страшен черт, как его малюют ;-)
Второй вариант, который ямогу домыслить, заключается не то, чтобы в мозолинии глаз, а в том, что разные формы одинаково выполняют обработку каких-либо событий (например, куча диалогов со сходными принципами обработки). Как это ни странно, но тут тоже можно подумать о том, чтобы вывести свой базовый класс, от которого будут наследоваться однотипные окна, включив общую логику обработки внутрь класса. А можно пойти по пути разработчиков VCL, которые создали специальную функцию, создающую и показывающую диалоговое окно (см. юнит Dialogs). Можно поступить так же, но сохранив возможности визуального проектирования: создать визуально базовую форму для диалогового окна, а классу этой формы дописать методы, которые по переданным значениям будут видоизменять эту форму (типа, тексты заполнять, кэпшно подписывать, иконку менять и т.п.).
Если все эти обработчики связаны некоторым общим содержательным смыслом, красиво выглядит решение с созданием специально класса, где все обработчики являются классовыми методами (в стиле классов Exception).
Ну, и последнее. Может так оказаться, что требуемые обработчики действительно реализуют логику работы формы (как это и должно быть), но они слишком громоздкие, плюс возможны какие-то пересечения (какие-то обработчики могут использоваться в разных формах). В этом случае считаю более правильным вариант, предложенный Сергеем Дупликом: процедуры выносите в отдельный юнит и вызывайте их из стандартных обработчиков событий. Это поможет Вам легче читать логику работы формы: вы по бработчикам сразу видите, что обрабатывается, а заглянув в реализацию понимаете, какая внешняя функция для обработки используется.
P.S. Во всех случаях нестоятельно рекомендуется грамотно продумывать имена (то есть никаких TDummy). Это существенно повысит читаемость кода.
P.P.S. Если уточните, что именно Вы хотите сделать, то можете получить более короткий и конкретный вариант ответа, вместо расплывчатого эссе обо всем на свете ;-)
777:
>>> Сделайте их методами прямого потомка TObject, описанного в том отдельном модуле.
Присоединяюсь...
>>> Если не ошибаюсь, то их даже можно описать как классовые методы (class procedure), и тогда не понадобится создавать экземпляр этого класса (если обращения к его полям не требуется).
Exelord:
В этом случае класс создавать не придётся:
procedure TForm1.FormCreate(Sender: TObject);
var m:TMyMethod;
begin
form1.OnClick:=m.Click;
end;
Я обычно поступаю так:
procedure TForm1.FormCreate(Sender: TObject);
begin
OnClick := TКлассОбработчика(nil).Click;
end;
В этом случае в качестве Self просто будет передан nil. Это хорошо ещё тем, что при попытке обращения к Self в том обработчике сразу же получим исключение (и т.о. обнаружим ошибку в коде).
Советую автору прочитать мой комментарий по приведённой ссылке (а также другие комментарии и саму статью, но очень критически). Я там привёл пример назначения всем TEdit, расположенным непосредственно на форме, обработчиков, благодаря которым каждый TEdit подсвечивается при входе в него и восстанавливает обычный цвет при выходе. Рекомендую также посмотреть ссылки: »вопрос КС №62879« »вопрос КС №55213«
Как можно назначить событию произвольный обработчик, в т.ч. из другого Unit'а?
Почему спрашиваю: хочу перенести часть кода обработчиков в отдельный модуль, чтобы не мозолил глаза.
Сделайте их методами прямого потомка TObject, описанного в том отдельном модуле. Если не ошибаюсь, то их даже можно описать как классовые методы (class procedure), и тогда не понадобится создавать экземпляр этого класса (если обращения к его полям не требуется).
Button.OnClick - TNotifyEvent, а
TNotifyEvent = procedure(Sender: TObject) of object;
Т.е. вы можете сделать так:
TMyMethod = class
Click: procedure(Sender:TObject);
end;
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.