Icon View Thread

The following is the text of the current message along with any replies.
Messages 1 to 7 of 7 total
Thread Inter-Component Communication
Thu, Jun 8 2017 3:52 AMPermanent Link

Mark Brooks

Slikware

Avatar

Has anybody found a neat way of "messaging" between component instances? For example, if I have a number of TXYZ component instances, is it possible for one to broadcast a message to the others, perhaps indicating its state change or similar?

I can do this in some cases, when all of the TXYZ instances are owned by a common parent, just by iterating the parent children, although it's a bit messy.

I know that Tim has mentioned the existing component messaging mechanism previously, but looking at the source, it's not clear to me exactly how that works and how I might (safely) utilise it for my own purposes.

Thanks
Mark
Thu, Jun 8 2017 7:42 AMPermanent Link

Matthew Jones

Mark Brooks wrote:

> Has anybody found a neat way of "messaging" between component instances? For example, if I have a number of TXYZ component instances, is it possible for one to broadcast a message to the others, perhaps indicating its state change or similar?
>
> I can do this in some cases, when all of the TXYZ instances are owned by a common parent, just by iterating the parent children, although it's a bit messy.
>
> I know that Tim has mentioned the existing component messaging mechanism previously, but looking at the source, it's not clear to me exactly how that works and how I might (safely) utilise it for my own purposes.

Indeed, I looked at the standard mechanism, and decided it wasn't what I wanted. But it showed what was possible, and I quickly wrote my own. It has been so liberating not having to go around trees of data to send a notification. Plus you can do it async if you don't need it asap.

I've included the full unit below. The consts are the events I wanted to broadcast - change as appropriate. Any object can register for any event ID (and unregister).


   g_xNotifications.NotifyRegister(EVENT_ITEM_HEADER_SIZED, OnHeaderResize);


function TfrmOutline.OnHeaderResize(Sender: TObject; nEventID: Integer; xData: TObject): Boolean;
var
   nLoop : Integer;
begin
   result := true; // carry on processing
   ...
end;

g_xNotifications.NotifyListenersAsync(self, EVENT_ITEM_HEADER_SIZED, nil);


--

Matthew Jones



unit uNotifications;

interface

uses WebCore;

{$DEFINE xDEBUG_NOTIFICATIONS}

const
   EVENT_RESOURCE_HEADER_SIZED = 1;   
   EVENT_RESOURCE_HEADER_SIZED_MAX = 5;                
   EVENT_CLEAR_MOUSE_OVER_MAIN = 6;
   EVENT_CLEAR_MOUSE_OVER_RESOURCE = 7;
   EVENT_ITEM_HEADER_SIZED = 8;        
   EVENT_ITEM_DETAILS_CHANGE = 9;

type
     
TNotificationEvent = function (Sender: TObject; nEventID: Integer; xData: TObject): Boolean of object;

TNotifications = class
private
   m_axEvents : array of TNotificationEvent;
   m_anIDs : array of Integer;
   m_nEventMax : Integer;

public
   constructor Create; override;

   function NotifyRegister(nEventID : Integer; xEvent : TNotificationEvent) : Integer;
   procedure NotifyUnregister(nRegisterHandle : Integer);
   procedure NotifyListeners(Sender: TObject; nEventID: Integer; xData: TObject);
   procedure NotifyListenersAsync(Sender: TObject; nEventID: Integer; xData: TObject);
end;

var
   g_xNotifications : TNotifications;

implementation                        

uses uDebugReport;

const
   ALLOCATE_BUMP = 6;

constructor TNotifications.Create;
var
   nLoop : Integer;
begin
   inherited Create;

   m_nEventMax := ALLOCATE_BUMP;

   SetLength(m_axEvents, m_nEventMax);
   SetLength(m_anIDs, m_nEventMax);
   for nLoop := 0 to m_nEventMax - 1 do
   begin
       m_anIDs[nLoop] := -1;
   end;
end;

function TNotifications.NotifyRegister(nEventID : Integer; xEvent : TNotificationEvent) : Integer;
var
   nLoop : Integer;
   nOldMax : Integer;
begin
{$IFDEF DEBUG_NOTIFICATIONS}
DebugReport('NotifyRegister ' + IntToStr(nEventID));
{$ENDIF}
   for nLoop := 0 to m_nEventMax - 1 do
   begin
       if m_anIDs[nLoop] = -1 then
       begin            
           m_anIDs[nLoop] := nEventID;
           m_axEvents[nLoop] := xEvent;
           Result := nLoop;
{$IFDEF DEBUG_NOTIFICATIONS}
DebugReport('NotifyRegister ' + IntToStr(nEventID) + ' at ' + IntToStr(nLoop));
{$ENDIF}
           exit;
       end;
   end;

   // not enough room, so make more

   nOldMax := m_nEventMax;
   m_nEventMax := m_nEventMax + ALLOCATE_BUMP;
{$IFDEF DEBUG_NOTIFICATIONS}
DebugReport('Adding more slots ' + IntToStr(nOldMax) + ' to ' + IntToStr(m_nEventMax));
{$ENDIF}
   SetLength(m_axEvents, m_nEventMax);
   SetLength(m_anIDs, m_nEventMax);
   for nLoop := nOldMax to m_nEventMax - 1 do
   begin
       m_anIDs[nLoop] := -1;
   end;
   Result := NotifyRegister(nEventID, xEvent);
