Is there any way to get all the controls on a container control?

被刻印的时光 ゝ 提交于 2019-11-27 03:17:01

问题


I've got a form with a bunch of controls on it, and I wanted to iterate through all the controls on a certain panel and enable/disable them.

I tried this:

var component: TComponent;
begin
  for component in myPanel do
    (component as TControl).Enabled := Value;
end;

But that did nothing. Turns out all components are in the form's component collection, not their parent object's. So does anyone know if there's any way to get all the controls inside a control? (Besides an ugly workaround like this, which is what I ended up having to do):

var component: TComponent;
begin
  for component in myPanel do
    if (component is TControl) and (TControl(component).parent = myPanel) then
      TControl(component).Enabled := Value;
end;

Someone please tell me there's a better way...


回答1:


You're looking for the TWinControl.Controls array and the accompanying ControlCount property. Those are for a control's immediate children. To get grandchildren etc., use standard recursive techniques.

You don't really want the Components array (which is what the for-in loop iterates over) since it has nothing to do, in general, with the parent-child relationship. Components can own things that have no child relationship, and controls can have children that they don't own.

Also note that disabling a control implicitly disables all its children, too. You cannot interact with the children of a disabled control; the OS doesn't send input messages to them. To make them look disabled, though, you'll need to disable them separately. That is, to make a button have grayed text, it's not enough to disable its parent, even though the button won't respond to mouse clicks. You need to disable the button itself to make it paint itself "disabledly."




回答2:


If you disable a panel, al controls on it are disabled too.

Recursive solution with anonymous methods:

type
  TControlProc = reference to procedure (const AControl: TControl);

procedure TForm6.ModifyControl(const AControl: TControl; 
  const ARef: TControlProc);
var
  i : Integer;
begin
  if AControl=nil then
    Exit;
  if AControl is TWinControl then begin
    for i := 0 to TWinControl(AControl).ControlCount-1 do
      ModifyControl(TWinControl(AControl).Controls[i], ARef);
  end;
   ARef(AControl);
end;

procedure TForm6.Button1Click(Sender: TObject);
begin
  ModifyControl(Panel1,
    procedure (const AControl: TControl)
    begin
      AControl.Enabled := not Panel1.Enabled;
    end
  );
end;



回答3:


Here is a Delphi 2007 way:

procedure TForm6.ModifyControl(const AControl: TControl; const value: Boolean);
var
  i: Integer;
begin
  if AControl=nil then Exit;
  if AControl is TWinControl then begin
    for i := 0 to TWinControl(AControl).ControlCount-1 do
      ModifyControl(TWinControl(AControl).Controls[i], value);
  end;
  Acontrol.Enabled := value;
end;

procedure TForm6.Button1Click(Sender: TObject); 
begin 
  ModifyControl(Panel1, true);  // true or false
end;



回答4:


Simply

Panel.Enabled := Value;



回答5:


This one finds all controls, also nested in frames etc, and points to them via the list. Be aware to free the list afterwards.

Function AllControls(form : tForm) : tList<tControl>;
Procedure Add(Control : tControl );
var i : integer;
begin
  if Control is TWinControl then
  with TWinControl(Control) do
  for i := 0 to Controlcount-1 do
    Add(Controls[i]);

    if Control <> form  then
    result.Add(Control);
  end;
 begin
   result := tlist<tControl>.create;
   add(form);
 end;

 var contrls : tlist<tcontrol>;
     c : tcontrol;
 begin
    try
      contrls := AllControls(form1);
      for c in ctrls do Visit(c);  // Do something
    finally
      contrls.free;
    end;
 end;

And if you want a generic version, where you can ask for a specific control type, you can use this:

Procedure TForm1.Addcontrols( control : tcontrol; list : tlist<tcontrol>);
var i : integer;
begin
  if control is twincontrol then
  with twincontrol(control) do
    for i := 0 to controlcount-1 do
    addControl(controls[i], list);
    list.Add(control)
end;

Function TForm1.GetControls<T>(f : tform) : tlist<T>;
var list : tlist<tcontrol>;
    c : tcontrol;
begin
  list := tlist<tcontrol>.Create;
  addControls(f, list);
  result := tlist<t>.create;
  for c in list do
  if c <> f  then
  if c is t then
  result.Add(c);
  list.free;
end;

procedure TForm1.FormCreate(Sender: TObject);
VAR List : TList<TRadioButton>;
begin
  List := GetControls<TRadioButton>(self);
end;

end.

Use

  List := GetControls<TControl>(self);

to get all controls..




回答6:


I know this post is a little old but I came here based on a search for the same information. Here is some C++ code that I worked out for anyone interested.

// DEV-NOTE:  GUIForm flattens the VCL controls
// VCL controls are nested.  I.E. Controls on a
// Panel would have the Panel as a parent and if
// that Panel is on a TForm, TForm's control count
// does not account for the nested controls on the
// Panel.
//
// GUIControl is passed a Form pointer and an index
// value, the index value will walk the controls on the
// form and any child controls counting up to the idx
// value passed in.  In this way, every control has a
// unique index value
//
// You can use this to iterate over every single control
// on a form.  Here is example code:
//
// int count = 0;
// TForm *pTForm = some_form
// TControl *pCtrl = 0;
// do
// {
//      pCtrl = GUIControl(pTForm, count++);
//
// }while(pCtrl);

TControl *GUIControl(TForm *F, int idx)
{
    TControl *rval = 0;
    int RunCount = 0;

    for(int i=0; i<F->ControlCount && !rval; i++)
    {
        TControl *pCtl = F->Controls[i];

        if(RunCount == idx )
            rval = pCtl;
        else
            rval = GUIChildControl( pCtl, RunCount, idx);

        RunCount++;
    }

    return(rval);
}

TControl *GUIChildControl(TControl *C, int &runcount, int idx)
{
    TControl *rval = 0;
    TWinControl *pC = dynamic_cast<TWinControl *>(C);
    if(pC)
    {
        for(int i=0; i<pC->ControlCount && !rval; i++)
        {
            TControl *pCtrl = pC->Controls[i];
            runcount++;

            if( runcount == idx)
                rval = pCtrl;
            else
            {
                TWinControl *pCC = dynamic_cast<TWinControl *>(pCtrl);

                if(pCC)
                {
                    if( pCC->ControlCount )
                        rval = GUIChildControl(pCtrl, runcount, idx);
                }
            }
        }
    }

    return(rval);
}


来源:https://stackoverflow.com/questions/414928/is-there-any-way-to-get-all-the-controls-on-a-container-control

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!