Delphi 利用接口实现frame窗体间的通讯(互动)

需求说明:

image

 程序设计:

image

效果演示:

test

设计思路:

f9dfe0ffbfd092f0f7bbace85f7c1ddb

 FrmCK 只负责发布事件,不关心谁在监听.

FrmGrid 只负责响应事件,不关心事件来源.

创建过程:

一.创建接口单元FrmInterface.然后在单元中需要声明两个接口,即监听事件的接口和发布事件的接口

image

  1.监听接口只需要声明一个过程即可,它接收发布事件传过来的参数(如果有的话) 

  2.发布接口需要声明注册和销毁过程,参数是一个监听器.此外还要有一个广播过程,把需要的参数传播给监听接口

 全部代码如下:

unit FrmInterface;

interface

uses
  system.SysUtils;

type
  // 监听器接口 - 用于接收仓库选择事件的组件需要实现此接口
  IWarehouseSelectionListener = interface
    ['{C2228C39-FA0D-448D-B905-43CD35C6EC0A}']  // GUID,用于接口标识
    // 当仓库被选择时调用的方法
    procedure OnWarehouseSelected(const AWarehouseID: Integer; const AWarehouseCode: string);
  end;

  // 发布者接口 - 负责管理监听器并发布仓库选择事件
  IWarehouseSelectionPublisher = interface
    ['{F2A92608-BDB0-4324-9FAA-539D4E237555}']  // GUID,用于接口标识
    // 注册监听器
    procedure RegisterListener(AListener: IWarehouseSelectionListener);
    // 取消注册监听器
    procedure UnregisterListener(AListener: IWarehouseSelectionListener);
    // 通知所有监听器仓库选择事件
    procedure NotifyWarehouseSelection(AWarehouseID: Integer; const AWarehouseCode: string);
  end;

  // 实现发布者接口的类 - 具体的事件发布者实现
  TWarehouseSelectionPublisher = class(TInterfacedObject, IWarehouseSelectionPublisher)
  private
    FListeners: TArray<IWarehouseSelectionListener>;  // 存储所有注册的监听器
  public
    // 实现接口方法
    procedure RegisterListener(AListener: IWarehouseSelectionListener);
    procedure UnregisterListener(AListener: IWarehouseSelectionListener);
    procedure NotifyWarehouseSelection(AWarehouseID: Integer; const AWarehouseCode: string);
    destructor Destroy; override;  // 析构函数,清理资源
  end;

// 全局发布者实例函数 - 提供单例访问点
function WarehousePublisher: IWarehouseSelectionPublisher;

implementation

var
  FGlobalPublisher: IWarehouseSelectionPublisher;  // 全局唯一的发布者实例

// 获取全局发布者实例的函数
function WarehousePublisher: IWarehouseSelectionPublisher;
begin
  // 如果全局发布者尚未创建,则创建新实例
  if not Assigned(FGlobalPublisher) then
    FGlobalPublisher := TWarehouseSelectionPublisher.Create;
  Result := FGlobalPublisher;  // 返回全局发布者实例
end;

{ TWarehouseSelectionPublisher }

// 注册监听器方法
procedure TWarehouseSelectionPublisher.RegisterListener(AListener: IWarehouseSelectionListener);
begin
  // 检查监听器是否已经注册,避免重复注册
  for var i := 0 to High(FListeners) do
    if FListeners[i] = AListener then
      Exit;  // 如果已存在,直接退出

  // 将监听器添加到监听器数组中
  SetLength(FListeners, Length(FListeners) + 1);  // 扩展数组长度
  FListeners[High(FListeners)] := AListener;  // 将新监听器放在数组末尾
end;

// 取消注册监听器方法
procedure TWarehouseSelectionPublisher.UnregisterListener(AListener: IWarehouseSelectionListener);
begin
  // 遍历所有监听器,查找要移除的监听器
  for var i := 0 to High(FListeners) do
  begin
    if FListeners[i] = AListener then
    begin
      // 找到要移除的监听器,通过数组移位来移除
      for var j := i to High(FListeners) - 1 do
        FListeners[j] := FListeners[j + 1];  // 将后续元素前移
      SetLength(FListeners, Length(FListeners) - 1);  // 缩减数组长度
      Break;  // 找到并移除后立即退出循环
    end;
  end;
end;

// 通知所有监听器仓库选择事件
procedure TWarehouseSelectionPublisher.NotifyWarehouseSelection(AWarehouseID: Integer; const AWarehouseCode: string);
begin
  // 遍历所有注册的监听器,逐一通知
  for var i := 0 to High(FListeners) do
  begin
    try
      // 调用监听器的仓库选择处理方法
      FListeners[i].OnWarehouseSelected(AWarehouseID, AWarehouseCode);
    except
      on E: Exception do
        // 捕获并处理异常,确保一个监听器的异常不会影响其他监听器的通知
        // 这里可以记录日志,但保持注释状态以避免干扰用户界面
        // showmessage(PChar('通知监听器时发生错误: ' + E.Message));
    end;
  end;
end;

