可以通过将 class 名称指定为字符串来在运行时创建控件吗?

Can one create a control at runtime by specifying its class name as a string?

而不是,

TButton *button = new TButton(MyForm);

有人可以做这样的事情吗?

TControl *control = new TControl(MyForm, "TButton");

在 Delphi 中,可以根据 RTTI 的 class' 元类型创建对象实例。毕竟,这正是 DFM 流在运行时所做的。

但是在 C++ 中,您不能使用 RTTI 做任何类似的事情。

因此,您必须:

  1. 让您的 C++ 代码创建自己的 table 字符串到函数映射的查找,其中函数调用适当的 class 构造函数,例如:
#include <string>
#include <map>
#include <functional>

std::map<std::string, std::function<TControl*(TComponent*)>> mymap;
...
mymap["TButton"] = [](TComponent *Owner) -> TControl* { return new TButton(Owner); };
...

或者:

#include <string>
#include <map>

typedef TControl* (*CreateControlFunc)(TComponent*);
std::map<std::string, CreateControlFunc> mymap;
...
TControl* CreateButton(TComponent *Owner) { return new TButton(Owner); }
mymap["TButton"] = &CreateButton;
...

无论哪种方式,那么你可以这样做:

TControl *control = mymap["TButton"](MyForm);
  1. .pas 单元中编写一个 Delphi 函数来处理按 class 名称创建的对象,然后将该文件添加到您的 C++Builder 项目中。编译项目时,将输出一个 .hpp 文件,您可以在 C++ 代码中 #include
unit CreateControlHelper;

interface

uses
  Classes, Controls;

function CreateControlByClassName(const ClassName: String; Owner: TComponent): TControl;

implementation

function CreateControlByClassName(const ClassName: String; Owner: TComponent): TControl;
var
  Cls: TPersistentClass;
begin
  Cls := FindClass(ClassName);
  if (Cls <> nil) and Cls.InheritsFrom(TControl) then
    Result := TControlClass(Cls).Create(Owner)
  else
    Result := nil;
end;

end.
#include "CreateControlHelper.hpp"

TControl *control = CreateControlByClassName("TButton", MyForm);

更新:

  1. 理论上,你也可以使用Enhanced RTTI,因为它的实现基于Delphi,例如:
#include <System.Rtti.hpp>

TRttiMethod* FindControlConstructor(TRttiType *Type)
{
    while (Type)
    {
        DynamicArray<TRttiMethod*> methods = Type->GetDeclaredMethods();
        for(int i = 0; i < methods.Length; ++i)
        {
            TRttiMethod *method = methods[i];
            if ((method->Name == "Create") && (method->IsConstructor))
            {
                DynamicArray<TRttiParameter*> params = method->GetParameters();
                if ((params.Length == 1) && (params[0]->ParamType->Handle == __typeinfo(TComponent)))
                {
                    return method;
                }
            }
        }
        Type = Type->BaseType;
    }
    return NULL;
}

TControl* CreateControlByClassName(const String &QualifiedClassName, TComponent *Owner)
{
    TRttiContext ctx = TRttiContext::Create();
    TRttiType *type = ctx.FindType(QualifiedClassName);
    TRttiMethod *method = FindControlConstructor(type);
    if (method)
    {
        TValue val;
        val = Owner;
        TValue res = method->Invoke(type->AsInstance()->MetaclassType, &val, 0);
        return static_cast<TControl*>(res.AsObject());
    }
    return NULL;
}
TControl *control = CreateControlByClassName("Vcl.StdCtrls.TButton", MyForm);