C++ Builder XE4, 10.2 Tokyo > TStreamWriter > clWhite cannot be copied

旧巷老猫 提交于 2021-02-19 09:42:04

问题


My environment:

  • RadStudio 10.2 Tokyo (and also XE4)

I was implementing a copy property method to copy TShape properties.

Following is what I implemented:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    // set Shape1 color to [clWhite]
    Shape1->Brush->Color = clRed;   // clWhite
    Shape2->Brush->Color = clAqua;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
    // to Keep original names
    String orgName_src = srcCtrl->Name;
    String orgName_dst = dstCtrl->Name;

    // copy properties
    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;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    copyProperties((TControl *)Shape1, (TControl *)Shape2);

    // shift to avoid position-overlapping
    Shape2->Left = Shape1->Left + 150;
}
//---------------------------------------------------------------------------

The code seems work fine.

But there is a single case, in which the code does not work. i.e. when the Brush->Color = clWhite for Shape1.

This bug? can be reproduced also for XE4.

I wonder why only the clWhite has this kind of bug? Other colors does not have this kind of bug.


回答1:


There is no bug in the streaming. It is operating as designed. You are simply using it in a way that it is not intended for.

clWhite is the declared default value of the TBrush.Color property. The DFM streaming system does not stream properties that are currently set to their default values, unless those properties are declared as nodefault or stored=true. TBrush.Color is neither. So the current Brush.Color value will not be streamed when it is set to clWhite.

Consider using the RTTI system directly instead of using the DFM system to copy properties from one object to another. Then you can copy property values regardless of defaults, if you choose to do so. And you can opt to ignore the Name property without having to (re)store it each time.

For example:

#include <System.TypInfo.hpp>

void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
    PTypeInfo pDstTypeInfo = static_cast<PTypeInfo>(dstCtrl->ClassInfo());

    PPropList srcPropList;
    int srcPropCount = GetPropList(srcCtrl, srcPropList);
    try
    {
        for (int i = 0; i < srcPropCount; ++i)
        {
            PPropInfo pSrcPropInfo = (*srcPropList)[i];
            if (pSrcPropInfo->Name == "Name") continue;

            PTypeInfo pSrcPropTypeInfo = *(pSrcPropInfo->PropType);

            if (pSrcPropTypeInfo->Kind == tkClass)
            {
                PPropInfo pDstPropInfo = GetPropInfo(pDstTypeInfo, pSrcPropInfo->Name, TTypeKinds() << tkClass);
                if (pDstPropInfo)
                {
                    TPersistent *pDstObj = static_cast<TPersistent*>(GetObjectProp(dstCtrl, pDstPropInfo, __classid(TPersistent)));
                    if (pDstObj)
                    {
                        TPersistent *pSrcObj = static_cast<TPersistent*>(GetObjectProp(srcCtrl, pSrcPropInfo, __classid(TPersistent)));
                        pDstObj->Assign(pSrcObj);
                    }
                }
            }
            else
            {
                PPropInfo pDstPropInfo = GetPropInfo(pDstTypeInfo, pSrcPropInfo->Name);
                if (pDstPropInfo)
                {
                    Variant value = GetPropValue(srcCtrl, pSrcPropInfo);
                    SetPropValue(dstCtrl, pDstPropInfo, value);
                }
            }
        }
    }
    __finally
    {
        FreeMem(srcPropList);
    }
}

Alternatively:

#include <System.Rtti.hpp>

void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
    TRttiContext ctx;

    TRttiType *pSrcType = ctx.GetType(srcCtrl->ClassInfo());
    TRttiType *pDstType = ctx.GetType(dstCtrl->ClassInfo());

    DynamicArray<TRttiProperty*> srcProps = pSrcType->GetProperties();
    for (int i = 0; i < srcProps.Length; ++i)
    {
        TRttiProperty *pSrcProp = srcProps[i];
        if (pSrcProp->Name == L"Name") continue;

        if (pSrcProp->PropertyType->TypeKind == tkClass)
        {
            TRttiProperty *pDstProp = pDstType->GetProperty(pSrcPropInfo->Name);
            if ((pDstProp) && (pDstProp->PropertyType->TypeKind == tkClass))
            {
                TPersistent *pDstObj = dynamic_cast<TPersistent*>(pDstProp->GetValue(dstCtrl).AsObject());
                if (pDstObj)
                {
                    TPersistent *pSrcObj = dynamic_cast<TPersistent*>(pSrcProp->GetValue(srcCtrl).AsObject());
                    pDstObj->Assign(pSrcObj);
                }
            }
        }
        else
        {
            TRttiProperty *pDstProp = pDstType->GetProperty(pSrcPropInfo->Name);
            if (pDstProp)
            {
                TValue value = pSrcProp->GetValue(srcCtrl);
                pDstProp->SetValue(dstCtrl, value);
            }
        }
    }
}


来源:https://stackoverflow.com/questions/53931205/c-builder-xe4-10-2-tokyo-tstreamwriter-clwhite-cannot-be-copied

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!