Mega Code Archive

 
Categories / Delphi / Examples
 

Event Chain Mechanism II

Title: Event Chain Mechanism II Question: Attach and detach many event handlers to an event at runtime Answer: My last event chain mechanism doesn't include UnSaveEvent method. We use UnSafeEvent method for removing an event from event chain. EventId parameter is removed from event chain mechanism, so only event wrapper mechanism can be used. There aren't any changes on SaveEvent usage, here's the example: procedure TfrmEventChain.btnAttachEvent1Click(Sender: TObject); begin SaveEvent(btnTrigger.OnClick,Event1Click,btnTrigger); btnTrigger.OnClick:=Event1Click; end; There aren't any changes on ChainedEvent usage also, here's the example: procedure TfrmEventChain.Event1Click(Sender: TObject); begin ChainedEvent(Event1Click,TComponent(Sender)); chbEvent1.Checked:=not chbEvent1.Checked; end; I only add new usage of UnSaveEvent, here's the example: procedure TfrmEventChain.btnDetachEvent1Click(Sender: TObject); var NextEvent:TNotifyEvent; begin NextEvent:=UnSaveEvent(Event1Click,btnTrigger); if SameEvent(btnTrigger.OnClick,Event1Click) then btnTrigger.OnClick:=NextEvent; end; OK, here's the unit source code for TNotifyEvent (this unit and the demo project will be sent to delphi3000 admin immediately): unit EventChain; interface uses Classes, SysUtils; type ECircularEventChain = class(Exception); //EventId is removed, //use event handler wrapper to differentiate events of single component //that share one event handler procedure SaveEvent(OldEvent,NewEvent:TNotifyEvent;Sender:TComponent);overload; procedure ChainedEvent(NewEvent:TNotifyEvent;Sender:TComponent);overload function UnSaveEvent(Event:TNotifyEvent;Sender:TComponent):TNotifyEvent;overload function SameEvent(LeftEvent,RightEvent:TNotifyEvent):boolean;overload; var EventList:TStringList; //move declaration to implementation section after testing implementation type TEventListCleaner = class(TComponent) protected procedure Notification(AComponent: TComponent;Operation: TOperation);override; end; var EventListCleaner:TEventListCleaner; procedure SaveEvent(OldEvent,NewEvent:TNotifyEvent;Sender:TComponent); var EventName:string; EventString:string; i,u:integer; SenderString:string; begin if Assigned(OldEvent) and Assigned(NewEvent) and Assigned(Sender) and (@OldEvent@NewEvent) then begin EventString:=IntToStr(Integer(TMethod(NewEvent).Data))+'.'+ IntToStr(Integer(TMethod(NewEvent).Code)); u:=EventList.Count-1; SenderString:=IntToStr(Integer(Sender)); //check for circular event chain for i:=0 to u do begin EventName:=EventList.Names[i]; if (pos(SenderString,EventName)=1)and (EventList.Values[EventName]=EventString) then raise ECircularEventChain.Create('Circular event chain found!'); end; EventName:=SenderString+'.'+IntToStr(Integer(@NewEvent)); EventString:=IntToStr(Integer(TMethod(OldEvent).Data))+'.'+ IntToStr(Integer(TMethod(OldEvent).Code)); EventList.Values[EventName]:=EventString; Sender.FreeNotification(EventListCleaner); end; end; procedure ChainedEvent(NewEvent:TNotifyEvent;Sender:TComponent); var EventName:string; OldEvent:TNotifyEvent; EventString:string; Separator:integer; begin if Assigned(NewEvent) and Assigned(Sender) then begin EventName:=IntToStr(Integer(Sender))+'.'+IntToStr(Integer(@NewEvent)); EventString:=EventList.Values[EventName]; if (EventString'') then begin Separator:=pos('.',EventString); TMethod(OldEvent).Data:=Pointer(StrToInt(Copy(EventString,1,Separator-1))); TMethod(OldEvent).Code:=Pointer(StrToInt(Copy(EventString,Separator+1,length(EventString)-Separator)));; if Assigned(OldEvent) then OldEvent(Sender); end; end; end; function UnSaveEvent(Event:TNotifyEvent;Sender:TComponent):TNotifyEvent;overload var i,u,pstn,separator:integer; EventName,EventString,EventCodeString,SenderString,NextEventString:string; begin Result:=nil; if Assigned(Event) and Assigned(Sender) then begin NextEventString:=IntToStr(Integer(TMethod(Result).Data))+'.'+ IntToStr(Integer(TMethod(Result).Code)); EventCodeString:=IntToStr(Integer(TMethod(Event).Code)); EventString:=IntToStr(Integer(TMethod(Event).Data))+'.'+ EventCodeString; SenderString:=IntToStr(Integer(Sender)); u:=EventList.Count-1; pstn:=EventList.IndexOfName(SenderString+'.'+EventCodeString); for i:=u downto 0 do begin EventName:=EventList.Names[i]; if (pos(SenderString,EventName)=1)and(EventList.Values[EventName]=EventString)then begin //Next event found redirect next event if pstn=0 then NextEventString:=EventList.Values[SenderString+'.'+EventCodeString]; EventList.Values[EventName]:=NextEventString; end; end; if pstn=0 then begin NextEventString:=EventList.Values[SenderString+'.'+EventCodeString]; Separator:=pos('.',NextEventString); TMethod(Result).Data:=Pointer(StrToInt(Copy(NextEventString,1,Separator-1))); TMethod(Result).Code:=Pointer(StrToInt(Copy(NextEventString,Separator+1,length(NextEventString)-Separator)));; EventList.Delete(pstn); end; end; end; function SameEvent(LeftEvent,RightEvent:TNotifyEvent):boolean; begin with TMethod(LeftEvent) do Result:=(Data=TMethod(RightEvent).Data)and(Code=TMethod(RightEvent).Code); end; end.