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.
Related
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.
Essentially, I want a user to add comments in the form of labels. I have implemented this no problem, but I want the user to be able to delete the newly created labels.
comment := TLabel.Create(Self);
comment.Parent := Form1;
I want to add Labels during runtime, and then fill a combobox or a stringgrid or something similar with the name or any sort of a reference to that label, so that the user can then delete that label.
How would I get the name of the newly created label for a reference ?
Thanks for any help.
Components that you create yourself don't have a Name assigned unless you explicitly assign one yourself. It is the IDE that synthesizes a Name when you drop a component on the form at design-time.
You can just assign whatever Name you feel like, as long as it's a valid component name (unused, no spaces, and so on), and then the component can be found via its Owner.FindComponent() method, if you don't keep track of the component yourself. Otherwise, put the component into your own list/array so you can find it later.
The name of a component can be read from its Name property:
comment.Name
You can write to that property to give the component a name that is amenable to showing to users. You'll want to do that when you create a new component dynamically because it won't have a name until you name it.
There's no need for you to use the Name property if you don't wish to. You can use naming of your own choice. For instance you may wish to use names that contain spaces. Or not be constrained from using names that are already in use by the static controls. Hold the components in a dictionary with the name as the key and the component as the value. My advice is that you go down this second path.
How to write a simple component that never allows it to drop multiple time on the form?
let say, I already drop my TMyComponent on the form and if i will drop another TMyComponent it notifies 'Component already exist!' and will cancel the dropping of the component on the form.
The components Constructor takes an Owner parameter: that'll normally be your Form (or DataModule). Simply walk the list of Owner.Components and see if there's an other one of yours. If there is, raise an Exception.
This would block your component from being added, both at design time and at runtime.
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).
I am starting to learn Delphi. So I decided to write an application like MS Excel from scratch. In a new Form1, I did put a TPageControl component containing only 1 page. In that page, I did put a TAdvStringGrid and a TPanel with some buttons (button1, button2) and a Popup1 menu for defining some actions on the grid, like copy cell, copy row, delete row, etc. For that StringGrid, also, I have defined some properties, like colors, fonts, etc. I added a toolbar with a button to the main form, in order to add more pages to the PageControl. The OnClick method of that button defines two actions:
1) to add a new Page2 to the PageControl, and
2) to add a new StringGrid In the new created Page2 .
That new (runtime defined) StringGrid created in a new Page of a Tpagecontrol should inherite (get, copy, clone, duplicate) the properties and methods of the StringGrid parent already defined in the first page at design time, and should be able to call the PopUp1 menu just like the StringGrid parent. How we do this?
At the beginning, I thought I just should copy the StringGrid properties using assing(), but when using this approach, the popup menu does not pop up when right mouse clicking on the new StringGrid... and the buttons (button1 and button2) of Form1 only work with the first StringGrid but not with the new added one. I did read somewhere that in order to solve this problem, I could duplicate the StringGrid component by using the write and read TMemoryStream (save the parent stringgrid into a memorystream, create a new stringgrid and then read that memorystream into the new created stringgrid), so I did, but when the program executes this component cloning method, I get a runtime error. :-(
I did carefully chech the Help. Nothing found on that topic. Seems there is not any Delphi component library or third party components that cope with this kind of task. Can anybody help, please? :o)
I would use an tabcontrol instead of an pagecontrol. That way, you would end up with multiple tabs but only one page and grid. I would then make some kind of data structure to keep all my cell information in, and render this structure to the grid. This way, I can have multiple structures, and let the active tab decide which structure to render. You will also end up with a looser coupling between your gui and your logic, making it easier change things later. E.g. if you need to bring in some values form a different spreadsheet into a cell in the current spreadsheet, you can load up a structure and pick out the wanted values. No need to make any gui for the second spreadsheet at all.
For a 3.party component, I will recommend TMS FlexCell and TAdvSpreadGrid. That will give you most of what you need.
A tricky choice for a learner :) however you do not need to start streaming things.
Look up the assign() procedure for TPersistent this is the routine you need to copy parts of the grid easily. For example
for i := 0 to StringGrid1.RowCount - 1 do
StringGrid2.Rows[i].Assign(StringGrid1.Rows[i]);
for an easy start differentiate your grids with the Tag property(StringGrid1.Tag := 1, StringGrid2.Tag := 2 Etc.
The popup menu is pretty simple too:
StringGrid2.popupmenu := stringGrid1.popupMenu But then then you must decide in the Popup Routine Which Grid is "Active" some thing l like
Tform1.popupMenuItem1Click(Sender: TObject)
if Sender is TStringGrid then
Case TStrigngGrid(Sender).Tag of
1: // Grid 1
2: // Grid 2
You can use the same simple logic with the buttons.
As neftali mentioned the best thing would be to put the created grids in an ObjectList.
You would then end up with the slightly more complex but expandabe:
Tform1.popupMenuItem1Click(Sender: TObject)
var AGrid: TStringGrid;
if Sender is TStringGrid then
AGrid := MyListOfStringGrids[MyListOfStringGrids.IndexOf(Sender)];
DoMenuItem1Stuff(AGrid);
Have fun
"...and the buttons (button1 and button2) of Form1 only work with the first StringGrid but not with the new added one. I did read somewhere that in order to solve this problem..."
There is no generic method for solving this. Delphi offers different tools to solve it.
You can create a list of Objects (TObjectList) that containts all the StringGrid; At Button1 Click event you must search what is the grid that you are using at this moment. For example (BIS for the other buttons):
var
index:integer;
sg:TStringGrid;
begin
...
// search the active page
index := pageControl.ActivePageIndex; //0, 1, 2,...
// USe this for search the StringGrid
sg := TStringGrid(OList.Objects[index]);
// the code that you have at the event bus woking with sg
// not stringgrid1, stringgrid2,...
...
sg.Color :=
...
If you don't want use ObjectList, there are alternatives. You can use Tag property for all StringGrids. Assign 0, 1, 2, 3,...
Extract the index (active page) and search the TStringGrid that have the property Tag with the same value. You can do this with FindComponent. The first methos is better. ;-)
Regards.
P.D: Excuse for my bad english.
Dear all, I am trying to learn Delphi
The Delphi style is to find/create/buy a component that does the job and use them in the design-time. You could try making a custom component based on a grid or use TFrame. See links from Custom Component Development and help files that comes with Delphi.
If you really need to clone the control dynamically, here's an example I found that uses stream.ReadComponent.