首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 剪貼簿進階 (4):連同子控件一起複製組件(組件流式存取)

     編輯:Delphi

問題背景

Clipboard.SetComponent 默認只序列化組件本身。若容器組件(如 TPanel)包含子控件(TEdit、TButton 等),直接複製面板不會帶上子控件。解決方案:創建自定義容器類,在構造函數中內建子控件,並註冊類,使流式系統識別完整對象圖。

核心原理:組件流式機制

  • Delphi 組件通過 WriteComponent/ReadComponent 序列化/反序列化。
  • 流式系統按類名查找 RegisterClasses 註冊表實例化對象。
  • 父組件的 .dfm 流中包含子組件的屬性數據,前提是子組件被父組件擁有(Owner := Self)。

完整實現:自定義帶編輯框的面板

1. 組件單元 MyPanel.pas

unit MyPanel;

interface

uses
  Classes, StdCtrls, ExtCtrls;

type
  TMyPanel = class(TPanel)
  private
    FEdit: TEdit;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property Edit: TEdit read FEdit;
  end;

implementation

constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  // 內建子控件,關鍵:Owner := Self,Parent := Self
  FEdit := TEdit.Create(Self);
  FEdit.Parent := Self;
  FEdit.SetBounds(10, 10, 150, 24);
  FEdit.Text := '隨面板複製';
  // 註冊類,讓流式系統能按類名實例化
  RegisterClasses([TMyPanel]);
end;

destructor TMyPanel.Destroy;
begin
  // FEdit 由 Owner 釋放,無需手動 Free
  inherited;
end;

end.

2. 測試主程式

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Clipbrd, MyPanel;

type
  TForm1 = class(TForm)
    btnCopy: TButton;
    btnPaste: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnCopyClick(Sender: TObject);
    procedure btnPasteClick(Sender: TObject);
  private
    FPanel: TMyPanel;
    FPastedObj: TComponent;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // 創建自定義面板實例
  FPanel := TMyPanel.Create(Self);
  FPanel.Parent := Self;
  FPanel.SetBounds(20, 20, 200, 100);
  FPanel.Caption := '源面板';
  FPanel.Edit.Text := '一起被複製的編輯框';

  btnCopy.Caption := '複製面板';
  btnPaste.Caption := '粘貼副本';
  btnPaste.Left := 120;
end;

procedure TForm1.btnCopyClick(Sender: TObject);
begin
  Clipboard.SetComponent(FPanel);
  ShowMessage('面板及其子控件已放入剪貼簿');
end;

procedure TForm1.btnPasteClick(Sender: TObject);
var
  NewPanel: TMyPanel;
begin
  if not Clipboard.HasFormat(CF_COMPONENT) then Exit;

  // GetComponent(Owner, Parent) 返回新組件
  FPastedObj := Clipboard.GetComponent(Self, Self);
  if not Assigned(FPastedObj) then Exit;

  NewPanel := FPastedObj as TMyPanel;
  // 避免重疊,稍微偏移
  NewPanel.SetBounds(FPanel.Left + 30, FPanel.Top + 50, FPanel.Width, FPanel.Height);
  NewPanel.Caption := '副本_' + IntToStr(Random(1000));
  NewPanel.Edit.Text := '我是副本的編輯框';
end;

initialization
  // 關鍵:確保 TMyPanel 在流式系統中可被識別
  RegisterClasses([TMyPanel]);

end.

關鍵點深度解析

1. RegisterClasses 的時機與位置

  • 最佳位置:單元的 initialization 區塊。保證單元被引用時自動註冊,無需調用方記憶。
  • 重複註冊無害:RTL 內部去重,可在構造函數、FormCreate、initialization 多處出現。
  • 遺漏後果:反序列化時拋出 EClassNotFound「Class not found: TMyPanel」。

2. 所有權鏈:Owner vs Parent

代碼中 FEdit := TEdit.Create(Self) 確立 Owner 鏈,FEdit.Parent := Self 確立視覺樹。

3. GetComponent(AOwner, AParent) 參數含義

  • AOwner:新組件的 Owner(通常為 Form 或 Frame),負責釋放。
  • AParent:新組件的 Parent(容器控件),決定顯示位置。傳 nil 表示無父容器(需手動設 Parent)。

4. 複製後的副本特性

  • 副本是全新實例,與源組件無任何引用關係。
  • 所有發布屬性(published)被複製,運行時動態修改的屬性(如 Text、Color)同步複製。
  • 事件處理器不複製(方法指針無法跨實例序列化),需粘貼後手動綁定。
  • Name 屬性自動重命名(如 MyPanel1 → MyPanel2)避免衝突。

擴展:複製任意組件樹

同理,可構建包含多層嵌套控件的複合組件:

type
  TToolBarPanel = class(TPanel)
    FBtnNew, FBtnOpen, FBtnSave: TButton;
    FCombo: TComboBox;
  public
    constructor Create(AOwner: TComponent); override;
  end;

構造函數中創建所有子控件,RegisterClasses([TToolBarPanel]),即可整體複製粘貼工具欄。

總結

「連同子控件複製」的本質是將組件樹封裝為單一可序列化的複合組件。核心三步:1. 繼承容器類 2. 構造函數內建子控件(Owner=Self) 3. RegisterClasses 註冊。此技術廣泛應用於可視化設計器、報表模板複製、UI 組件庫拖放等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
屬性作用複製行為
Owner生命週期管理,析構時釋放流式存儲必須,決定組件樹結構
Parent視覺包含、座標系、訊息路由不存儲,粘貼時由 GetComponent 的 AParent 參數指定