Siempre se puede crear la forma en la forma. No es la solución más feliz, pero funciona.Que creo la mejor manera de resolver este problema sería mediante la utilización de GDI + o D2D, pero, por desgracia, no podía entenderlo, así que fui con el enfoque de "forma en forma":
forma en capas:
unit uLayeredForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.Types,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Imaging.PngImage;
type
TfrmLayered = class(TForm)
procedure FormActivate(Sender: TObject);
private
FParentForm: TForm;
procedure SetAlphaBackground(const AResourceName: String);
public
constructor Create(AOwner: TComponent; const ABitmapResourceName: String); reintroduce;
procedure UpdatePosition;
end;
var
frmLayered: TfrmLayered;
implementation
{$R *.dfm}
constructor TfrmLayered.Create(AOwner: TComponent; const ABitmapResourceName: String);
begin
inherited Create(AOwner);
FParentForm := AOwner as TForm;
SetAlphaBackground(ABitmapResourceName);
end;
procedure TfrmLayered.FormActivate(Sender: TObject);
begin
if (Active) and (FParentForm.Visible) and (Assigned(FParentForm)) then
FParentForm.SetFocus;
end;
procedure TfrmLayered.UpdatePosition;
begin
if Assigned(FParentForm) then
begin
Left := FParentForm.Left - (ClientWidth - FParentForm.ClientWidth) div 2 - 1;
Top := FParentForm.Top - (ClientHeight - FParentForm.ClientHeight) div 2 - 1;
end;
end;
procedure TfrmLayered.SetAlphaBackground(const AResourceName: String);
var
blend_func: TBlendFunction;
imgpos : TPoint;
imgsize : TSize;
exStyle : DWORD;
png : TPngImage;
bmp : TBitmap;
begin
// enable window layering
exStyle := GetWindowLongA(Handle, GWL_EXSTYLE);
if ((exStyle and WS_EX_LAYERED) = 0) then
SetWindowLong(Handle, GWL_EXSTYLE, exStyle or WS_EX_LAYERED);
png := TPngImage.Create;
try
png.LoadFromResourceName(HInstance, AResourceName);
bmp := TBitmap.Create;
try
bmp.Assign(png);
// resize the form
ClientWidth := bmp.Width;
ClientHeight := bmp.Height;
// position image on form
imgpos := Point(0, 0);
imgsize.cx := bmp.Width;
imgsize.cy := bmp.Height;
// setup alpha blending parameters
blend_func.BlendOp := AC_SRC_OVER;
blend_func.BlendFlags := 0;
blend_func.SourceConstantAlpha := 255;
blend_func.AlphaFormat := AC_SRC_ALPHA;
UpdateLayeredWindow(Handle, 0, nil, @imgsize, bmp.Canvas.Handle, @imgpos, 0, @blend_func, ULW_ALPHA);
finally
bmp.Free;
end;
finally
png.Free;
end;
end;
end.
forma principal:
unit uMainForm;
interface
uses
uLayeredForm,
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TfrmMain = class(TForm)
imgClose: TImage;
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure FormShow(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormHide(Sender: TObject);
procedure imgCloseClick(Sender: TObject);
private
FLayeredForm: TfrmLayered;
protected
procedure WMMove(var AMessage: TMessage); message WM_MOVE;
public
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
uCommon, Vcl.Themes, Vcl.Styles.FormStyleHooks;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
{$IFDEF DEBUG} ReportMemoryLeaksOnShutdown := TRUE; {$ENDIF}
FLayeredForm := TfrmLayered.Create(self, 'MainBackground');
FLayeredForm.Visible := TRUE;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
FLayeredForm.Free;
end;
procedure TfrmMain.FormHide(Sender: TObject);
begin
FLayeredForm.Hide;
end;
procedure TfrmMain.WMMove(var AMessage: TMessage);
begin
if Assigned(FLayeredForm) then
FLayeredForm.UpdatePosition;
inherited;
end;
procedure TfrmMain.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
FormMove(self, Button, Shift, X, Y);
end;
procedure TfrmMain.FormShow(Sender: TObject);
begin
if Assigned(FLayeredForm) then
begin
FLayeredForm.Show;
FLayeredForm.UpdatePosition;
end;
end;
procedure TfrmMain.imgCloseClick(Sender: TObject);
begin
Close;
end;
initialization
TStyleManager.Engine.RegisterStyleHook(TfrmMain, TFormStyleHookBackground);
TFormStyleHookBackground.BackGroundSettings.Color := clBlack;
TFormStyleHookBackground.BackGroundSettings.Enabled := TRUE;
end.
como se puede ver, se tendrá que hacer un poco de trabajo manual para hacer dos formas se comportan como una sola, pero este código debería empezar.
Como necesitaba formularios con bordes redondeados lisos, la siguiente captura de pantalla es lo que obtuve como resultado final. Coloreé plena forma en gris, específicamente para este post, para la distinción fácil entre ella y capas forma negro:
Se puede ver claramente la diferencia entre las fronteras de forma grises con alias (hecho por SetWindowRgn() y CreateRoundRectRgn() API) y bordes de forma negra antialiased.
Sólo una nota al margen. No sé cómo lograr esto, pero si vas a hacer una forma semitransparente de color sólido sin ninguna configuración especial, simplemente puedes configurar ['AlphaBlend'] (http://docwiki.embarcadero.com/Libraries /XE2/en/Vcl.Forms.TForm.AlphaBlend) a True y ['AlphaBlendValue'] (http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TForm.AlphaBlendValue) al alfa deseado valor ;-) Pero es una buena pregunta en mi opinión; +1. – TLama
Creo que su respuesta está en el 4to y 5to párrafo de [Windows en capas] (http://msdn.microsoft.com/en-us/library/ms632599%28v=vs.85%29.aspx#layered). Básicamente, si desea continuar utilizando su código de pintura ya existente (VCL), use 'SetLayeredWindowAttributes'. Si dibujará usted mismo, use 'UpdateLayeredWindow'. –
@Sertac, al igual que las propiedades 'AlphaBlend' y' AlphaBlendValue' ;-) – TLama