Is there a ComboBox that has Items like a TcxRadioGroup? - delphi

The TcxRadioGroup component of DevExpress has a very nice way to specify items. You can specify a Caption and a Value (and a Tag) for each TcxRadioGroupItem.
The TcxComboBox and the normal TComboBox of Delphi on the other hand use TStrings to store its items.
While TStrings can have a Name and an Object, there is no easy way to hook up a name and a value using the form designer of the Delphi IDE.
Is there a ComboBox control (preferably from DevExpress) that allows to visually design its items with a Caption and a Value?
PS: I'm not looking for a DB aware control.

Try a TcxImageComboBox. See here - you don't have to assign images despite the name. You can also edit the items visually.
(I use it as cell editor in cxGrids because of the separation Description/Value.)

Raize Components have TRzComboBox which introduces a Values property as an addition to the existing Items.

ESBPCS for VCL has an enhanced Lookup ComboBox. It stores 2 Lists, the ones normally in TCombobox's Items as well as the new Values list. These two StringLists are in a 1-1 relationship. Use AsItem to retrieve the string currently displayed and AsValue to retrieve the "related" string from Values.

Use a standard Delphi TComboBox, it can store a string (for visualization, and an object of any TObject descendant that you implement yourself, i.e you can store anything associated to a string in the dropdown).

Related

Delphi - comprehensive list of properties of object

For some particular object, I want the equivalent of copying (as text) the list of properties and events that appears in Delphi's Object Inspector.
My purpose is to be able to paste that into a spreadsheet, to be able to add notes to each item, add categorizations of properties that pertain to related functionality, or to compare to other objects (for example to align with inheritance ancestor or descendant).
So far as I know, Object Inspector doesn't have such a copy feature. So what's an alternative quick way to achieve this goal?
For what it's worth, I have Delphi's 1 through 7, 2007, XE, XE2, and Tokyo (of the latter, only Starter).
Clarification based on first few comments:
I do already know that items appearing on Object Inspector is the class's published properties, hence the information can be retrieved from the source files. However, the published properties may be distributed across multiple classes, and indeed multiple source files (due to inheritance), and the items themselves are not in a particularly convenient format. All surmountable. I simply hoped for a faster easier method, given that the Object Inspector's display is already so close to what I was looking for.
Since I don't have enough reputation to comment, I am writing this as an answer.
I think gwideman is asking for a way to copy inside Delphi's IDE, and without any coding with RTTI.
To an extend this is possible. What you need to do is simply select an object and copy (Ctrl-C). After that you can paste it to any text editor or even Excel. It should be something like this:
object Button1: TButton
Left = 60
Top = 510
Width = 80
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Save'
Enabled = False
TabOrder = 0
OnClick = Button1Click
end
Notice that even the event handlers are included.
You may also notice that the list is rather short. This is because properties that have the default values are skipped. This can be a problem if you need all properties. But if you just want to comment your settings this saves time and is the best.
If you need the whole list of published properties, you can easily get it in Delphi's help. Like TSpeedButton.
Finally, if you right-click on the form and click "View as Text", you can get the properties of the form and all its objects.

How to refence a DBGrid from another form

I have a mainForm with a DBGrid and I have a second form with a CheckListBox that shows all of the DBGrid columns for the user to choose. I need to reference in Form2 the DBGrid that I have in MainForm.
I would like this second form to handle all of the procedures connected to the dbdgrid columns , so that I can reuse it easily.
That was the idea, but I dod'nt find the way to pass the DBGrid reference.
Is it possible ?
Answering the question you asked, on your Form2, define a property
TForm2
[...]
private
FGrid : TDBGrid
public
property Grid : TDBGrid read FGrid write FGrid;
Then, after you've created an instance of TForm2, just do
Form2.Grid := MainForm.DBGrid1;
Then, on Form2, you can do anything valid you like to change Grid and the changes will be made to MainForm.DBGrid1.
Is it possible?
The question should rather be Is there a better way to achieve what I want?
Would it be maintainable if Form2 worked basically with a control from a different form? What if other forms would also need to hold references to components on other forms?
How hard would it be in a year to find a bug if controls are used over different forms?
Would such a solution match to the SOLID principles?
Answering these questions should help you to look for a different approach.
You should consider to separate UI and business logic. A TDBGrid seems to be a convenient way to get data from a database into your application but it violates the Single Responsibility Principle since it loads and displays data at the same time. Don't use it as a basic data provider inside your application. Perform the SQL queries from a deeper UI independant layer of your software. Store the results in containers and display them in all the ways you want in your different forms.

