C++ Builder XE4 > TControl > プロパティのコピー > Shape1からShape2へプロパティをコピーする


動作環境
C++ Builder XE4

処理概要

  • Shape1:TShapeからShape2:TShapeへプロパティをコピーしたい
    • 線の幅や色などプロパティを一つ一つコピーするのでなく、丸ごとコピーする

参考

RTTIの方法など紹介されている例もあるが、自分の目に留まったのはTMemoryStreamを使った実装。

下記はDelphiでの実装。

それをC++ Builder用に変更した

フォームデザイン

実装

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TShape *Shape1;
    TShape *Shape2;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
private:    // ユーザー宣言
    void __fastcall copyProperties(TControl *srcCtrl, TControl *dstCtrl);
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};


//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    // 
    copyProperties((TControl *)Shape1, (TControl *)Shape2);

    // 重なっているため
    Shape2->Left = Shape1->Left + 150;
}

void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
    // 元の名前を保持
    String orgName_src = srcCtrl->Name;
    String orgName_dst = dstCtrl->Name;

    // プロパティコピー
    TMemoryStream *strm = new TMemoryStream;
    Shape1->Name = L"";  // to avoid source collision
    try {
        strm->WriteComponent(srcCtrl);
        strm->Position = 0;
        strm->ReadComponent(dstCtrl);
    }
    __finally
    {
        delete strm;
    }

    srcCtrl->Name = orgName_src;
    dstCtrl->Name = orgName_dst;
}

動作例

ソフト起動直後

Button1押下後

イベントのコピー

に記載があるように、上記の実装では「プロパティ」のコピーはできるが、「イベント」のコピーはできない。
「イベント」のコピーは上記のDelphi実装を参考に別途実装する必要がある。

(2018年12月現在は不要のため、この記事では実装しない)

関連

不具合? > フォーム間で「同じ」名前のコンポーネント間のコピー

  • FormA: Shape1
  • FormB: Shape1

2つのコンポーネントでコピーをするとどうなるか?
下記のエラーが発生した。

モジュール 'XXX.exe'のアドレス XXXでアドレスXXXに対する読取り違反がおきました。

名前は同じだとダメなのだろうか。

試しに名前を違うようにして実行したが、同じエラーとなった。

失敗理由が分かった。

v0.2

copyProperties()内のsrcCtrl->Name = L"";srcCtrl->Name = L"";に修正。

Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    // 
    copyProperties((TControl *)Shape1, (TControl *)Shape2);

    // 重なっているため
    Shape2->Left = Shape1->Left + 150;
}

void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
    // 元の名前を保持
    String orgName_src = srcCtrl->Name;
    String orgName_dst = dstCtrl->Name;

    // プロパティコピー
    TMemoryStream *strm = new TMemoryStream;
    srcCtrl->Name = L"";  // to avoid source collision
    try {
        strm->WriteComponent(srcCtrl);
        strm->Position = 0;
        strm->ReadComponent(dstCtrl);
    }
    __finally
    {
        delete strm;
    }

    srcCtrl->Name = orgName_src;
    dstCtrl->Name = orgName_dst;
}