end;

procedure TNotifications.NotifyUnregister(nRegisterHandle : Integer);
begin           
   if (nRegisterHandle >= 0) and (nRegisterHandle < m_nEventMax) then
   begin
       m_anIDs[nRegisterHandle] := -1;
       m_axEvents[nRegisterHandle] := nil;
   end;
end;

procedure TNotifications.NotifyListeners(Sender: TObject; nEventID: Integer; xData: TObject);
var
   nLoop : Integer;
begin                                                                       
   for nLoop := 0 to m_nEventMax - 1 do
   begin
{$IFDEF DEBUG_NOTIFICATIONS}
DebugReport('NotifyListeners ' + IntToStr(nEventID) + ' [' + IntToStr(nLoop) + '] = ' + IntToStr(m_anIDs[nLoop]));
{$ENDIF}
       if m_anIDs[nLoop] = nEventID then
       begin            
           if not m_axEvents[nLoop](Sender, nEventID, xData) then  // return false to stop further processing
           begin
{$IFDEF DEBUG_NOTIFICATIONS}
DebugReport('NotifyListeners stop processing');
{$ENDIF}
               exit;
           end;
       end;
   end;
end;

procedure TNotifications.NotifyListenersAsync(Sender: TObject; nEventID: Integer; xData: TObject);
begin
   async NotifyListeners(Sender, nEventID, xData);
end;


initialization

   g_xNotifications := TNotifications.Create;


end.
Thu, Jun 8 2017 9:20 AMPermanent Link

Mark Brooks

Slikware

Avatar

Appreciated Mathew - will review as "bedtime reading" !
Wed, Jun 14 2017 1:27 PMPermanent Link

Tim Young [Elevate Software]

Elevate Software, Inc.

Avatar

Email timyoung@elevatesoft.com

Mark,

<, I know that Tim has mentioned the existing component messaging mechanism previously, but looking at the source, it's not clear to me exactly how that works and how I might (safely) utilise it for my own purposes. >>

Everything you need is in the WebCore unit for the TComponent class.  The key methods are:

     protected
...   
        function Notification(Sender: TComponent; ID: Integer; Data: TObject): Boolean; virtual;
        procedure Notify(ID: Integer; Data: TObject=nil); virtual;
...
     public
...
        procedure Register(Component: TComponent);
        procedure UnRegister(Component: TComponent);
...

What you do is use the Register method to register a component for event notifications, and UnRegister to unregister.  

When a component is registered for event notifications, it will receive such notifications via the Notification method, which you can override to handle the notification.  Returning True from the notification method will stop a component from notifying any other subsequent components (even if they are registered for notifications), whereas returning False will allow the notifications to proceed.

When you want to notify all registered components of an event, you can use the Notify method.  Just be sure to use the cmUser identifier (defined in the WebCore unit as 1000) as the base for your event IDs.  That will keep you away from any internal EWB event IDs.

Tim Young
Elevate Software
www.elevatesoft.com
Wed, Jun 14 2017 4:25 PMPermanent Link

Mark Brooks

Slikware

Avatar

Awesome Tim. I'll give that a spin for sure. Thanks.
Thu, Jun 15 2017 5:38 AMPermanent Link

Matthew Jones

Tim Young [Elevate Software] wrote:

> Everything you need is in the WebCore unit for the TComponent class.

I remember now why I chose to roll my own. Yours is great for when you know the components, and very effective. In my situation, part of the problem I had was that the sender didn't have a clue how to find the interested recipient. Typically some sort of parent, but not directly, and complicated to go find. As you know, from my fun with unit ordering. So my version was a central point where you could send a message and not care how many recipients etc there are, and no need to override functions, just provide event handlers. The built-in system could probably work, but the nice thing with Web Builder is you can make these systems and use them as required. Not constrained in any way.

--

Matthew Jones
Mon, Jun 19 2017 1:52 PMPermanent Link

Tim Young [Elevate Software]

Elevate Software, Inc.

Avatar

Email timyoung@elevatesoft.com

Matthew,

<< I remember now why I chose to roll my own. Yours is great for when you know the components, and very effective. In my situation, part of the problem I had was that the sender didn't have a clue how to find the interested recipient. >>

Just to clarify: the above code does not require the sender to know anything about the recipient other than the fact that the recipient registered to receive notifications.  The recipient is responsible for filtering the notifications, based upon their needs.

<< and no need to override functions, just provide event handlers. >>

This can, of course, be easily added to a descendant class, if you need such functionality.

Tim Young
Elevate Software
www.elevatesoft.com
Image