Переопределение событий

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

Переопределение событий

Сообщение Dem » 12.02.2011 04:04:57

Доброго времени суток, Камрады!

Нужна Ваша помощь.

И вот, по какому вопросу:

Код: Выделить всё
mbutton = class(tbutton)
   private
   public
      constructor create(aowner:tcomponent); overload;
      destructor destroy; override;
      procedure onclick(sender:tobject); override;   
end;


Вот-этого события (onclick) в базовом классе не определено. Т.е. при сборке собс-но и сообщает, что: "not found in base class"...

Как повесить свое событие?


P.S. Прошу сильно не пинать - с таким вопросом столкнулся впервые. Ранее не было необходимости.
P.P.S. Вопрос, конечно, не в кнопке, но интересен сам механизм...

Заранее спасибо :)
Последний раз редактировалось Dem 12.02.2011 04:29:31, всего редактировалось 1 раз.
Dem
незнакомец
 
Сообщения: 8
Зарегистрирован: 30.08.2010 16:28:06

Re: Переопределение событий

Сообщение Иван Шихалев » 12.02.2011 04:11:02

А какова цель всего этого?

И что подразумевается под:
constructor create(aowner:tcomponent); overload;

?

Добавлено спустя 1 минуту 40 секунд:
Да, еще — вышеприведенный кусок кода не должен выдавать "not found in base class" на onclick...
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Re: Переопределение событий

Сообщение Dem » 12.02.2011 04:26:35

А какова цель всего этого?


Целей несколько (вернее - в нескольких классах).

Более-предметно:

(обертка для локального прокси)

Код: Выделить всё
TLproxy =class(TIDMappedPortTCP)
   private
      _defaultPort:integer;
   public
      constructor create(Aowner:Tcomponent; proxy:string; _defaultPort:string); overload;
      destructor destroy; override;
      function getDefaultPort:integer;
      //property onConnect;
      {ошибка тут} procedure onConnect; override;
end;

{...}

constructor TLproxy.create(Aowner:Tcomponent; proxy:string; _defaultPort:string);
begin
   inherited create(aowner);
   MappedHost:=copy(proxy,1,length(proxy)-(length(proxy)-pos(':',proxy))-1);
   MappedPort:=strtoint(trim(copy(proxy,pos(':',proxy)+1,length(proxy)-pos(':',proxy))));
   DefaultPort:=_defaultPort;
   Active:=true;
end;

{...}

procedure TLproxy.onConnect(acontext: TidContext);
begin
  //inherited;
  if acontext.Connection.Socket.Binding.PeerIP<>'127.0.0.1'
  then acontext.Connection.Disconnect;
end;



Про кнопку для простоты спросил, поскольку думаю, что механизм одинаков...

Добавлено спустя 4 минуты:
Да, еще — вышеприведенный кусок кода не должен выдавать "not found in base class" на onclick...


Поправил код.
Dem
незнакомец
 
Сообщения: 8
Зарегистрирован: 30.08.2010 16:28:06

Re: Переопределение событий

Сообщение Maxizar » 12.02.2011 10:24:46

Если я вас правильно понял, вам нужно в вашем классе создать событие, которое будет видно в инспекторе объектов, скажем как OnClick для кнопки. И иметь возможность перекрывать его, то делать нужно так:
Событие –это функция или процедура объявленная с дополнительным идентификатором или как там его называют of object;.
Пример:
Допустим, ваш обработчик OnRun1 имеет один параметр типа Integer, и OnRun2 имеет один параметр типа Integer и возвращает результат типа Boolean.
То необходимо объявить два типа данных событий:
Код: Выделить всё
type
  TRun1 = procedure(I: Integer) of object;
   TRun2 = function(I: Integer):Boolean of object;


Теперь объявите ваш класс:
Код: Выделить всё
type
TSomeClass = class()
private
FOnRun1: TRun1;
FOnRun2: TRun2;
Protected

procedure DoRun1 (I: Integer); override;
function DoRun2 (I: Integer):Boolean; override;


public

property OnOnRun1: TOnRun1 read FOnOnRun1
write FOnOnRun1;
property OnOnRun2: TOnRun2 read FOnOnRun2
write FOnOnRun2;

end;


Метод DoRun1:

Код: Выделить всё
procedure TSomeClass.DoRun1 (I: Integer);
begin
  if Assigned(FOnRun1) then
    FOnRun1(I);
end;


Метод DoRun2:
Код: Выделить всё
procedure TSomeClass.DoRun2 (I: Integer) : Boolean;
begin
  if Assigned(FOnRun2) then
Result :=   FOnRun2(I);
end;


Не забываем о области видимости процедур DoRun1 и DoRun2, в большинстве случаев это protected.
Если их нужно будет перекрывать то не забываем про override;

Скажем нужно будет перекрыть в наследнике делаем так:
Код: Выделить всё
type
TSomeClass2 = class(TSomeClass)
Protected
FI:Integer;

procedure DoRun1 (I: Integer); override;

end;


Метод DoRun1:

Код: Выделить всё
procedure TSomeClass2.DoRun1 (I: Integer);
begin
 
    inherited DoRun1(I); //Вызываем метод родителя
  //далее делаем дополнительные операции
