0

I am using WS_EX_NOACTIVATE, but form still can be activated by clicking on it. Testing under Windows 10, Delphi XE8. What am I doing wrong?

Main form (calling Form3.Show on button click):

unit Unit2;

...

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);

...

procedure TForm2.Button1Click(Sender: TObject);
begin
form3.Show;
end;

end.

toolbar form (form3):

unit Unit3;

...

type
  TForm3 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  protected
  procedure CreateParams(var Params: TCreateParams); override;
  public

...   

procedure TForm3.CreateParams(var Params: TCreateParams);
const WS_EX_NOACTIVATE = $8000000;
begin
  inherited;
  Params.ExStyle := Params.ExStyle + WS_EX_NOACTIVATE;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
FormStyle := fsStayOnTop;
end;

end.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    Your flag handling is wrong. Use `or` and not `+`. – David Heffernan Aug 23 '15 at 16:48
  • To prevent form activation in `CreateParams` set: `Params.WndParent := GetDesktopWindow; Params.Style := Params.Style or WS_CHILD; Params.ExStyle := Params.ExStyle or WS_EX_TOPMOST or WS_EX_TOOLWINDOW;` – Abelisto Aug 23 '15 at 17:56
  • Abelisto, it works! But I cant interact with form. E.g. if I press any button on form - nothing happens; – user1596704 Aug 23 '15 at 19:04
  • Now you are taking biscuit. You want your form not to be activated, but also you want it to respond to input? – David Heffernan Aug 23 '15 at 19:45
  • 2
    @Abelisto A child window of the desktop? That's surely not a good idea. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/24/79212.aspx – David Heffernan Aug 23 '15 at 19:45
  • @DavidHeffernan This code not mine. It was stolen from [fpGUI Toolkit](http://fpgui.sourceforge.net/) and it works fine there. But it is more complicated then the above fragment. Actually not for Sunday evening. However this Q more about winapi which tag is missing. – Abelisto Aug 23 '15 at 19:57
  • 1
    @DavidHeffernan: A window that cannot be activated but still receives user input is more common than you may think. This is a common requirement for on-screen keyboards, for example. – IInspectable Aug 23 '15 at 22:05
  • I think on screen keyboards don't have windowed controls. – Sertac Akyuz Aug 23 '15 at 22:31
  • 1
    @SertacAkyuz: Since you cannot possibly know all on-screen keyboard implementations, that statement is easily rebutted. – IInspectable Aug 23 '15 at 23:07
  • Ok, [here is the very dirty example](http://pastebin.com/sP3n7dmg). However it is handle at least button click event :) Main idea is the using lowlevel handling of the windows messages using WndProc. So the task is solvable in general. I hope it is the more delphi-like way to solve it. – Abelisto Aug 23 '15 at 23:21
  • 2
    @IInspectable - I don't follow your logic. I think, I'll think what I think until I see a counter example, despite what you think. – Sertac Akyuz Aug 23 '15 at 23:39
  • @Abelisto why did you write code to respond when the mouse went down rather than up? – David Heffernan Aug 24 '15 at 07:30
  • @DavidHeffernan The answer to your question is: `WM_LBUTTONDOWN` is prior then `WM_LBUTTONUP` in the code completion list :) Seriously for now: my code snippets is not the full solution but the sign where asker can moving. So the Message is not matter. It is another Q: why the form with such attributes does not hit to the Delphi Application message queue. – Abelisto Aug 24 '15 at 07:53
  • 1
    @user1596704, Do you need a caption? a border? How do you intend to move this window for example? – kobik Aug 24 '15 at 08:40
  • @kobik, I want behaviour just like photoshop toolbar (simple form with buttons on it). It can be moded, clicked, but never takes focus. https://db.tt/nWPvnnct – user1596704 Aug 24 '15 at 11:57
  • 1
    But as I see in the picture, it *does* take focus. look at the title bar. in any case, google "Delphi WS_EX_NOACTIVATE". there are even duplicates on SO. e.g. http://stackoverflow.com/questions/8125108/avoid-window-getting-focus – kobik Aug 24 '15 at 12:35

1 Answers1

1

I am not able to post comments yet so I have to post as an answer.

You can quite easily achieve it by setting the focus on any window you want. Just use EnumWindows to get all windows and then use SetForegroundWindow to loose focus. I did this in my implementation of an onscreen keyboard.

This means while you are clicking your window will have focus. In the OnClick method you call SetForegroundWindow with the Handle you want to have focussed an you will loose focus again. If you use the OnMouseDown method you will loose focus even faster.

Here is production code I made around 6 years ago. You will have to modify it but I hope it will give you the idea what I am talking about.

  TWindowData = class(TObject)
  private
    FWDWindowHandle: HWND;
    FWDWindowCaption: string;
    FWDExeFileName: string;
    procedure SetWDExeFileName(const Value: string);
    procedure SetWDWindowCaption(const Value: string);
    procedure SetWDWindowHandle(const Value: HWND);
  public
    property WDWindowCaption:string read FWDWindowCaption write SetWDWindowCaption;
    property WDExeFileName:string read FWDExeFileName write SetWDExeFileName;
    property WDWindowHandle: HWND read FWDWindowHandle write SetWDWindowHandle;
  end;

function GetTitle (Hwnd: THandle; Param: Pointer): Boolean; stdcall;
{ get caption from every visible window }

var Text,TempString: string;
    Buf : Array [0..100] of Char;
begin
  result := True;
  If (GetWindowLong(Hwnd,GWL_HWNDPARENT)=0) and (IsWindowVisible(Hwnd) or IsIconic(Hwnd))then
  begin
  TempString := GetWindowExeName(Hwnd);
  SetLength (Text, 100);

  GetModuleFileName( Hwnd,buf, 100 );

  GetWindowText (Hwnd, PChar (Text), 100);
  if NoSystemWindowOrSelf(TempString,Text) then
  begin
    MainForm.OneWindowData := TWindowData.create;
    with MainForm.OneWindowData do
    begin
      WDWindowHandle := Hwnd;
      WDWindowCaption := Text;
      WDExeFileName := TempString;
    end;
    MainForm.WindowDataList.Add (MainForm.OneWindowData);
    MainForm.lbWindowListMemo.Items.Add(text); { Show all Windows in this Memo }

  end;
  Result := True;
  end;
end;

procedure TMainForm.bFillMemoClick(Sender: TObject);
var EWProc: EnumWindowsProc;
begin
  WindowListMemo.Items.Clear;
  WindowDataList.clear;

  { EnumWindows geht der Reihe nach alle offenen Fenster durch }
  EWProc := GetTitle;
  EnumWindows (@EWProc, 0);
end;

procedure TMainForm.FocusWindow;
{ Click on a Memo line to focus a window }
var TempWindowData : TWindowData;
begin
  TempWindowData := TWindowData(WindowDataList[WindowListMemo.itemindex]);
  SetForegroundWindow(TempWindowData.WDWindowHandle);
end;