Более быстрый способ создания TCombobox во время выполнения
Я хочу заполнить форму во время выполнения с помощью множества комбинированных списков с одинаковыми списками. Они также получают тот же обработчик событий, который действует в зависимости от Sender
Наименование объектов. Тем не менее, это занимает довольно много времени, и я догадался, что я что-то не так делаю.
Я использую XE2 Rad Studio C++ Builder и графический интерфейс VCL.
Изменить: эти поля содержат различные виды содержимого и распределены по нескольким вкладкам внутри формы. тем не менее, необходимо отобразить то, что он выбрал, по крайней мере, 80 из них с первого взгляда. Может быть, было бы лучше заменить их на TLabels и создать TCombobox при нажатии на TLabel, чтобы выбрать другой элемент?
Код выглядит примерно так:
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 с. Указатели удаляются при уничтожении формы. Я просто разместил их декларации здесь для упрощения.
Есть ли лучший способ создать несколько комбинированных списков? Может клонировать их?
1 ответ
Вы серьезно должны изменить свой интерфейс. Используя 512 TComboBox
элементы управления на одном экране с одинаковым списком значений не имеют логического смысла и являются пустой тратой времени и ресурсов. Существуют лучшие способы отображения 512 строк на экране, такие как TListView
в режиме отчета или TListBox
(оба они поддерживают виртуальный режим, поэтому они могут обмениваться общими данными без потери памяти). Или используйте TValueListEditor
или же TStringGrid
с esPickList
встроенный редактор. Или, если вы действительно любите приключения, напишите собственный элемент управления с нуля, чтобы вы использовали 1 эффективный элемент управления вместо 512 отдельных элементов управления. Все лучше, чем 512 TComboBox
управления.
Что, как говорится, TComboBox
не поддерживает виртуальный режим, как TListBox
а также TListView
сделать, но есть несколько оптимизаций, которые вы можете сделать, чтобы ускорить ваш TComboBox
Это немного:
не делайте 512 копий одного и того же
TStringList
содержание. Все, что вы добавляете вTComboBox::Items
хранится внутриTComboBox
в памяти. Вы должны стремиться повторно использовать свой синглTStringList
и пусть все делегируют ему по мере необходимости. В этом случае вы можете установитьTComboBox::Style
собственность наcsOwnerDrawFixed
и использоватьTComboBox::OnDrawItem
событие, чтобы нарисоватьTStringList
Строки по запросу. Вам все еще нужно добавить строки к каждомуTComboBox
, но они могут быть пустыми строками, по крайней мере.подкласс
TComboBox
переопределить его виртуальныйCreateParams()
метод и удалитьCBS_HASSTRINGS
стиль окна, то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...
}