У меня есть приложение с бесконечным (многим) количеством кадров, которые динамически добавляются и удаляются. Я никогда не знаю, какие из них активны, а какие неактивны.
У всех из них есть такая процедура:
procedure DoAfterPermissions();
begin
// Code to run after permissions have been refreshed
end;
В моей основной форме я время от времени обновляю разрешения. После обновления разрешений мне нужно, чтобы эта процедура выполнялась для всех моих созданных кадров.
Как это может быть сделано? Я даже не знаю, с чего начать.
Обновлено: это приложение FMX, работающее на Windows, Android и iOS.
Почему бы не использовать шаблон наблюдателя с System.Classes.TComponent.Observers, который TFrame уже предоставляется как свойство. Хотя класс TObservers был добавлен в Delphi для обеспечения функционирования LiveBindings, его также можно использовать без использования LiveBindings.





Я бы предложил две вещи:
Пусть все классы фреймов наследуются от общего базового класса (или реализуют общий интерфейс), который определяет метод DoAfterPermissions(), а затем все фреймы переопределяют его.
Сохраните все экземпляры фреймов в списке, типом элемента которого является указанный выше базовый класс/интерфейс.
Таким образом, вы можете просто перебирать список, когда это необходимо, вызывая метод непосредственно в каждом кадре.
Например:
type
TMyFrameBase = class(TFrame)
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure DoAfterPermissions; virtual; abstract;
end;
var
MyFrames: TList<TMyFrameBase>;
...
constructor TMyFrameBase.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
MyFrames.Add(Self);
end;
destructor TMyFrameBase.Destroy;
begin
MyFrames.Remove(Self);
inherited Destroy;
end;
initialization
MyFrames := TList<TMyFrameBase>;
finalization
MyFrames.Free;
type
TMyFrame = class(TMyFrameBase)
public
procedure DoAfterPermissions; override;
end;
procedure TMyFrame.DoAfterPermissions;
begin
// do something...
end;
RefreshPermissions;
for I := 0 to MyFrames.Count-1 do
MyFrames[I].DoAfterPermissions;
Спасибо за ответ. Не могли бы вы привести пример короткого кода для пункта номер 2?
Я добавил пример
Вы можете использовать событие многоадресной рассылки.
Если ваше приложение основано на Windows, вы можете изучить возможность использования широковещательной рассылки настраиваемых сообщений и реализации обработчиков сообщений там, где это необходимо.
В качестве альтернативы или если вам нужно получать уведомления об объектах, которые не способны принимать сообщения, вы можете рассмотреть событие многоадресной рассылки (один отправитель, 0..N получателей).
Я не знаю, добавила ли Delphi поддержку многоадресных событий в последние годы; если нет, вы можете использовать реализацию, которую я собрал несколько лет назад.
Это не тестировалось с последними версиями Delphi, но не использует никаких экзотических методов и все равно должно работать. Если нет, не стесняйтесь изменить его, чтобы исправить любые проблемы (если да, дайте мне знать?). :)
В README упоминается «duget», инструмент управления зависимостями, который я также реализовал и созданный по образцу nuget. Это не было опубликовано, так как я так и не смог заставить его работать с частными репозиториями файловой системы, прежде чем отойти от Delphi. :(
Но в случае с пакетом deltics.multicast это не так уж и важно; единственная зависимость проекта — это проект deltics.inc, который предоставляет версию Delphi и возможности компилятора $DEFINE в файле .inc; вы также можете взять это с github.
Это эффективно обеспечивает реализацию подхода, описанного Реми, используя события Delphi для доставки уведомлений, а не полагаясь на определенную иерархию классов (виртуальные методы) или использование интерфейсов.
Многоадресную версию TNotifyEvent предоставляет TMulticastNotify.
В каком-нибудь известном синглтоне вы должны предоставить экземпляр этого (источник события). Каждый кадр (и все остальное, что хочет реагировать на событие) будет реализовывать обработчик TNotifyEvent и регистрировать его в источнике события.
Если получатели эфемерны (то есть они могут быть уничтожены после регистрации событий), им следует явно удалить свой обработчик из источника события в своем деструкторе (самый простой подход) или инкапсулировать ссылку на событие TOnDestroy (также часть deltics.multicast код).
В вашем случае подход TOnDestroy кажется излишне сложным; это задокументировано в коде, если вам интересно.
Простая иллюстрация:
TPermissions = class(TComponent)
private
fOnChanged: TMulticastNotify;
procedure DoChanged;
public
constructor Create(aOwner: TObject); override;
property OnChanged: TMulticastNotify read fOnChanged;
end;
var Permissions: TPermissions; // unit scoped singleton, initialised somewhere as required
// ----
constructor TPermissions.Create(aOwner: TObject);
begin
inherited Create(aOwner);
fOnChanged := TMulticastNotify.Create(self);
end;
procedure TPermissions.DoChanged;
begin
fOnChanged.DoEvent;
end;
Обратите внимание, что событию OnChanged нужен только аксессор read, поскольку он не изменяется при добавлении получателя; это объект, который управляет списком получателей с помощью открытых методов (Add/Remove).
Затем вы можете реализовать класс для получения события многоадресной рассылки и вызова соответствующего события одноадресной рассылки (если оно назначено). Затем экземпляр этого класса можно скомпоновать внутри фрейма или любого другого класса, который необходимо уведомлять об изменениях разрешений (или вы можете просто реализовать аналогичный код для каждого класса, который хочет подписаться на события изменения разрешений):
TPermissionsChanged = class
private
fOnPermissionsChanged: TNotifyEvent;
procedure DoPermissionsChanged(aSender: TObject);
public
constructor Create; override;
destructor Destroy; override;
property OnPermissionsChanged: TNotifyEvent read fOnPermissionsChanged write fOnPermissionsChanged;
end;
// ---
constructor TPermissionsChanged.Create;
begin
inherited Create;
// 'Permissions' is the well-known (i.e. public) singleton instance of TPermissions
Permissions.OnChanged.Add(self.DoPermissionsChanged)
end;
destructor TPermissionsChanged.Destroy;
begin
Permissions.OnChanged.Remove(self.DoPermissionsChanged);
inherited;
end;
procedure TPermissionsChanged.DoPermissionsChanged(aSender: TObject);
begin
if Assigned(fOnPermissionsChanged) then
fOnPermissionsChanged(aSender);
end;
У Delphi есть собственная система обмена сообщениями System.Messaging (по крайней мере, начиная с XE6), предназначенная, как я полагаю, для FMX, который не может полагаться на сообщения Windows, но она не ограничивается FMX. См. Отправка и получение сообщений с использованием RTL.
Спасибо @PhilipJ.Rayment, выглядит как (очень простая) версия MediatR (из .net). Кому-то это может быть полезно, хотя, ИМХО, менее элегантно. ymmv
Идеальный кандидат для System.Messaging.