¿Cómo crear dinámicamente controles alineados con la parte superior pero después de otros controles alineados?

En este caso particular, estoy usando la biblioteca PowerPDF para construir dinámicamente un documento PDF, pero lo mismo aplica para el concepto general de alinear dinámicamente controles secuencialmente dentro de un control padre. En esta biblioteca, TPRPage es el control base que contiene todos los controles de elementos, en este caso, instancias secuenciales de TPRLayoutPanel .

Lo que hago cuando dinámicamente agrego controles:

  1. Crear un control ( TPRLayoutPanel )
  2. Establecer el padre del control ( TPRPage )
  3. Alinee el control hacia arriba ( PRLayoutPanel.Align:= alTop; )

El problema es que se fuerza desde el principio (arriba) en lugar del final (abajo) de la página.

Intenté configurar su orden PRLayoutPanel.SendToBack; o PRLayoutPanel.BringToFront pero sin suerte.

¿Cómo puedo crear dinámicamente y alinear controles múltiples dentro de un control padre secuencialmente? Mi única solución actual es agregar los controles en orden inverso (de principio a fin), lo cual es ridículamente innecesario.

Aquí está mi función universal que crea cada instancia nueva de un control alineado en este elemento primario:

 function TfrmReport.InsertPanel: TPRLayoutPanel; begin Result:= TPRLayoutPanel.Create(PRPage); Result.Parent:= PRPage; Result.Align:= alTop; Result.Height:= 40; //Default, may change later end; 

Debe configurar la propiedad Top para que esté en la parte inferior del panel anterior. Por ejemplo, así:

 PanelTop := 0; for i := 0 to 5 do begin Panel[i] := TPanel.Create(Self); Panel[i].Parent := Self; Panel[i].Height := PanelHeight; Panel[i].Align := alTop; Panel[i].Top := PanelTop; inc(PanelTop, PanelHeight); end; 

Para adaptarlo a su código, debe realizar un seguimiento de la ubicación del panel agregado más recientemente. Quizás podría agregar un parámetro var a su función InsertPanel :

 function TfrmReport.InsertPanel(var PanelTop: Integer): TPRLayoutPanel; begin Result:= TPRLayoutPanel.Create(PRPage); Result.Parent:= PRPage; Result.Top:= PanelTop; Result.Height:= 40; Result.Align:= alTop; inc(PanelTop, Result.Height); end; 

¡Confío en que entiendas la idea!

Una vez más, DisableAlign y EnableAlign al rescate:

 procedure TForm1.FormCreate(Sender: TObject); var I: Integer; P: TPanel; begin DisableAlign; try for I := 0 to 4 do begin P := TPanel.Create(Self); P.Caption := IntToStr(I); P.Align := alTop; P.Parent := Self; end; finally EnableAlign; end; end; 

Explicación:

Cuando la alineación está habilitada , cada adición de un control a un contenedor (el formulario en sí en este caso específico) volverá a evaluar todas las configuraciones de alineación (y anclaje) de todos los otros controles dentro de ese contenedor. En caso de que el control no tenga un conjunto de propiedades Top específico, Top será 0 . Cuando ya hay otro control alineado a la parte superior, hay dos controles con Top = 0 , y el que está a punto de insertarse gana. Yo (actualmente) no tengo una explicación en profundidad para eso, pero simplemente lo es, y el orden de posición realmente se revierte del orden de creación.

Ahora, cuando la alineación del contenedor está desactivada , los controles agregados consecutivos simplemente se insertan con todas sus propiedades de posicionamiento inalteradas. Cuando la alineación se habilita de nuevo, todos los controles se vuelven a evaluar de la misma manera, con la diferencia de que esto tiene lugar en un solo bucle en el orden del índice en la matriz Controls ; es decir, el orden en que fueron creados.

Puede utilizar el tipo de alineación AlCustom y controlar todas las posiciones de sus paneles mediante el método CustomAlignPosition (deberá anularlo en control principal). Esto le dará más flexibilidad y control.