shineworld 73 Posted February 19 A native VCL, and not Windows-based, TComboBox cotrol. In one application I have hundreds and hundreds of TComboBoxes with values None and then from 1 to 256. I have used many strategies to enter values in these combos but the time required is enormous. The fastest system was to leave them empty, at FormCreate create the fields by hand by searching all TComboBox components ...... and on some PCs it takes almost 3 or more seconds.... // creates components hash and binds component events to code and adds inputs/outputs items C := ComponentCount - 1; for I := 0 to C do begin Component := Components[I]; FComponentsHashManager.Add(Component); if Component is TComboBox then begin with TComboBox(Component) do begin if Pos('InputNum', Name) <> 0 then begin SendMessage(Handle, WM_SETREDRAW, Ord(False), 0); SendMessage(Handle, CB_INITSTORAGE, I_O.MAX_DIGITAL_INPUTS + 1, COMBOBOX_MAX_TEXT_LENGTH * (I_O.MAX_DIGITAL_INPUTS + 1)); for J := 0 to I_O.MAX_DIGITAL_INPUTS do Items.Insert(J, InputsList.Strings[J]); SendMessage(Handle, WM_SETREDRAW, Ord(True), 0); Continue; end; if Pos('OutNum', Name) <> 0 then begin SendMessage(Handle, WM_SETREDRAW, Ord(False), 0); SendMessage(Handle, CB_INITSTORAGE, I_O.MAX_DIGITAL_OUTPUTS + 1, COMBOBOX_MAX_TEXT_LENGTH * (I_O.MAX_DIGITAL_OUTPUTS + 1)); for J := 0 to I_O.MAX_DIGITAL_OUTPUTS do Items.Insert(J, OutputsList.Strings[J]); SendMessage(Handle, WM_SETREDRAW, Ord(True), 0); Continue; end; end; end; ... Share this post Link to post
Lars Fosdal 1765 Posted February 19 Not sure why this was attached to a different thread, so I split it off, @shineworld 1 Share this post Link to post
JonRobertson 63 Posted February 19 20 minutes ago, Lars Fosdal said: Not sure why this was attached to a different thread, so I split it off Perhaps it was a request (in Delphi 13) for a new VCL combo box control that is not a subclass of the Win32API COMBOBOX class. 1 Share this post Link to post
Lars Fosdal 1765 Posted February 19 That's possible, @JonRobertson, but adding context to code is probably smart. Anyways, @shineworld should do a proper write-up and perhaps include a demo app with a comparison that shows the benefits of doing it his way, and post it on github and the QualityPortal when it comes back online. Share this post Link to post
Remy Lebeau 1330 Posted February 19 (edited) 2 hours ago, shineworld said: SendMessage(Handle, WM_SETREDRAW, Ord(False), 0); SendMessage(Handle, CB_INITSTORAGE, I_O.MAX_DIGITAL_INPUTS + 1, COMBOBOX_MAX_TEXT_LENGTH * (I_O.MAX_DIGITAL_INPUTS + 1)); for J := 0 to I_O.MAX_DIGITAL_INPUTS do Items.Insert(J, InputsList.Strings[J]); SendMessage(Handle, WM_SETREDRAW, Ord(True), 0); Some suggestions: don't use WM_SETREDRAW directly. Use the TComboBox.Items.(Begin|End)Update() instead, which will use WM_SETREDRAW internally for you. CB_INITSTORAGE helps, but simply don't waste time putting actual string values in so many TComboBoxes to begin with. Instead, fill them with blank strings, and set their Style property to csOwnerDrawFixed and use their OnDrawItem event to render the actual text whenever a drop-down list is visible. consider using a different UI control, such as a TListBox, or a TListView in vsReport mode. Both have true virtual modes that can handle a lot of items very quickly. Edited February 19 by Remy Lebeau 1 Share this post Link to post
Brian Evans 100 Posted February 19 (edited) Common to populate the list in OnDropDown. procedure TForm1.ComboBox1DropDown(Sender: TObject); begin ComboBox1.Items.BeginUpdate; Try ComboBox1.Items.Clear; ComboBox1.Items.Add('None'); for var I:integer := 1 to 256 do ComboBox1.Items.Add(IntToStr(I)); Finally ComboBox1.Items.EndUpdate; End; end; Can also clear the list in OnExit. procedure TForm1.ComboBox1DropDown(Sender: TObject); begin Var AComboBox := TComboBox(Sender); If AComboBox.Items.Count = 0 then begin AComboBox.Items.BeginUpdate; Try AComboBox.Items.Add('None'); for var I:integer := 1 to 256 do AComboBox.Items.Add(IntToStr(I)); Finally AComboBox.Items.EndUpdate; End; end; end; procedure TForm1.ComboBox1Exit(Sender: TObject); begin TComboBox(Sender).Items.Clear; end; Edited February 19 by Brian Evans 2 Share this post Link to post
Vincent Parrett 706 Posted February 19 5 hours ago, shineworld said: In one application I have hundreds and hundreds of TComboBoxes Perhaps a combo box isn't the best option here - a numeric input might be a better option - use 0 for none or create your own custom control. 1 Share this post Link to post
DelphiUdIT 145 Posted February 19 (edited) Seems that you populate your controls for an automation / building application. It' necessary to view the values with a combobox ? You can use a label (or other simple control) for every field and when a user click in one of them you can propose a combobox to change the present value or numeric keyboard or others custom control. Edited February 19 by DelphiUdIT 1 Share this post Link to post
shineworld 73 Posted February 20 I've used a TComboBox set as List because arguments start from None, 1, 2 .. 256 and use can fastly move to any using key: However, I guess I to move them to the Edit field and check the entered data as suggested. In the past I've used old SweedControls combobox made by zero with Delphi code but moving to 64bit the old and not supported library does not work fine. Share this post Link to post
Kas Ob. 109 Posted February 20 1 minute ago, shineworld said: I guess I to move them to the Edit field and check the entered data as suggested. Will work, but will need lot of details to be ugly. I am suggesting as Remy suggested, switch all the CobmoBoxes to custom draw (virtual mode) and solve this for good. Share this post Link to post
Pat Foley 51 Posted February 20 Here is a sample of runtime created controls that use same string. Plus assigns event handler. const InputPLC256 = 'x0,x1,x32,pin64,pin128,pin255,None'; procedure TForm1.Button4Click(Sender: TObject); begin for var I := 1 to 100 do begin var P := TPanel.Create(Self); with P do begin Name := 'TagInputEdit_' + I.ToString; SetBounds(280 - 5, 17 + I * 34, 204, 30); Parent := Self; Caption := ''; end; var L := TLabel.Create(Self); with L do begin SetBounds(80, 6, 64, 22); Parent := P; end; var CB := TComboBox.Create(Self); with CB do begin SetBounds(5, 3, 65, 22); Parent := P; Show; onchange := TagUpdater; Tag := NativeInt(L); items.CommaText := InputPLC256;//memo1.Lines; Text := Items[Random(items.Count)-1]; L.Caption := Text; end; end; end; procedure TForm1.TagUpdater(Sender: Tobject); begin var S := Sender as TComboBox; var L := TLabel(S.Tag); var N := S.Parent.Name; L.Caption := Format('%s %s', [N, S.Text]); end; 1 Share this post Link to post
Stano 143 Posted February 20 I will note that most programmers completely reject the "with" construct. 3 Share this post Link to post
JonRobertson 63 Posted February 20 27 minutes ago, Stano said: I will note that most programmers completely reject the "with" construct. As does the Delphi debugger. 3 Share this post Link to post
Remy Lebeau 1330 Posted February 20 14 hours ago, shineworld said: I've used a TComboBox set as List because arguments start from None, 1, 2 .. 256 and use can fastly move to any using key: You say you have hundreds of TComboBoxes, but the screenshot above only shows 7 (presumably 11?). If this screen is similar for the rest of the app, where each item on the side list displays its own page with just a handful of ComboBoxes on it, then a simple solution would be to NOT load every page at one time, but instead to load one page at a time only when it is made visible to the user, and then unload it when it is no longer visible to the user. That will greatly speed up your load times, and then you don't have to resort to using hacks like owner-drawing, OnDropDown refreshes, etc. You can move each page to its own TFrame that you create and destroy when needed, that way you still regain design-time support for your page layout. 4 Share this post Link to post
John R. 17 Posted February 22 Alternatively, your UI seems to be very similar to a table/grid layout so you could potentially use a grid component with support to input fields such as combo boxes. Those grids are usually highly optimized to handle thousands of rows with various data types and input options. They usually allow the dynamic instantiation of input controls (such as combo boxes) when a row is actually being edited to save resources. I highly recommend DevExpress' ExpressQuantumGrid: https://docs.devexpress.com/VCL/171093/ExpressQuantumGrid/vcl-data-grid Or their ExpressVerticalGrid: https://docs.devexpress.com/VCL/403720/ExpressVerticalGrid/concepts/vcl-vertical-grid Both of which I extensively use for this kind of tasks. 2 Share this post Link to post