拷貝控件的問題

拷貝控件的問題。其實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就是這樣做。有興趣者自己完成吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章