2017-09-12 1 views
3

Sie müssen viele Elemente (mehr als 10k) in TComboBox hinzufügen (ich weiß, dass TComboBox nicht viele Elemente enthalten soll, aber es ist nicht meine Aufgabe, dies zu ändern), ohne Duplikate hinzuzufügen. Also muss ich die vollständige Liste vor dem Hinzufügen durchsuchen. Ich möchte TComboBox.items.indexof vermeiden, da ich eine binäre Suche benötige, aber die binäre Suche in TStrings nicht verfügbar ist.Effizientes Auffüllen der Combobox in Delphi

Also habe ich eine temporäre Tztringlist erstellt, nach True sortiert und verwendet gefunden. Aber jetzt die temporäre TStringList Zuordnung zurück zu TComboBox.Items

(myCB.Items.AddStrings(myList)) 

ist wirklich langsam, wie es kopiert die ganze Liste. Gibt es eine Möglichkeit, die Liste zu verschieben, anstatt sie zu kopieren? Oder eine andere Möglichkeit zum effizienten Befüllen meiner TComboBox?

+1

Einfach das zugrunde liegende Problem beheben. 10k Elemente in einer Combo-Box ist lächerlich. Welche Version von Delphi? –

+0

@TomBrunberg Assign ist noch langsamer als AddStrings – siwmas

+0

@J ... Zustimmen, dass 10k ist lächerlich, aber das ändert nichts daran, dass das Hinzufügen von Elementen zu Combobox für ein Szenario wie diese sieht wirklich ineffizient? Es sei denn, ich vermisse etwas .. Delphi-Version -> XE3 – siwmas

Antwort

2

Wie Rudy Velthuis bereits in den Kommentaren erwähnt und vorausgesetzt, Sie VCL verwenden, die CB_INITSTORAGE Nachricht eine Option sein könnte:

SendMessage(myCB, CB_INITSTORAGE, myList.Count, 20 * myList.Count*sizeof(Integer)); 

wo 20 ist Ihre durchschnittliche String-Länge.

Ergebnisse (auf i5-7200U und 20K Einzelteile mit gelegentlicher Länge betwen 1 und 50 Zeichen):

  • ohne CB_INITSTORAGE: ~ 265ms
  • mit CB_INITSTORAGE: ~ 215ms

So Während Sie die Dinge ein wenig beschleunigen können, indem Sie den Speicher vormerken, scheint das größere Problem die schlechte Benutzererfahrung zu sein. Wie kann ein Benutzer das richtige Element in einer Combobox mit so vielen Elementen finden?

+0

Verwendet eine [TStopWatch] (http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Diagnostics.TStopwatch), um die Zeiten zu messen. – ventiseis

6

Es gibt keine Möglichkeit, die Liste in das Kombinationsfeld zu verschieben, da der Speicher der Kombinationsbox zur internen Windows-Steuerungsimplementierung gehört. Es kennt keine Möglichkeit, Ihr Delphi TStringList Objekt direkt zu verbrauchen. Alles, was es anbietet, ist ein Befehl, um einen Eintrag zu der Liste hinzuzufügen, welcher TComboBox dann verwendet, um jedes Element aus der Zeichenfolgenliste nacheinander in die Systemsteuerung zu kopieren. Die einzige Möglichkeit, das Kopieren der vielen tausend Elemente in das Kombinationsfeld zu vermeiden, besteht darin, das Problem vollständig zu vermeiden, z. B. indem Sie ein anderes Steuerelement verwenden oder die Anzahl der hinzuzufügenden Elemente reduzieren.

Eine Listenansicht hat einen "virtuellen" Modus, in dem Sie nur angeben, wie viele Elemente es haben sollte, und dann ruft es zurück zu Ihrem Programm, wenn es Details über das, was auf dem Bildschirm sichtbar ist, wissen muss. Elemente, die nicht sichtbar sind, belegen in der Implementierung der Listenansicht keinen Platz, sodass Sie das Kopieren vermeiden. Jedoch system combo boxes don't have a "virtual" mode. Möglicherweise können Sie ein Steuerelement von Drittanbietern finden, das diese Fähigkeit bietet.

Die Anzahl der Elemente zu reduzieren, die Sie in das Kombinationsfeld einfügen müssen, ist die nächstbeste Option, aber nur Sie und Ihre Kollegen verfügen über das erforderliche Domänenwissen, um den besten Weg zu finden.

+1

Es ist wahr, dass die 'TComboBox' der VCL keinen virtuellen Modus hat, aber es hat Besitzer-Zeichen-Modi, so dass Sie nicht die tatsächlichen Zeichenfolgen darin laden müssen, Sie könnten einfach leere Platzhalter laden und dann die Zeichenfolgen zeichnen tatsächliche Strings, wenn nötig. Ansonsten ist eine ComboBox nur eine verklärte Edit + ListBox-Kombination, die Sie manuell in einem benutzerdefinierten 'TForm' simulieren können, und' TListBox' hat einen virtuellen Modus. –

3

Ungeachtet dessen, dass 10k Elemente in einer TComboBox verrückt ist, wäre eine effiziente Strategie hier, einen Cache in einem separaten Objekt zu halten. Zum Beispiel erklären:

{ use a TDictionary just for storing a hashmap }   
    FComboStringsDict : TDictionary<string, integer>; 

wo

procedure TForm1.FormCreate(Sender: TObject); 
var 
    i : integer; 
    spw : TStopwatch; 
begin 
    FComboStringsDict := TDictionary<string, integer>.Create; 
    spw := TStopwatch.StartNew; 
    { add 10k random items } 
    for i := 0 to 10000 do begin 
    AddComboStringIfNotDuplicate(IntToStr(Floor(20000*Random))); 
    end; 
    spw.Stop; 
    ListBox1.Items.Add(IntToStr(spw.ElapsedMilliseconds)); 
end; 

function TForm1.AddComboStringIfNotDuplicate(AEntry: string) : boolean; 
begin 
    result := false; 
    if not FComboStringsDict.ContainsKey(AEntry) then begin 
    FComboStringsDict.Add(AEntry, 0); 
    ComboBox1.Items.Add(AEntry); 
    result := true; 
    end; 
end; 

10k Artikel zunächst dauert ca. 0,5 s auf diese Weise hinzufügen.

{ test adding new items } 
procedure TForm1.Button1Click(Sender: TObject); 
var 
    spw : TStopwatch; 
begin 
    spw := TStopwatch.StartNew; 
    if not AddComboString(IntToStr(Floor(20000*Random))) then 
    ListBox1.Items.Add('Did not add duplicate'); 
    spw.Stop; 
    ListBox1.Items.Add(IntToStr(spw.ElapsedMilliseconds)); 
end; 

Aber das Hinzufügen jedes folgenden Elements ist sehr schnell < 1ms. Dies ist eine plumpe Implementierung, aber Sie können dieses Verhalten problemlos in eine benutzerdefinierte Klasse umwandeln. Die Idee besteht darin, Ihr Datenmodell so weit wie möglich von der visuellen Komponente getrennt zu halten - halten Sie sie beim Hinzufügen oder Entfernen von Elementen synchron, führen Sie jedoch umfangreiche Suchen im Wörterbuch durch, in dem die Suche schnell ist. Das Entfernen von Elementen würde sich immer noch auf .IndexOf verlassen.

Verwandte Themen