在运行时创建 TCombobox 的更快方法
Faster way to create TCombobox at Runtime
我想在运行时用许多具有相同列表的组合框填充一个表单。他们还获得相同的事件处理程序,该处理程序根据 Sender
对象的名称进行操作。但是,这需要很长时间,我猜我做错了什么。
我正在使用 XE2 Rad Studio C++ Builder 和 VCL GUI。
编辑: 这些框包含不同种类的内容,分布在表单中的几个标签页上。但是,有必要一目了然地显示至少 80 个 select 编辑的内容。当点击 TLabel 到 select 一个不同的元素时,用 TLabel 替换它们并创建一个 TCombobox 可能会更好吗?
代码看起来类似于:
void __fastcall TForm::TForm(){
int i=0;
TStringList* targetlist = new TStringList();
targetlist->Add("Normal");
targetlist->Add("Inverted");
Vcl::Stdctrls::TComboBox **com = new Vcl::Stdctrls::TComboBox[512];
for(i=0;i<512;++i){
com[i]=new Vcl::Stdctrls::TComboBox(this);
com[i]->Parent=this;
com[i]->Name.printf(L"Combo_%d", i);
com[i]->SetBounds(10, 198 + 20 * i, 130, 200);
com[i]->Items = targetlist;
com[i]->ItemIndex = 0;
com[i]->Style = csDropDownList;
com[i]->OnChange = MyComboTriggerChange;
}
}
在我的机器上一次迭代似乎需要大约 20 毫秒(用 std::clock
测试),这使得这部分长约 10 秒。指针在表单销毁时被删除。为了简化起见,我只是将他们的声明放在这里。
有没有更好的方法来创建多个组合框?也许克隆它们?
你真的需要重新设计你的UI。在具有相同值列表的一个屏幕上使用 512 TComboBox
控件在逻辑上没有意义,而且是浪费时间和资源。有更好的方法可以在屏幕上显示 512 个字符串,例如报告模式下的 TListView
或 TListBox
(它们都支持虚拟模式,因此它们可以共享公共数据而不会浪费内存)。或者将 TValueListEditor
或 TStringGrid
与 esPickList
内联编辑器一起使用。或者,如果您真的很有冒险精神,可以从头开始编写一个自定义控件,这样您就可以使用 1 个高效控件,而不是 512 个单独的控件。什么都比512TComboBox
控件好。
也就是说,TComboBox
不支持虚拟模式,就像 TListBox
和 TListView
一样,但是您仍然可以进行一些优化来加快您的速度TComboBox
有一点:
不要复制相同的 TStringList
内容 512 份。您添加到 TComboBox::Items
的任何内容都存储在 TComboBox
的内存中。你应该努力重用你的单一 TStringList
并让一切根据需要委托给它。在这种情况下,您可以将 TComboBox::Style
属性 设置为 csOwnerDrawFixed
并使用 TComboBox::OnDrawItem
事件按需绘制 TStringList
字符串。您仍然需要为每个 TComboBox
添加字符串,但它们至少可以是空字符串。
子类 TComboBox
覆盖其虚拟 CreateParams()
方法并删除 CBS_HASSTRINGS
window 样式,然后 TComboBox
实际上不需要在其内存中存储空字符串。
尝试这样的事情:
class TMyComboBox : public Vcl::Stdctrls::TComboBox
{
typedef Vcl::Stdctrls::TComboBox inherited;
private:
TStrings *fSharedItems;
void __fastcall SetSharedItems(TStrings *Values)
{
if (fSharedItems != Values)
{
fSharedItems = Values;
Items->BeginUpdate();
try
{
Items->Clear();
if (fSharedItems)
{
for (int i = 0; i < fSharedItems->Count; ++i)
Items->Add(L"");
}
}
__finally
{
Items->EndUpdate();
}
}
}
protected:
virtual void __fastcall CreateParams(TCreateParams &Params)
{
inherited::CreateParams(Params);
Params.Style &= ~CBS_HASSTRINGS;
}
virtual __fastcall DrawItem(int Index, TRect Rect, TOwnerDrawState State)
{
// draw the items however you want...
if (fSharedItems)
Canvas->TextRect(Rect.Left, Rect.Top, fSharedItems->Strings[Index]);
}
public:
__fastcall TMyComboBox(TComponent *Owner)
: Vcl::Stdctrls::TComboBox(Owner)
{
Style = csOwnerDrawFixed;
}
__property TStrings* SharedItems = {read=fSharedItems, write=SetSharedItems};
};
class TMyForm : public TForm
{
...
private:
TStringList* targetlist;
TMyComboBox **com;
void __fastcall MyComboTriggerChange(TObject *Sender);
...
public:
__fastcall TMyForm(TComponent *Owner);
__fastcall ~TMyForm();
...
};
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
targetlist = new TStringList;
targetlist->Add("Normal");
targetlist->Add("Inverted");
com = new TMyComboBox*[512];
for(int i=0;i<512;++i)
{
com[i] = new TMyComboBox(this);
com[i]->Parent = this;
com[i]->Name = String().sprintf(L"Combo_%d", i);
com[i]->SetBounds(10, 198 + 20 * i, 130, 200);
com[i]->SharedItems = targetlist;
com[i]->ItemIndex = 0;
com[i]->OnChange = &MyComboTriggerChange;
}
}
__fastcall TMyForm::~TMyForm()
{
delete targetlist;
delete[] com;
}
void __fastcall TMyForm::MyComboTriggerChange(TObject *Sender)
{
TMyComboBox *cb = static_cast<TMyComboBox*>(Sender);
// use targetlist->Strings[cb->ItemIndex] as needed...
}
我想在运行时用许多具有相同列表的组合框填充一个表单。他们还获得相同的事件处理程序,该处理程序根据 Sender
对象的名称进行操作。但是,这需要很长时间,我猜我做错了什么。
我正在使用 XE2 Rad Studio C++ Builder 和 VCL GUI。
编辑: 这些框包含不同种类的内容,分布在表单中的几个标签页上。但是,有必要一目了然地显示至少 80 个 select 编辑的内容。当点击 TLabel 到 select 一个不同的元素时,用 TLabel 替换它们并创建一个 TCombobox 可能会更好吗?
代码看起来类似于:
void __fastcall TForm::TForm(){
int i=0;
TStringList* targetlist = new TStringList();
targetlist->Add("Normal");
targetlist->Add("Inverted");
Vcl::Stdctrls::TComboBox **com = new Vcl::Stdctrls::TComboBox[512];
for(i=0;i<512;++i){
com[i]=new Vcl::Stdctrls::TComboBox(this);
com[i]->Parent=this;
com[i]->Name.printf(L"Combo_%d", i);
com[i]->SetBounds(10, 198 + 20 * i, 130, 200);
com[i]->Items = targetlist;
com[i]->ItemIndex = 0;
com[i]->Style = csDropDownList;
com[i]->OnChange = MyComboTriggerChange;
}
}
在我的机器上一次迭代似乎需要大约 20 毫秒(用 std::clock
测试),这使得这部分长约 10 秒。指针在表单销毁时被删除。为了简化起见,我只是将他们的声明放在这里。
有没有更好的方法来创建多个组合框?也许克隆它们?
你真的需要重新设计你的UI。在具有相同值列表的一个屏幕上使用 512 TComboBox
控件在逻辑上没有意义,而且是浪费时间和资源。有更好的方法可以在屏幕上显示 512 个字符串,例如报告模式下的 TListView
或 TListBox
(它们都支持虚拟模式,因此它们可以共享公共数据而不会浪费内存)。或者将 TValueListEditor
或 TStringGrid
与 esPickList
内联编辑器一起使用。或者,如果您真的很有冒险精神,可以从头开始编写一个自定义控件,这样您就可以使用 1 个高效控件,而不是 512 个单独的控件。什么都比512TComboBox
控件好。
也就是说,TComboBox
不支持虚拟模式,就像 TListBox
和 TListView
一样,但是您仍然可以进行一些优化来加快您的速度TComboBox
有一点:
不要复制相同的
TStringList
内容 512 份。您添加到TComboBox::Items
的任何内容都存储在TComboBox
的内存中。你应该努力重用你的单一TStringList
并让一切根据需要委托给它。在这种情况下,您可以将TComboBox::Style
属性 设置为csOwnerDrawFixed
并使用TComboBox::OnDrawItem
事件按需绘制TStringList
字符串。您仍然需要为每个TComboBox
添加字符串,但它们至少可以是空字符串。子类
TComboBox
覆盖其虚拟CreateParams()
方法并删除CBS_HASSTRINGS
window 样式,然后TComboBox
实际上不需要在其内存中存储空字符串。
尝试这样的事情:
class TMyComboBox : public Vcl::Stdctrls::TComboBox
{
typedef Vcl::Stdctrls::TComboBox inherited;
private:
TStrings *fSharedItems;
void __fastcall SetSharedItems(TStrings *Values)
{
if (fSharedItems != Values)
{
fSharedItems = Values;
Items->BeginUpdate();
try
{
Items->Clear();
if (fSharedItems)
{
for (int i = 0; i < fSharedItems->Count; ++i)
Items->Add(L"");
}
}
__finally
{
Items->EndUpdate();
}
}
}
protected:
virtual void __fastcall CreateParams(TCreateParams &Params)
{
inherited::CreateParams(Params);
Params.Style &= ~CBS_HASSTRINGS;
}
virtual __fastcall DrawItem(int Index, TRect Rect, TOwnerDrawState State)
{
// draw the items however you want...
if (fSharedItems)
Canvas->TextRect(Rect.Left, Rect.Top, fSharedItems->Strings[Index]);
}
public:
__fastcall TMyComboBox(TComponent *Owner)
: Vcl::Stdctrls::TComboBox(Owner)
{
Style = csOwnerDrawFixed;
}
__property TStrings* SharedItems = {read=fSharedItems, write=SetSharedItems};
};
class TMyForm : public TForm
{
...
private:
TStringList* targetlist;
TMyComboBox **com;
void __fastcall MyComboTriggerChange(TObject *Sender);
...
public:
__fastcall TMyForm(TComponent *Owner);
__fastcall ~TMyForm();
...
};
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
targetlist = new TStringList;
targetlist->Add("Normal");
targetlist->Add("Inverted");
com = new TMyComboBox*[512];
for(int i=0;i<512;++i)
{
com[i] = new TMyComboBox(this);
com[i]->Parent = this;
com[i]->Name = String().sprintf(L"Combo_%d", i);
com[i]->SetBounds(10, 198 + 20 * i, 130, 200);
com[i]->SharedItems = targetlist;
com[i]->ItemIndex = 0;
com[i]->OnChange = &MyComboTriggerChange;
}
}
__fastcall TMyForm::~TMyForm()
{
delete targetlist;
delete[] com;
}
void __fastcall TMyForm::MyComboTriggerChange(TObject *Sender)
{
TMyComboBox *cb = static_cast<TMyComboBox*>(Sender);
// use targetlist->Strings[cb->ItemIndex] as needed...
}