Ismail Dz
Ismail Dz

Reputation: 75

Win API Delphi GDI+: Drawing a String Inside a Child Window

When I draw a string using GDI+ in a child window, the text does not appear. But when I change the child window to a normal window, the text appears normally. So here is what I did so far:

program Test_Program;

 Uses
   Windows,
   Messages,
   GdipObj,
   GdipApi,
   DirectDraw;

var
  Msg: TMSG;
  LWndClass: TWndClass;
  hMainHandle: HWND;

Function CreateChildWindow(hParent: hWnd): hWnd;
Var
  WindowClass: TWndClass;
Begin
  Result := 0;
  WindowClass.lpszClassName := 'ChildWindow';
  WindowClass.Style := cs_HRedraw or cs_VRedraw;
  WindowClass.lpfnWndProc := @DefWindowProc;
  WindowClass.cbClsExtra := 0;
  WindowClass.cbWndExtra := 0;
  WindowClass.hInstance := HInstance;
  WindowClass.hIcon := LoadIcon(0, idi_Application);
  WindowClass.hCursor := LoadCursor(0, idc_Arrow);
  WindowClass.hbrBackground := COLOR_BTNFACE + 1;
  WindowClass.lpszMenuName := NIL;
  WindowClass.lpszClassName := 's';
  if RegisterClass(WindowClass)<>0 Then
    Result := CreateWindowEx(WS_EX_CONTROLPARENT or WS_EX_LAYERED, 
      WindowClass.lpszClassName, '', WS_CHILD or WS_VISIBLE or WS_SYSMENU 
      or WS_CAPTION, 0, 0, 400, 300, hParent, 0, hInstance, nil);
End;

Function MainFormProc(hWnd, Msg: LongInt; wParam: WPARAM; lParam: LPARAM):LongInt;
  stdcall;
var ChildhWnd: Integer;
    Author : String;
    AuthorPosX, AuthorPosY: Single;
    SrcDC, DestDC: HDC;
    BitmapHandle, PrevBitmap: HBITMAP;
    BlendFunc: _BLENDFUNCTION;
    Size: TSize;
    POSS: TRect;
    P, S: TPoint;
    Graphics  : TGPGraphics;
    Bitmap    : TGPBitmap;
    Font      : TGPFont;
    FontFamily: TGPFontFamily;
    SolidBrush: TGPSolidBrush;
