Более быстрый способ создания 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Это немного:

  1. не делайте 512 копий одного и того же TStringList содержание. Все, что вы добавляете в TComboBox::Items хранится внутри TComboBoxв памяти. Вы должны стремиться повторно использовать свой сингл TStringList и пусть все делегируют ему по мере необходимости. В этом случае вы можете установить TComboBox::Style собственность на csOwnerDrawFixed и использовать TComboBox::OnDrawItem событие, чтобы нарисовать TStringList Строки по запросу. Вам все еще нужно добавить строки к каждому TComboBox, но они могут быть пустыми строками, по крайней мере.

  2. подкласс 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...
}
Другие вопросы по тегам