可以通过将 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 做任何类似的事情。
因此,您必须:
- 让您的 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);
- 在
.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);
更新:
- 理论上,你也可以使用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);
而不是,
TButton *button = new TButton(MyForm);
有人可以做这样的事情吗?
TControl *control = new TControl(MyForm, "TButton");
在 Delphi 中,可以根据 RTTI 的 class' 元类型创建对象实例。毕竟,这正是 DFM 流在运行时所做的。
但是在 C++ 中,您不能使用 RTTI 做任何类似的事情。
因此,您必须:
- 让您的 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);
- 在
.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);
更新:
- 理论上,你也可以使用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);