title: How to display a message window in the right bottom corner of the active display using Delphi
body: <p>These days you see a lot of software displaying <strong>message windows in the right bottom corner of the active screen</strong> for a few seconds or until a close button is clicked (f.i. Norton does this after it has checked a download).</p>
  
  <p>I would like to do this using <strong>Delphi 7</strong> (and if possible <strong>Delphi 2010</strong>, since I am slowly migrating my code to the latest version).</p>
  
  <p>I found some posts here on SO regarding forms not receiving focus, but that's only one part of the problem. I'm thinking also on how to determine the exact position of this message window (knowing that f.i. a user may have put his taskbar to the right of the screen.</p>
  
  <p>Thx in advance.</p>
  
  <p><strong>UPDATE 26 Jan, 10:</strong> <em>Starting from the code of <code>drorhan</code> I created the following form (in Delphi 7) which works whether the taskbar is displayed at the bottom, the right, the left or the top of the schreen.</em></p>
  
  <pre><code>fPopupMessage.dpr:
  
    object frmPopupMessage: TfrmPopupMessage
      Left = 537
      Top = 233
      AlphaBlend = True
      AlphaBlendValue = 200
      BorderStyle = bsToolWindow
      Caption = 'frmPopupMessage'
      ClientHeight = 48
      ClientWidth = 342
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'MS Sans Serif'
      Font.Style = []
      OldCreateOrder = False
      OnClose = FormClose
      OnCreate = FormCreate
      DesignSize = (
        342
        48)
      PixelsPerInch = 96
      TextHeight = 13
      object img: TImage
        Left = 0
        Top = 0
        Width = 64
        Height = 48
        Align = alLeft
        Center = True
        Transparent = True
      end
      object lblMessage: TLabel
        Left = 72
        Top = 8
        Width = 265
        Height = 34
        Alignment = taCenter
        Anchors = [akLeft, akTop, akRight, akBottom]
        AutoSize = False
        Caption = '...'
        Font.Charset = DEFAULT_CHARSET
        Font.Color = clNavy
        Font.Height = -11
        Font.Name = 'Verdana'
        Font.Style = [fsBold]
        ParentFont = False
        Transparent = True
        WordWrap = True
      end
      object tmr: TTimer
        Enabled = False
        Interval = 3000
        OnTimer = tmrTimer
        Left = 16
        Top = 16
      end
    end
  </code></pre>
  
  <p>and</p>
  
  <p>fPopupMessage.pas</p>
  
  <pre><code>  unit fPopupMessage;
  
    interface
  
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls, ImgList;
  
    type
      TfrmPopupMessage = class(TForm)
        tmr: TTimer;
        img: TImage;
        lblMessage: TLabel;
        procedure FormCreate(Sender: TObject);
        procedure tmrTimer(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      private
        { Private declarations }
        bBeingDisplayed : boolean;
        function GetPopupMessage: string;
        procedure SetPopupMessage(const Value: string);
        function GetPopupCaption: string;
        procedure SetPopupCaption(const Value: string);
        function TaskBarHeight: integer;
        function TaskBarWidth: integer;
        procedure ToHiddenPosition;
        procedure ToVisiblePosition;
      public
        { Public declarations }
        procedure StartAnimationToHide;
        procedure StartAnimationToShow;
        property PopupCaption: string read GetPopupCaption write SetPopupCaption;
        property PopupMessage: string read GetPopupMessage write SetPopupMessage;
      end;
  
    var
      frmPopupMessage: TfrmPopupMessage;
  
    procedure DisplayPopup( sMessage:string; sCaption:string = '');
  
    implementation
  
    {$R *.dfm}
  
    const
       DFT_TIME_SLEEP = 5;       // the speed you want to show/hide.Increase/descrease this to make it faster or slower
       DFT_TIME_VISIBLE = 3000;  // number of mili-seconds the form is visible before starting to disappear
       GAP = 2;                  // pixels between form and right and bottom edge of the screen
  
    procedure DisplayPopup( sMessage:string; sCaption:string = '');
    begin
       // we could create the form here if necessary ...
       if not Assigned(frmPopupMessage) then Exit;
  
       frmPopupMessage.PopupCaption := sCaption;
       frmPopupMessage.PopupMessage := sMessage;
       if not frmPopupMessage.bBeingDisplayed
       then begin
          ShowWindow( frmPopupMessage.Handle, SW_SHOWNOACTIVATE);
          frmPopupMessage.Visible := True;
       end;
       frmPopupMessage.StartAnimationToShow;
    end;
  
    procedure TfrmPopupMessage.FormCreate(Sender: TObject);
    begin
      img.Picture.Assign(Application.Icon);
      Caption := '';
      lblMessage.Caption := '';
      bBeingDisplayed := False;
  
      ToHiddenPosition();
    end;
  
    procedure TfrmPopupMessage.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
       tmr.Enabled := False;
       Action := caHide;
       bBeingDisplayed := False;
    end;
  
    function TfrmPopupMessage.TaskBarHeight: integer; // this is just to get the taskbar height to put
    // my form in the correct position
    var
      hTB: HWND;
      TBRect: TRect;
    begin
      hTB := FindWindow('Shell_TrayWnd', '');
      if hTB = 0 then
        Result := 0
      else
      begin
        GetWindowRect(hTB, TBRect);
        if TBRect.Top = 0  // tray bar is positioned to the left or to the right
        then
           Result := 1
        else
           Result := TBRect.Bottom - TBRect.Top;
      end;
    end;
  
    function TfrmPopupMessage.TaskBarWidth: integer; // this is just to get the taskbar height to put
    // my form in the correct position
    var
      hTB: HWND;
      TBRect: TRect;
    begin
      hTB := FindWindow('Shell_TrayWnd', '');
      if hTB = 0 then
        Result := 0
      else
      begin
        GetWindowRect(hTB, TBRect);
        if TBRect.Left = 0  // tray bar is positioned to the left or to the right
        then
           Result := 1
        else
           Result := TBRect.Right - TBRect.Left
      end;
    end;
  
    procedure TfrmPopupMessage.ToHiddenPosition;
    begin
      Self.Left := Screen.Width - TaskbarWidth - Self.Width - GAP;
      Self.Top := Screen.Height - TaskBarHeight;
    end;
  
    procedure TfrmPopupMessage.ToVisiblePosition;
    begin
      Self.Left := Screen.Width - TaskBarWidth - Self.Width - GAP;
      Self.Top := Screen.Height - Self.Height - TaskBarHeight - GAP;
    end;
  
    procedure TfrmPopupMessage.StartAnimationToShow;
    var
      i: integer;
    begin
      if bBeingDisplayed
      then
         ToVisiblePosition()
      else begin
         ToHiddenPosition();
  
         for i := 1 to Self.Height+GAP do
         begin
           Self.Top := Self.Top-1;
           Application.ProcessMessages;
           Sleep(DFT_TIME_SLEEP);
         end;
      end;
      tmr.Interval := DFT_TIME_VISIBLE;
      tmr.Enabled := True;
      bBeingDisplayed := True;
  
    end;
  
    procedure TfrmPopupMessage.StartAnimationToHide;
    var
      i: integer;
    begin
      if not bBeingDisplayed then Exit;
  
      for i := 1 to Self.Height+GAP do
      begin
        Self.Top := Self.Top+1;
        Application.ProcessMessages;
        Sleep(DFT_TIME_SLEEP);
      end;
      bBeingDisplayed := False;
      Visible := False;
    end;
  
    procedure TfrmPopupMessage.tmrTimer(Sender: TObject);
    begin
       tmr.Enabled := False;
       StartAnimationToHide();
    end;
  
    function TfrmPopupMessage.GetPopupMessage: string;
    begin
       Result := lblMessage.Caption;
    end;
  
    procedure TfrmPopupMessage.SetPopupMessage(const Value: string);
    begin
       lblMessage.Caption := Value;
    end;
  
    function TfrmPopupMessage.GetPopupCaption: string;
    begin
       Result := frmPopupMessage.Caption;
    end;
  
    procedure TfrmPopupMessage.SetPopupCaption(const Value: string);
    begin
       frmPopupMessage.Caption := Value;
    end;
  
    end.
  </code></pre>
  
  <p>To be used as in my test form with two buttons:</p>
  
  <pre><code>procedure TfrmMain.button1Click(Sender: TObject);
  begin
     DisplayPopup('Message displayed at ' + FormatDateTime('ddd mmm yy zzz', Now),'My Program');
     beep;
  end;
  
  procedure TfrmMain.button2Click(Sender: TObject);
  begin
     DisplayPopup('Another message displayed at ' + FormatDateTime('hh:nn zzz', Now),'My Program');
  end;
  </code></pre>
  
  <p>The message form will display the application icon, but I will probably add a TImageList and add a property to pass an image index so I can display different icons. I will also use the TcxLabel from the Dev.Express components as this will provide verticle positionting, but the above unit can be used as is.</p>
  
  <p>I tested this with Delphi 7 and Windows XP. If anyone uses this unit with another version of Delphi and/or Windows Vista or Windows 7, please tell me if this unit will work there too.</p>