Конференция "Компоненты" » Клонирование компонентов
 
  • estra (04.10.11 10:26) [0]
    Всем доброго времени суток.

    Нужна ваша помощь в поиске оптимального решения следующей задачи. Пишу редактор форм, аналогичный Delphi. Нужно реализовать поддержку копипаста (ctrl+c, ctrl+v). Решил пойти тем же путем, что и любимый компилятор, то есть по ctrl+c в буфер копируется текстовое представление компонента:

    function ComponentToString( Component: TComponent ): string;
    var
     BinStream: TMemoryStream;
     StrStream: TStringStream;
    begin
      BinStream := TMemoryStream.Create;
      try
         StrStream := TStringStream.Create( Result );
         try
            BinStream.WriteComponent( Component );
            BinStream.Seek( 0, soFromBeginning );
            ObjectBinaryToText( BinStream, StrStream );
            StrStream.Seek( 0, soFromBeginning );
            Result := StrStream.DataString;
         finally
            StrStream.Free;
         end;
      finally
         BinStream.Free
      end;
    end;



    А вот с обратным действием возникли затруднения. Получить из строки компонент я могу

    procedure StringToComponent( Component: TComponent; Value: string );
    var
     StrStream: TStringStream;
     BinStream: TMemoryStream;
    begin
      StrStream := TStringStream.Create( Value );
      try
         BinStream := TMemoryStream.Create;
         try
            ObjectTextToBinary( StrStream, BinStream );
            BinStream.Position := 0;
            BinStream.ReadComponent( Component );
         finally
            BinStream.Free;
         end;
      finally
         StrStream.Free;
      end;
    end;



    но есть пара моментов, которые пока не получается оптимизировать.

    1. Скопировав компонент в буфер и тут же вставляя его на форму я получу ошибку, т.к. компонент с таким именем на форме уже есть. Пока вижу решение в том, чтобы парсить строку, "вытаскивать" имя компонента, и менять его если компонент с таким именем на форме уже есть.

    2. Передавать в качестве параметра Component нужно переменную того типа, строковое представление которого лежит сейчас в буфере. Значит опять нужно парсить строку, а дальше огромная вереница из if'ов.

    Работать, конечно, такое будет, но хочется более изящного решения. Ведь в Delphi сотни компонентов, и можно кучу сторонних установить, и при этом ctrl+c/ctrl+v работают, значит есть универсальное решение. Пожалуйста, помогите его найти!
  • Slym © (04.10.11 11:00) [1]
    function StringToComponent(const Value: string):TComponent; вар параметр или результат
    var
     StrStream: TStringStream;
     BinStream: TMemoryStream;
    begin
     StrStream := TStringStream.Create( Value );
     try
       BinStream := TMemoryStream.Create;
       try
         ObjectTextToBinary( StrStream, BinStream );
         BinStream.Seek(0,soFromBeginning);
         result:=BinStream.ReadComponent(nil);
        finally
           BinStream.Free;
        end;
     finally
        StrStream.Free;
     end;
    end;

  • Slym © (04.10.11 11:01) [2]
    estra   (04.10.11 10:26)
    2. Передавать в качестве параметра Component нужно переменную того типа, строковое представление которого лежит сейчас в буфере. Значит опять нужно парсить строку, а дальше огромная вереница из if'ов.

    RegisterClass(TButton);
    RegisterClass(TMemo);
    и тек далее :)
  • estra (04.10.11 11:32) [3]
    Slym
    Спасибо, дело сдвинулось с места. Но снова вопрос.

    var
     _Component: TComponent;
    begin
      _Component := StringToComponent( 'строковое представление компонента' );
      // Далее нужно установить свойство Parent, чтобы отобразить компонент,
      // а для этого нужно сделать приведение типа,
      ( _Component as TEdit ).Parent := ...
    end;



    Можно ли эту строчку ( ( _Component as TEdit ).Parent := ... ) унифицировать, иначе от кучи if'ов не уйти

    if __Component is TEdit then
    и так далее

    if ( _Component as TEdit ).Parent := ...
  • estra (04.10.11 11:42) [4]
    И владельца тоже нужно как то установить компоненту...
  • estra (04.10.11 12:17) [5]
    С владельцем разобрался - InsertComponent, теперь от кучи if'ов надо избавиться
  • Slym © (04.10.11 13:04) [6]
    c:=StringToComponent(s);
     if c is TControl then
     begin
       Form1.InsertComponent(c);
       Form1.InsertControl(TControl(c));
       TControl(c).Top:=10;
     end;
  • estra (04.10.11 14:45) [7]
    Огромное спасибо, вопрос решен.
  • Zummer © (12.03.15 10:08) [8]
    Клонирование компонента на примере TEdit

    // ---------------------------------------------------------------------------
    // абстрактный класс, с виртуальной фунцией
    class TClonable {
    public:
           virtual TClonable* Clone() = 0;
    }
    ;

    // ---------------------------------------------------------------------------
    // множественное наследование, которое допустимо
    // с использованием не VCL классов
    class TClonableEdit : public TEdit, public TClonable {
    public:
           __fastcall TClonableEdit(TComponent *Owner) : TEdit(Owner) {

                   TForm *form = (TForm*)Owner;
                   Parent = form;
           }
    ;

           // ---------------------------------------------------------------------------
           // конструктор копирования
           // тут обратите внимание на TEdit(rhs.Owner)
           // передаем в базовый класс VCL не rhs, а rhs.Owner  
           TClonableEdit(const TClonableEdit& rhs) : TEdit(rhs.Owner) {
                   Top = rhs.Top + 50;
                   Color = clRed;
                   Parent = rhs.Parent;
                   Text = "Клон";
           }
    ;
           // ---------------------------------------------------------------------------
           // функция клонирования
           TClonableEdit* Clone() {
                   return new TClonableEdit(*this);
           }
    ;
    };

    // ---------------------------------------------------------------------------
    class TEditWithLabel : public TClonableEdit {
    public:
           __fastcall TEditWithLabel(TComponent *Owner) : TClonableEdit(Owner) {
                   label = new TLabel(this);
                   label->Parent = Parent;
                   label->Caption = "Какая-то надпись";
                   label->Top = Top;
                   label->Left = Left + Width + 5;
           }
    ;

           TLabel *label;

           // ---------------------------------------------------------------------------
           // конструктор копирования
           TEditWithLabel(const TEditWithLabel& rhs) : TClonableEdit(rhs) {
                   label = new TLabel(this);
                   label->Parent = rhs.label->Parent;
                   label->Caption = rhs.label->Caption;
                   label->Top = Top;
                   label->Left = rhs.label->Left;
           }
    ;
           // ---------------------------------------------------------------------------
           // функция клонирования
           TEditWithLabel* Clone() {
                   return new TEditWithLabel(*this);
           }
    ;
    };

    // использование
    TEditWithLabel *newEdit = new TEditWithLabel(this);
    newEdit->Text = "Исходник";
    // допустим что мы не знаем какой класс у newEdit,
    // поэтому обращаемся к нему через TClonableEdit  
    TClonableEdit *clone = newEdit->Clone();
    // но в результате получаем его копию в TEditWithLabel

 
Конференция "Компоненты" » Клонирование компонентов
Есть новые Нет новых   [134427   +34][b:0][p:0.004]