// 析构函数
destructor TWarehouseSelectionPublisher.Destroy;
begin
  // 清理监听器数组,释放资源
  SetLength(FListeners, 0);  // 将数组长度设为0,释放内存
  inherited;  // 调用父类析构函数
end;

initialization
  // 单元初始化部分,确保全局发布者初始为nil
  FGlobalPublisher := nil;

finalization
  // 单元结束部分,清理全局发布者实例
  FGlobalPublisher := nil;  // 释放全局发布者引用

end.

 

二.发布者单元FrmCK

use
...,FrmInterface;  //引入接口单元

type
  TCK = class(TFrame, IWarehouseSelectionPublisher)  //调用发布者接口
...
procedure cxDBTreeList1FocusedNodeChanged(Sender: TcxCustomTreeList;
      APrevFocusedNode, AFocusedNode: TcxTreeListNode);  //节点事件,触发发布者的广播行为
  private
    { Private declarations }
    FPublisher: IWarehouseSelectionPublisher;
    function GetSelectedWarehouseID: Integer;
    function GetSelectedWarehouseCode: string;public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    // IWarehouseSelectionPublisher 接口实现
    procedure RegisterListener(AListener: IWarehouseSelectionListener);
    procedure UnregisterListener(AListener: IWarehouseSelectionListener);
    procedure NotifyWarehouseSelection(AWarehouseID: Integer; const AWarehouseCode: string);

  end;


//主要代码

constructor TCK.Create(AOwner: TComponent);
begin
  inherited;
  // 使用全局发布者实例
  FPublisher := WarehousePublisher;
end;

destructor TCK.Destroy;
begin
  FPublisher := nil;
  inherited;
end;

procedure TCK.cxDBTreeList1FocusedNodeChanged(Sender: TcxCustomTreeList;
  APrevFocusedNode, AFocusedNode: TcxTreeListNode);
var
  WarehouseID: Integer;
  WarehouseCode: string;
begin
  // 当 TreeList 节点改变时,通知所有监听器
  if not Assigned(AFocusedNode) then Exit;

  WarehouseID := GetSelectedWarehouseID;
  WarehouseCode := GetSelectedWarehouseCode;

  if (WarehouseID > 0) and (WarehouseCode <> '') then
  begin
    // 通过发布者通知所有监听器
    FPublisher.NotifyWarehouseSelection(WarehouseID, WarehouseCode);
  end;
end;

function TCK.GetSelectedWarehouseCode: string;     //获取仓库代码,参数按实际需求设置
begin
  Result := '';
  if Assigned(cxDBTreeList1.FocusedNode) then
    Result := cxDBTreeList1.FocusedNode.Values[1];
end;

function TCK.GetSelectedWarehouseID: Integer;    //获取仓库ID,参数按实际需求设置
begin
  Result := -1;
  if Assigned(cxDBTreeList1.FocusedNode) then
    Result := cxDBTreeList1.FocusedNode.Values[0];
end;
procedure TCK.RegisterListener(AListener: IWarehouseSelectionListener);
begin
  FPublisher.RegisterListener(AListener);
end;

procedure TCK.UnregisterListener(AListener: IWarehouseSelectionListener);
begin
  FPublisher.UnregisterListener(AListener);
end;

procedure TCK.NotifyWarehouseSelection(AWarehouseID: Integer;
  const AWarehouseCode: string);
begin
  FPublisher.NotifyWarehouseSelection(AWarehouseID, AWarehouseCode);
end;end.

三.监听者FrmGrid

unit FrmGrid;

interface

uses
  ..., FrmInterface;

type
  TGrid = class(TFrame, IWarehouseSelectionListener)
    ...
  private
    { Private declarations }
    FListener: IWarehouseSelectionListener;
    const
      isql = 'SELECT * from tblName';
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    // IWarehouseSelectionListener 接口实现
    procedure OnWarehouseSelected(const AWarehouseID: Integer; const AWarehouseCode: string);
    procedure FinalizeFrame;
  end;

implementation

{$R *.dfm}

constructor TGrid.Create(AOwner: TComponent);
begin
  inherited;
   // 将自己注册为监听器
  FListener := Self;
  WarehousePublisher.RegisterListener(FListener);
end;

destructor TGrid.Destroy;
begin
  // 注销监听器
  if Assigned(FListener) then
    WarehousePublisher.UnregisterListener(FListener);
  FListener := nil;
  inherited;
end;

procedure TGrid.FinalizeFrame;
begin
     // 清理资源
  if Assigned(FListener) then
    WarehousePublisher.UnregisterListener(FListener);
end;



{ TGrid }
 // 当仓库选择时,加载对应仓库的物料数据
procedure TGrid.OnWarehouseSelected(const AWarehouseID: Integer; const AWarehouseCode: string);
begin
  with DataModule1.FDQuery2 do
  begin
    Close;
    if AWarehouseID = 1 then   //用户点到了'全部' 节点
    begin
      sql.Text := isql;
    end
    else
    begin
      var isql2 := Format(isql + ' where 物料信息.默认仓库=%d', [AWarehouseID]);
      sql.Text := isql2;
    end;
    Open();
  end;

end;

end.

 

posted @ 2025-10-28 21:25  一曲轻扬  阅读(7)  评论(0)    收藏  举报