You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何存储(流化)TColor数组属性并保留颜色常量?

问题:VCL控件中TColor数组属性的持久化读写(保留颜色常量)

我正在编写一个非窗口VCL控件,其中包含名为Colors[]的TColor数组属性,用于在运行时设置控件不同部位的颜色,同时要给控件配备属性编辑器,方便设计时设置Colors[]属性。

我知道怎么存储数组属性,但从没处理过TColor类型的值。通常TColor属性会存储为颜色常量(比如clBlack、clWindowText)或者数值(自定义颜色)。TReader和TWriter有读写整数和字符串的方法,但没有直接读写TColor的方法,我查了TPersistent的源码也没找到相关实现。

以下是我的代码摘要:

class TMyControl : public TGraphicControl
{
    typedef TGraphicControl inherited;

    private:
        TColor* FColors;
        int FCount;

        TColor __fastcall GetColors (int Index);
        void __fastcall SetColors (int Index, TColor Value);
        void __fastcall ReadColors (TReader* Reader);
        void __fastcall WriteColors (TWriter* Writer);
        void __fastcall SetCount (int Value);

        void __fastcall CMColorChanged (TMessage &Msg);

    protected:
        virtual void __fastcall DefineProperties (TFiler *Filer);

    public:
        __fastcall TMyControl (TComponent *Owner);
        __fastcall ~TMyControl ();

        __property TColor Colors[int Index] = {read=GetColors, write=SetColors};

    __published:
        __property Color;

        __property int Count = {read=FCount, write=SetCount, default=1};

BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(CM_COLORCHANGED, TMessage, CMColorChanged)
END_MESSAGE_MAP(inherited)
};


void ModifyExtents (TColor* &Array, int NewSize, TColor Default)
{
    // Allocates/resizes 'Array' to contain 'NewSize' elements and initializes new elements with 'Default'.
}

__fastcall TMyControl::TMyControl (TComponent *Owner) : inherited(Owner)
{
}

__fastcall TMyControl::~TMyControl ()
{
    delete[] FColors;
}

TColor __fastcall TMyControl::GetColors (int Index)
{
    if (Index < 0 || Index >= FCount)
        throw Exception(SIndexOutOfRange);

    return(FColors ? FColors[Index] : Color);
}

void __fastcall TMyControl::SetColors (int Index, TColor Value)
{
    if (Index < 0 || Index >= FCount)
        throw Exception(SIndexOutOfRange);

    if (!FColors)
        ModifyExtents(FColors, FCount, Color);

    if (FColors[Index] != Value)
    {
        FColors[Index] = Value;

        Invalidate();
    }
}

void __fastcall TMyControl::SetCount (int Value)
{
    if (Value < 0)
        Value = 0;

    if (FCount != Value)
    {
        if (FColors)
            ModifyExtents(FColors, Value, Color);

        FCount = Value;

        Invalidate();
    }
}

void __fastcall TMyControl::CMColorChanged (TMessage &Msg)
{
    inherited::Dispatch(&Msg);

    if (FColors)
    {
        delete[] FColors;
        FColors = NULL;
    }
}

void __fastcall TMyControl::DefineProperties (TFiler* Filer)
{
    inherited::DefineProperties(Filer);

    Filer->DefineProperty("Colors", ReadColors, WriteColors, (FColors != NULL));
}

void __fastcall TMyControl::ReadColors (TReader* Reader)
{
    Reader->ReadListBegin();

    for (int i = 0; i < FCount; i++)
        Colors[i] = Reader->Read...();  // ReadInteger, ReadString, ...?

    Reader->ReadListEnd();
}

void __fastcall TMyControl::WriteColors (TWriter* Writer)
{
    Writer->WriteListBegin();

    for (int i = 0; i < FCount; i++)
        Writer->Write...(Colors[i]);    // WriteInteger, WriteString, ...?

    Writer->WriteListEnd();
}

Count属性指定控件不同部位的数量,也就是FColors数组存储的TColor值数量。只有写入Colors[]属性时才会分配FColors数组;如果FColors不存在,读取Colors[]会返回控件的Color属性值。设置控件的Color属性会销毁FColors数组。

需要解决的核心问题:如何读写TColor值,以保留颜色常量(而非转换为数值)?


解决方案

要实现TColor的持久化并保留颜色常量,需要利用VCL内置的ColorToIdent和IdentToColor函数,结合TReader/TWriter的字符串读写方法,具体实现如下:

1. 写入TColor值

在WriteColors方法中,先将TColor转换为对应的标识符字符串(如果是预定义颜色),否则转换为十六进制数值字符串,再用Writer->WriteString()写入:

void __fastcall TMyControl::WriteColors(TWriter* Writer)
{
    Writer->WriteListBegin();

    for (int i = 0; i < FCount; i++)
    {
        TColor ColorValue = Colors[i];
        AnsiString ColorStr = ColorToIdent(ColorValue);
        if (ColorStr.IsEmpty())
        {
            // 自定义颜色,转换为$00RRGGBB格式的十六进制字符串
            ColorStr = AnsiString().sprintf("$%08x", ColorValue);
        }
        Writer->WriteString(ColorStr);
    }

    Writer->WriteListEnd();
}

2. 读取TColor值

在ReadColors方法中,先读取字符串,尝试用IdentToColor转换为TColor,失败则解析十六进制数值:

void __fastcall TMyControl::ReadColors(TReader* Reader)
{
    Reader->ReadListBegin();

    // 先读取列表实际元素数量,避免与当前FCount不匹配
    int ListCount = Reader->ReadInteger();
    if (ListCount != FCount)
        SetCount(ListCount);

    for (int i = 0; i < ListCount; i++)
    {
        AnsiString ColorStr = Reader->ReadString();
        TColor ColorValue;

        // 优先解析为预定义颜色标识符
        ColorValue = IdentToColor(ColorStr);
        if (ColorValue == clNone)
        {
            // 解析为十六进制数值,失败则使用控件默认Color
            try
            {
                ColorValue = (TColor)StrToInt(ColorStr);
            }
            catch (...)
            {
                ColorValue = Color;
            }
        }

        Colors[i] = ColorValue;
    }

    Reader->ReadListEnd();
}

3. 关键注意事项

  • ColorToIdent和IdentToColor在Graphics.hpp中声明,确保代码中包含该头文件。
  • 读取时必须先获取列表实际长度,再调整控件的Count属性,避免数组越界或数据不匹配。
  • 自定义颜色会以$00RRGGBB格式存储,读取时能正确转换回原始数值。
  • 该实现完全遵循VCL的持久化惯例,预定义颜色保留常量名,自定义颜色存储数值。

内容的提问来源于stack exchange,提问作者Martin Nijhoff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:11:04