拷貝控件的問題。其實Delphi提供了非常好的持久化機制,筆者寫了一個類,提供兩個方法,一個是將多個控件保存到流中,另一個是從流中讀出控件。
2008-11-13 16:02
拷貝控件的問題。其實Delphi提供了非常好的持久化機制,筆者寫了一個類,提供兩個方法,一個是將多個控件保存到流中,另一個是從流中讀出控件。
下面是源代碼:
unit ComPersist;
interface
uses
Windows, Classes, Controls;
type
TComPersister = class
private
FRoot: TComponent;
protected
function UniqueName(BaseName: string): string; virtual;
procedure ReaderSetName(Reader: TReader; Component: TComponent;
var Name: string);
procedure ReaderReadComponent(Component: TComponent); virtual;
public
procedure SaveComsToStream(AStream: TStream; ComList: TList);
procedure LoadComsFromStream(AStream: TStream; AParent: TWinControl);
constructor Create(ARoot: TComponent);
property Root: TComponent read FRoot write FRoot;
end;
implementation
uses SysUtils;
{ TComPersister }
constructor TComPersister.Create(ARoot: TComponent);
begin
FRoot := ARoot;
end;
procedure TComPersister.LoadComsFromStream(AStream: TStream;
AParent: TWinControl);
var
Reader: TReader;
begin
Reader := TReader.Create(AStream, 1024);
try
Reader.OnSetName := ReaderSetName;
Reader.ReadComponents(FRoot, AParent, ReaderReadComponent);
finally
Reader.Free;
end;
end;
procedure TComPersister.ReaderReadComponent(Component: TComponent);
function ControlExist (AParent: TWinControl; ALeft, ATop: Integer): Boolean;
var
LI: Integer;
begin
Result := False;
for LI := 0 to AParent.ControlCount - 1 do
if AParent.Controls [LI] <> Component then
with AParent.Controls [LI] do
if (Left = ALeft) and (Top = ATop) then
begin
Result := True;
Break;
end;
end;
var
LNewLeft, LNewTop: Integer;
begin
if Component is TControl then
with TControl(Component) do
begin
if Parent <> nil then
begin
LNewLeft := Left;
LNewTop := Top;
while ControlExist(Parent, LNewLeft, LNewTop) do
begin
Inc (LNewLeft, 8);
Inc (LNewTop, 8);
end;
SetBounds (LNewLeft, LNewTop, Width, Height);
end;
end;
end;
procedure TComPersister.ReaderSetName(Reader: TReader;
Component: TComponent; var Name: string);
begin
//給控件取一個唯一的名字
if FRoot.FindComponent (Name) <> nil then
Name := UniqueName(Component.ClassName);
end;
procedure TComPersister.SaveComsToStream(AStream: TStream; ComList: TList);
var
Writer: TWriter;
i: Integer;
begin
Writer := TWriter.Create(AStream, 1024);
try
Writer.Root := FRoot;
for i := 0 to ComList.Count - 1 do
begin
Writer.WriteSignature;
Writer.WriteComponent(ComList[i]);
end;
Writer.WriteListEnd;
finally
Writer.Free;
end;
end;
function TComPersister.UniqueName(BaseName: string): string;
var
i: Integer;
LS: string;
begin
if (Length(BaseName) >= 2) and (BaseName[1] in ['t', 'T']) then
LS := Copy (BaseName, 2, MaxInt);
i := 0;
repeat
Inc(i);
Result := LS + IntToStr(i);
until FRoot.FindComponent (Result) = nil;
end;
end.
下面是新建一個窗體,代碼如下:
TForm1 = class(TForm)
BtnSave: TButton;
BtnLoad: TButton;
Button3: TButton;
Panel1: TPanel;
Button4: TButton;
procedure BtnSaveClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure BtnLoadClick(Sender: TObject);
private
{ Private declarations }
ComPersist: TComPersister;
MStream: TMemoryStream;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.BtnSaveClick(Sender: TObject);
var
List: TList;
begin
List := TList.Create;
MStream.Clear;
try
List.Clear;
List.Add(Panel1);
List.Add(Button3);
ComPersist.SaveComsToStream(MStream, List);
finally
List.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ComPersist := TComPersister.Create(self);
MStream := TMemoryStream.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
ComPersist.Free;
MStream.Free;
end;
procedure TForm1.BtnLoadClick(Sender: TObject);
begin
MStream.Position := 0;
ComPersist.LoadComsFromStream(MStream, Self);
end;
end.
說明:類中有一個FRoot成員,在類的構造方法中指明,是指拷貝的控件的根和最終擁有者,一般情況下都是窗體,所以一般要在構造方法中傳入窗體類,像上面那樣:
ComPersist := TComPersister.Create(self);
將控件保存爲流的方法是建一個List類,將要保存的控件加進去,然後調用:
ComPersist.SaveComsToStream(MStream, List);其中的MStream即是最後保存的流。
要將控件從流中讀出來,只需要:
MStream.Position := 0;
ComPersist.LoadComsFromStream(MStream, Self);
其中的Self是指控件讀出來後的Parent,如果把Self改爲Panel1,則讀出的控件最後將顯示在Panel1當中。
另外,如果要做到真正意義上的拷貝,粘貼,和剪切,則需要剪貼板的知識,定義一個自己的格式,然後將流中的數據保存到剪貼板上,這就是拷貝,如果要粘貼,則從剪貼板上讀出流,再調用上面的方法還原爲控件。用剪貼板的好處是即使程序關閉了,下次打開,也可以從剪貼板中取出控件來。Delphi的IDE就是這樣做。有興趣者自己完成吧。