Delphi TComboBox Dropdown fields filtering [duplicate]

Everyone probably knows what I mean, but to clarify the control would need to:
Fire an event when user edits the text. The event would provide a SuggestionList: TStrings which you could fill with matches/suggestions.
if the SuggestionList is not empty a drop down should appear.
Unlike combo, the control should not attempt to automatically select/auto complete or otherwise affect the editing.
So, is there a Delphi edit/combo control that works like that ?
Use the autocompletion feature built in to all Windows edit controls.
First, fill your TStrings object however you want. Then use GetOleStrings to create a TStringsAdapter to wrap it. (The adapter does not claim ownership of the TStrings object, so you must make sure you don't destroy it while the adapter is still live.) The adapter gives you an IStrings interface, which you'll need because the autocompletion feature requires an IEnumString interface to provide the completion matches. Call _NewEnum for that.
Next, call CoCreateInstance to create an IAutoComplete object. Call its Init method to associate it with the window handle of your edit control. If you're using a combo box, then send it a cbem_GetEditControl message to find the underlying edit window.
You can stop at that point and autocompletion should work automatically. You can disable autocompletion if you want, or you can set any number of autocompletion options.
You say you don't want autocompletion, but in the OS terminology, I think what you really don't want is called auto append, where the remainder of the string is entered into the edit box automatically as the user types, but selected so that further typing will overwrite it, and the user needs to delete the excess text if the desired value is shorter than one of the matches.
There is also auto suggest, which displays a drop-down list of suggestions.
You can enable either or both options. You don't need to filter the list of suggestions yourself; the autocomplete object filters the IEnumString list by itself.
You can use standard TComboBox and faststrings library (for stringMatches() function).
procedure TForm1.cbChange(Sender: TObject);
var
s:Integer;
tmpstr:string;
begin
//suggestions: tstringlist
cb.AutoComplete:=false;
tmpstr:=cb.Text;
cb.Items.Clear;
for s:=0 to suggestions.Count - 1 do
if StringMatches(suggestions[s],cb.Text+'*') then
cb.Items.Add(suggestions[s]);
cb.DroppedDown:=(cb.Items.Count<>0) and (Length(cb.Text)<>0);
cb.Text:=tmpstr;
cb.SelStart:=Length(cb.Text)
end;
If you just want to show a file or url list:
SHAutoComplete(GetWindow(eb_MyComboBox->Handle, GW_CHILD), SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_DIRS);
I first implemented this feature like Rob described it in his answer. Later I saw that TComboBoxEx has the property AutoCompleteOptions where I set acoAutoSuggest to True and acoAutoAppend to False. The ComboBox now filters its item list when doing some entry and shows the matching items.
I'm using RAD Studio 10 Seattle and XE2 but don't know if this feature is available in older versions.
To the last bit of your question: "So, is there a Delphi edit/combo control that works like that ?":
A bit late to the party but yes, I have written a free and open source component that implements the Google Place Autocomplete and Google Place Details API's:
It does inherit from the standard TComboBox but you can modify the code to work with any TEdit
https://carbonsoft.co.za/components/
or
https://github.com/RynoCoetzee/TRCGPlaceAutoCompleteCombo

Searching through form & Uses

We are creating a component and want to mimic the concept behind Designer.GetComponentNames, where as we give can obtain a list of the available components on the form or any form in the uses. We haven't been able to get to the root of GetComponentNames. Any input would be much appreciated.
LE: Actually I take that back. I need this from the design time aspect.
Runtime? You have the Vcl.Forms.TScreen.Forms array for all the displayed forms, and you have Vcl.Forms.Application.Components containing all your forms IIRC. Then, each form has a Components array.
If I understand the first part of your question, you want to get a list of components owned by a form (by name) at designtime.
As background, I have a non-visual component (Call it TColorEdits.) that manages the colors of selected TWinControls on a form at runtime. This component has a TStrings property that contains the names of selected TWinControls on the form. The names of TWinControls to be managed may be selected at designtime using a dialog (dlgEditColors) that contains a couple listboxes, one of which is named DstList and shows all TWinControls available for management by TColorEdits.
So, here is some (simplified) code I use to get the names of TWinControls on a form at designtime and load the TWinControl names into DstList.
{ Load names of TWinControls owned by a form into TListBox DstList }
for i := 0 to TColorEdits(GetComponent(0)).Owner.ComponentCount - 1 do
if ((TColorEdits(GetComponent(0)).Owner.Components[i] is TWinControl) then
dlgEditColors.DstList.Items.Add(TColorEdits(GetComponent(0)).Owner.Components[i].Name);
You should be able to adjust the above code as part of a custom property editor for your component. Hope this helps with the first part of your question.

AutoID Field in Many-to-Many, Insert Using TDataSet / Query (Delphi / MSAccess)

I have a database of "Items", which are assigned to multiple "Categories". Categories can have multiple items, and vice versa. The relevant portion of the database structure is as follows:
[tblItem]
ItemID (AutoNumber)
MainText (Text)
[tblCategory]
CategoryID (AutoNumber)
Name (Text)
[tblItemCategory]
ItemID (Long Integer)
CategoryID (Long Integer)
I want to build a panel component which shows a category name at the top, with a databound grid of items belonging to that grid below. There will be many instances of this panel component, and the end-user should be able to create a new item and simultaneously assign it to the category in question from any one of them.
In MS Access, it's possible to create a nested form, with the "child" one databound to a query which is "MasterFields" linked to a databound "Category" field on the "parent" form, such that the grid of items changes as the Category field is changed. This Items grid can also easily have new records added to it, with both the ItemID (in tblItem AND table tblItemCategory) and the linked CategoryID field (in tblItemCategory) being populated automatically.
The query for that Access form's grid is:
SELECT tblItemCategory.CategoryID, tblItem.*
FROM tblItemCategory LEFT JOIN tblItem ON tblItemCategory.ItemID = tblItem.ItemID
ORDER BY tblItemCategory.CategoryID;
If I try the same thing in Delphi, the ItemID AutoNumber field doesn't get populated, resulting in the following error:
..exception class EOleException with message 'The field 'tblItemCategory.ItemID' cannot contain a Null value because the Required property for this field is set to True. Enter a value in this field'.
..and the ItemID field is accordingly blank in the grid.
Is there a way to get Delphi/ADO to handle the behind-the-scenes two-table ItemID population as easily/neatly as Access does, without manually handling it programmatically? If not, what's the best/most elegant way to handle it programmatically?
I'd like to keep whatever solution I end up with as closely tied to the conventional TDataSet / TDataSource approach as possible, as I use a number of different kinds of databound controls, all of which will have to deal with this same data structure.
(Note: I'm using Delphi 2007 and an MSAccess 2000 format MDB file.)
Pretty much the same way. Theres's master source and master fields properties, so you just chain detail to master.
So mastersource would be Customer, Detail Source, Orders linked by CustomerID.
Dead easy to show this, but hard to explain.
Some other bloke wrote it all out though.
Master Detail Forms And Delphi
If you want to add new records to the tables via the grid, then you will have to use the BeforePost method of the underlying query in order to obtain the new key, which you then insert manually into the link table along with the category id.
I admit that I never write code with editable (and insertable) grids so my answer may need a little tweaking.

Resources