如何实现半透明子窗口内红色子窗口不透明且正常显示?(Delphi)
解决Delphi中半透明父窗口内子窗口不显示的问题
问题根源分析
- 当窗口设置为
WS_EX_LAYERED(分层窗口)后,系统会直接将窗口内容合成到屏幕,其子窗口无法参与常规绘制流程,父窗口的透明度会覆盖子窗口的显示效果。 - 你最初的代码中,蓝色分层窗口的透明度会传递给红色子窗口,即使移除子窗口的
WS_EX_LAYERED属性也无法解决。 - ChatGPT提供的方案是直接绘制静态位图到分层窗口,没有创建实际的红色子窗口,因此看不到独立的红色窗口实例。
解决方案1:使用Delphi自带的AlphaBlend属性(推荐)
Delphi封装的AlphaBlend属性会自动处理分层窗口的子窗口绘制逻辑,无需调用底层API即可实现半透明父窗口+不透明子窗口的效果。
修改后的代码:
procedure TNormalForm.CreateSecondWindow; begin // 半透明蓝色窗口 SecondWindow := TForm.Create(Self); SecondWindow.Parent := Self; SecondWindow.Color := clBlue; SecondWindow.Width := 400; SecondWindow.Height := 300; SecondWindow.Left := 100; SecondWindow.Top := 100; // 启用Alpha混合并设置透明度(200/255) SecondWindow.AlphaBlend := True; SecondWindow.AlphaBlendValue := 200; SecondWindow.Show; end; procedure TNormalForm.CreateThirdWindow; begin // 不透明红色子窗口 ThirdWindow := TForm.Create(SecondWindow); ThirdWindow.Parent := SecondWindow; ThirdWindow.Color := clRed; ThirdWindow.Width := 200; ThirdWindow.Height := 150; ThirdWindow.Left := 150; ThirdWindow.Top := 150; // 确保子窗口关闭Alpha混合(默认已关闭,可省略) ThirdWindow.AlphaBlend := False; ThirdWindow.Show; end;
解决方案2:将红色窗口设为蓝色窗口的兄弟窗口
如果必须使用底层WS_EX_LAYEREDAPI,可让红色窗口作为主窗口的子窗口(而非蓝色窗口的子窗口),通过调整位置和层级使其显示在蓝色窗口内部:
procedure TNormalForm.CreateSecondWindow; var Style: LongInt; begin SecondWindow := TForm.Create(Self); SecondWindow.Parent := Self; SecondWindow.Color := clBlue; SecondWindow.Width := 400; SecondWindow.Height := 300; SecondWindow.Left := 100; SecondWindow.Top := 100; Style := GetWindowLong(SecondWindow.Handle, GWL_EXSTYLE); SetWindowLong(SecondWindow.Handle, GWL_EXSTYLE, Style or WS_EX_LAYERED); SetLayeredWindowAttributes(SecondWindow.Handle, 0, 200, LWA_ALPHA); SecondWindow.Show; end; procedure TNormalForm.CreateThirdWindow; begin // 红色窗口的父窗口设为主窗口,而非蓝色窗口 ThirdWindow := TForm.Create(Self); ThirdWindow.Parent := Self; ThirdWindow.Color := clRed; ThirdWindow.Width := 200; ThirdWindow.Height := 150; // 计算相对于主窗口的位置:蓝色窗口偏移 + 红色窗口在蓝色内的偏移 ThirdWindow.Left := SecondWindow.Left + 150; ThirdWindow.Top := SecondWindow.Top + 150; // 确保红色窗口显示在蓝色窗口上方 ThirdWindow.BringToFront; ThirdWindow.Show; end;
解决方案3:手动绘制红色区域到分层窗口(静态内容场景)
如果红色部分无需作为独立窗口(仅静态显示),可修改ChatGPT的代码,在绘制蓝色半透明位图时直接加入红色不透明区域:
procedure DrawLayeredWindow(AForm: TForm); var ScreenDC, MemDC: HDC; Bitmap: HBITMAP; OldBitmap: HBITMAP; Blend: BLENDFUNCTION; BitmapData: array of TRGBQuad; BitmapInfo: TBitmapInfo; Width, Height: Integer; i, j: Integer; PtSize, PtSrc: TPoint; PtPos: TPoint; begin Width := AForm.Width; Height := AForm.Height; SetLength(BitmapData, Width * Height); for j := 0 to Height - 1 do begin for i := 0 to Width - 1 do begin // 红色区域:相对于蓝色窗口的位置调整为你需要的范围 if (i >= 150) and (i < 150+200) and (j >=150) and (j <150+150) then begin BitmapData[j * Width + i].rgbRed := 255; BitmapData[j * Width + i].rgbGreen := 0; BitmapData[j * Width + i].rgbBlue := 0; BitmapData[j * Width + i].rgbReserved := 255; // 完全不透明 end else begin BitmapData[j * Width + i].rgbRed := 0; BitmapData[j * Width + i].rgbGreen := 0; BitmapData[j * Width + i].rgbBlue := 255; BitmapData[j * Width + i].rgbReserved := 200; // 半透明 end; end; end; ScreenDC := GetDC(0); MemDC := CreateCompatibleDC(ScreenDC); Bitmap := CreateCompatibleBitmap(ScreenDC, Width, Height); OldBitmap := SelectObject(MemDC, Bitmap); FillChar(BitmapInfo, SizeOf(BitmapInfo), 0); with BitmapInfo.bmiHeader do begin biSize := SizeOf(TBitmapInfoHeader); biWidth := Width; biHeight := -Height; biPlanes := 1; biBitCount := 32; biCompression := BI_RGB; end; SetDIBits(MemDC, Bitmap, 0, Height, @BitmapData[0], BitmapInfo, DIB_RGB_COLORS); Blend.BlendOp := AC_SRC_OVER; Blend.BlendFlags := 0; Blend.SourceConstantAlpha := 255; Blend.AlphaFormat := AC_SRC_ALPHA; PtPos := Point(AForm.Left, AForm.Top); PtSize := Point(Width, Height); PtSrc := Point(0, 0); UpdateLayeredWindow(AForm.Handle, ScreenDC, @PtPos, @PtSize, MemDC, @PtSrc, 0, @Blend, ULW_ALPHA); SelectObject(MemDC, OldBitmap); DeleteObject(Bitmap); DeleteDC(MemDC); ReleaseDC(0, ScreenDC); end; procedure TNormalForm.CreateSecondWindow; var Style: LongInt; begin SecondWindow := TForm.Create(Self); SecondWindow.BorderStyle := bsNone; SecondWindow.Parent := Self; SecondWindow.Width := 400; SecondWindow.Height := 300; SecondWindow.Left := 100; SecondWindow.Top := 100; Style := GetWindowLong(SecondWindow.Handle, GWL_EXSTYLE); SetWindowLong(SecondWindow.Handle, GWL_EXSTYLE, Style or WS_EX_LAYERED); DrawLayeredWindow(SecondWindow); SecondWindow.Show; end;
内容的提问来源于stack exchange,提问作者NevTon
相关产品推荐
相关产品推荐