FI:=FI+I;
end;

необходимо помнить о том как нужно вызывать метод родителя до или после нужных нам операций, зависит от действий и сложности событий

Писал в блокноте, так что возможны опечатки..
Дополнительные источники:
1- Как создать свое событие для своего класса
2 -Делегирование события
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: Переопределение событий

Сообщение Odyssey » 12.02.2011 13:03:16

Dem писал(а):Вот-этого события (onclick) в базовом классе не определено. Т.е. при сборке собс-но и сообщает, что: "not found in base class"...
Как повесить свое событие?

Если под этим имелось в виду "как выполнить мой код при щелчке на моей кнопке", то
Код: Выделить всё
procedure Click; override;

В конце кода лучше вызвать inherited Click;, чтобы выполнить ещё и обработчик родительского компонента.
Строго говоря, это не мы не вешали событие, а перекрывали метод, который вызывается при щелчке мышью по компоненту.

Можно, конечно, "повесить событие" в буквальном смысле: завести в классе mbutton
Код: Выделить всё
procedure MyBtnClick(Sender: TObject);

а в конструктор добавить
Код: Выделить всё
Self.OnClick := @MyBtnClick.

Но у этого способа есть огромный недостаток: если тот, кто будет пользоваться классом mbutton переназначит событие OnClick в своём коде, то MyBtnClick больше не будет вызываться. Поэтому правило обычно таково: если мы пишем свой класс, то перекрываем (override) родительские методы, а если используем уже написанный класс, то вешаем события.

P.S.
Maxizar писал(а):Если я вас правильно понял, ...
Odyssey писал(а):Если под этим имелось в виду ...
Вот чем мне нравится этот форум. А на некоторых они постоянно в отпуске :)
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Переопределение событий

Сообщение hinst » 12.02.2011 19:15:53

короче :shock:
:idea: OnClick - это свойство класса, а не метод, поэтому его нельзя перекрыть
:idea: перекрыть надо Click, как уже правильно сказали
:idea: inherited не забывайте.
:idea: и не путаете ли вы override <перекрыть> и overload <перегрузить>
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Переопределение событий

Сообщение Dem » 15.02.2011 02:44:32

Всем спасибо - информация была полезной/любопытной... отчасти - известной...

Спрошу более-конкретно:

Базовый класс (предок)
Код: Выделить всё
TWebBrowser = class(TOleControl)
   {...}
   private
      FOnBeforeNavigate2: TWebBrowserBeforeNavigate2;
   {...}
   published
      property OnBeforeNavigate2: TWebBrowserBeforeNavigate2 read FOnBeforeNavigate2 write FOnBeforeNavigate2;
   {...}
end;   


В предке нет процедур
BrowserBeforeNavigate2,
TWebBrowserBeforeNavigate2,
BeforeNavigate и т.п.

Как мне получить доступ к событию "OnBeforeNavigate2"? (перекрыть)

Odyssey писал(а):Но у этого способа есть огромный недостаток: если тот, кто будет пользоваться классом mbutton переназначит событие OnClick в своём коде, то MyBtnClick больше не будет вызываться. Поэтому правило обычно таково: если мы пишем свой класс, то перекрываем (override) родительские методы, а если используем уже написанный класс, то вешаем события.

Как повесить событие - это понятно. А как его перекрыть?


P.S. - с простотой вопроса обманул сам-себя...
P.S.2. - все классы пишутся в редакторе и создаются исключительно в Runtime, и какие из их свойств попадают в визуальный редактор - не имею ни малейшего понятия...
Dem
незнакомец
 
Сообщения: 8
Зарегистрирован: 30.08.2010 16:28:06

Re: Переопределение событий

Сообщение daesher » 15.02.2011 08:21:10

Это сложнее (впрочем, можно говорить не просто о событиях, а вообще о свойствах, которыми формально события и являются). Можно:
1. Изменить видимость свойства (прописать его в Public, Private или Protected)
2. Создать такое же свойство с тем же именем. Старое никуда не денется, и при использовании потомка через переменную предка пойдёт обращение к старому свойству.
3. Смириться с тем свойством, которое уже есть, и использовать его (благо, в нём уже есть всё, что надо).
4. Пропатчить код для предка, повесив свойство не на поле, к тому же приватное, а на методы Set... и Get... (пусть они меняют только это поле), которые должны быть виртуальными и видимыми не менее чем protected, а потом перекрывать именно их.

Возможно, Вы хотели изменить тип свойства - тогда остаётся только вариант создания свойства с таким же именем.
Возможно, Вы хотели повесить на это событие что-то дополнительное (т.е., чтобы в потомке делалось что-то ещё, когда оно вызывалось) - это - более сложная задача; необходимо выяснить, когда предок создаёт событие, и попробовать "влезть" туда; или же, опять же, намудрить с Set... и Get... (можно просто с Get...), сохраняя метод, который вешают на событие, и подсовывая вместо него свой метод, который сделает то, что надо, а потом вызовет обычный повешенный на событие метод. Но, опять же, для этого варианта нужно патчить код предка.
daesher
постоялец
 
Сообщения: 221
Зарегистрирован: 09.03.2010 22:17:14


Вернуться в Обучение Free Pascal

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Рейтинг@Mail.ru