Begin
  Result := 0;
  Case Msg of
    WM_CREATE:
    Begin
      SolidBrush := TGPSolidBrush.Create($FF000000);
      FontFamily := TGPFontFamily.Create('Arial');
      Font       := TGPFont.Create(FontFamily, 18, FontStyleRegular, UnitPixel);

      ChildhWnd := CreateChildWindow(hWnd);

      Author    := 'Transparent Child Window + GDI+ Text';
      AuthorPosX:= 30;
      AuthorPosY:= 40;
      Size.cx := 600;
      Size.cy := 80;

      Bitmap   := TGPBitmap.Create(600, 80, PixelFormat32bppARGB);
      Graphics := TGPGraphics.Create(Bitmap);
      Graphics.SetTextRenderingHint(TextRenderingHintAntiAlias); {Set Text Anti Aliased}
      S.X := 0;
      S.Y := 0;
      With BlendFunc Do
      Begin
        BlendOp := AC_SRC_ALPHA;
        BlendFlags := 0;
        SourceConstantAlpha := 250;
        AlphaFormat := AC_SRC_ALPHA;
      End;
      Graphics.Clear(0);
      Graphics.DrawString(Author, Length(Author), Font,
      MakePoint(AuthorPosX, AuthorPosY), SolidBrush);
      SrcDC := CreateCompatibleDC(0);
      DestDC:= CreateCompatibleDC(SrcDC);
      Bitmap.GetHBITMAP(0, BitmapHandle);
      PrevBitmap := SelectObject(SrcDC, BitmapHandle);
      GetWindowRect(ChildhWnd, POSS);
      P.X := POSS.Left;
      P.Y := POSS.Top;
      UpdateLayeredWindow(ChildhWnd, DestDC, @P, @Size, SrcDC, 
        @S, 0, @BlendFunc, ULW_ALPHA);
      SelectObject(SrcDC, PrevBitmap);
      DeleteObject(BitmapHandle);
      DeleteDC(DestDC);
      DeleteDC(SrcDC);
      ShowWindow(ChildhWnd, CmdShow);
      UpdateWindow(ChildhWnd);

    End;

    WM_LBUTTONDOWN:
    Begin
      DefWindowProc(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
      Result:=0;
    End;
    WM_DESTROY: ExitProcess(0);
  else
    Result := DefWindowProc(hWnd, Msg, wParam, lParam);
  end;
end;

begin
  LWndClass.hInstance := hInstance;
  with LWndClass do
  begin
    lpszClassName := 'TMainForm';
    Style := CS_PARENTDC or CS_BYTEALIGNCLIENT;
    hIcon := LoadIcon(hInstance, 'MAINICON');
    lpfnWndProc := @MainFormProc;
    hbrBackground := COLOR_BTNFACE + 1;
    hCursor := LoadCursor(0, IDC_ARROW);
  end;

  RegisterClass(LWndClass);
  hMainHandle := CreateWindow(LWndClass.lpszClassName, 'Main Window',
    WS_SYSMENU, (GetSystemMetrics(SM_CXSCREEN)  div 2) - 400,
    (GetSystemMetrics(SM_CYSCREEN) div 2) - 300, 800, 600, 
    0, 0, hInstance,  nil);

  ShowWindow(hMainHandle, SW_SHOW);
  UpdateWindow(hMainHandle);

  while GetMessage(Msg, 0, 0, 0) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end.

Changing the Child window to normal window the text will appear!!

Code From:

CreateWindowEx(WS_EX_CONTROLPARENT or WS_EX_LAYERED, 
  WindowClass.lpszClassName, '', 
  WS_CHILD or WS_VISIBLE or WS_SYSMENU or WS_CAPTION, 
  0, 0, 400, 300, hParent, 0, hInstance, nil);

To:

CreateWindowEx(WS_EX_CONTROLPARENT or WS_EX_LAYERED, 
  WindowClass.lpszClassName, '', 
  WS_VISIBLE or WS_SYSMENU or WS_CAPTION, 
  0, 0, 400, 300, hParent, 0, hInstance, nil);

The text will be drawn!! What's the problem exactly? BTW, I want the childwindow to stay WS_EX_LAYERED because I want only the text to appear not the whole child window.

Upvotes: 1

Views: 2083

Answers (1)

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28516

UpdateLayeredWindow is only supported for top level windows and not child windows.

However, from Windows 8, MS added support for other windows too.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633556%28v=vs.85%29.aspx

Ok, here is rather basic code (no exception handling) for creating transparent VCL custom control that uses GDI+ for drawing. It is proof of concept, and it can be translated back to direct Windows API.

type
  TPWinControl = class(TWinControl);

  TTransparentControl = class(TCustomControl)
  protected
    procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND;
  end;

function GetScreenClient(Control: TControl): TPoint;
var
  p: TPoint;
begin
  p := Control.ClientOrigin;
  ScreenToClient(Control.Parent.Handle, p);
  Result := p;
end;

procedure TTransparentControl.WMEraseBkgnd(var Message: TWmEraseBkgnd);
var
  Bmp: TBitmap;
  DC: hDC;
  i: integer;
  p: TPoint;

  Author: string;
  AuthorPosX, AuthorPosY: Single;
  Size: TSize;
  Graphics: TGPGraphics;
  Bitmap: TGPBitmap;
  Font: TGPFont;
  FontFamily: TGPFontFamily;
  SolidBrush: TGPSolidBrush;
begin
  message.Result := 1;
  if (message.DC <> 0) and Assigned(Parent) then
    begin
      DC := message.DC;
      i := SaveDC(DC);
      p := GetScreenClient(Self);
      MoveWindowOrg(DC, -p.x, -p.y);
      SendMessage(Parent.Handle, WM_ERASEBKGND, DC, 0);
      TPWinControl(Parent).PaintControls(DC, nil);
      RestoreDC(DC, i);

      Bmp := TBitmap.Create;
      Bmp.PixelFormat := pf32bit;
      Bmp.Width := Width;
      Bmp.Height := Height;
      BitBlt(Bmp.Canvas.Handle, 0, 0, Width, Height, DC, 0, 0, SrcCopy);

      SolidBrush := TGPSolidBrush.Create($FF000000);
      FontFamily := TGPFontFamily.Create('Arial');
      Font := TGPFont.Create(FontFamily, 18, FontStyleRegular, UnitPixel);
      Author := 'Transparent Child Window + GDI+ Text';
      AuthorPosX := 30;
      AuthorPosY := 40;
      Size.cx := 600;
      Size.cy := 80;

      Bitmap := TGPBitmap.Create(Bmp.Handle);
      Graphics := TGPGraphics.Create(Bmp.Canvas.Handle);
      Graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
      Graphics.DrawString(Author, Length(Author), Font, 
        MakePoint(AuthorPosX, AuthorPosY), SolidBrush);
      Graphics.Free;
      Bitmap.Free;
      Canvas.Draw(0, 0, Bmp);
      Bmp.Free;
    end;
end;

Upvotes: 2

Related Questions