子窗体始终位于主窗体之上

7

我正在使用Delphi 2007,每次使用以下代码创建新窗体时:

var
  Child : TFrmChild;
begin
  Child:=TFrmChild.Create(Self);
  Child.Show();
end;

当一个子窗体显示并出现在所有其他窗体之上时,这是可以接受的。但是,当我点击主窗体时,子窗体仍然停留在主窗体之上。因此,我有两个问题:

  1. 为什么即使在主窗体上单击,子窗体仍然停留在主窗体之上?
  2. 如何使主窗体在单击时保持在所有其他窗体之上?

谢谢。

更新

这是子窗体的 dfm。

object FrmChild: TFrmChild
  Left = 549
  Top = 308
  Caption = 'FrmChild'
  ClientHeight = 228
  ClientWidth = 213
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
end
4个回答

4
你有两个顶级窗口。主窗体是子窗体的所有者。拥有的窗口始终显示在它们的所有者之上,这是Windows的规则之一。
请注意,我所指的所有者是Windows概念,而不是Delphi概念。 Windows Features主题解释了规则。关键语句是:
一个拥有的窗口总是在z顺序中高于其所有者。
至于如何使您的应用程序行为不同,我不太确定。例如,如果您使子窗体无所有权,则它将具有自己的任务栏按钮,并且当主窗体最小化时,它不会被最小化。

3
这个问题已经被报告到QualityCentral。简单的解决方法是设置


  Application.MainFormOnTaskbar := False;

在您的项目的.dpr文件中。缺点是会失去一些Vista功能。

还有另一种解决方法,但它很笨拙,并且存在其他问题(请参见QC链接)。


1
谢谢提供链接。如果我将Application.MainFormOnTaskbar设置为false,我会失去哪些Windows Vista功能? - DelphiNewbie
虽然您说得没错,这个问题已经被报告给QC了,但是您应该注意到它已经因为行为符合设计而被关闭了。 - David Heffernan
@DelphiNewbie - 我不使用Vista,所以我不知道,但我认为这与应用程序在任务栏上的显示方式有关。 - crefird
我也遇到了同样的问题,看起来这就是罪魁祸首。感谢您提供的信息,关于设置MainFormOnTaskbar时未在帮助文件中提到的副作用。 - Jessica Brown
晚了一步 - 您可以使用 ITaskBarList 接口来获取一些“Vista”效果。在 FormShow 中使用 AddTab,在 FormActivate 中使用 ActivateTab,在 FormHide 中使用 DeleteTab(或覆盖 DoShow、Activate 和 DoHide)。我已经在 Windows 7 和 10 上的 D2007 中实现了这个功能。 - Gerry Coll
显示剩余4条评论

1
迟来的回答 - 我刚刚解决了这个问题。我们想要Aero样式的ALT-TAB和WIN-TAB功能,但是MainFormOnTaskBar在与第三方组件(LMD Docking)发生一些问题。(如果主窗体上有一个停靠站点并且子窗体上也有一个停靠站点,将停靠项拖到子窗体上会将主窗体带到最前面)
解决方法是:
  • 在CreateParams中将Params.WndParent设置为0
  • 使用ITaskBarList接口来管理任务栏选项卡。这还将窗口添加到ALT-TAB和WIN-TAB处理程序中。

TaskBarList.pas

unit TaskbarList;

interface

uses
  Windows, Messages,
  CommCtrl,
  ShlObj,
  SysUtils, Classes, ComCtrls;

const
  SID_ITaskbarList                            = '{56FDF342-FD6D-11D0-958A-006097C9A090}';
  SID_ITaskbarList2                           = '{602D4995-B13A-429B-A66E-1935E44F4317}';
  SID_ITaskbarList3                           = '{EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF}';
  SID_ITaskbarList4                           = '{C43DC798-95D1-4BEA-9030-BB99E2983A1A}';

  CLSID_TaskbarList: TGUID                            = '{56FDF344-FD6D-11d0-958A-006097C9A090}';

type
  ITaskbarList = interface(IUnknown)
    [SID_ITaskbarList]
    function HrInit: HRESULT; stdcall;
    function AddTab(hwnd: HWND): HRESULT; stdcall;
    function DeleteTab(hwnd: HWND): HRESULT; stdcall;
    function ActivateTab(hwnd: HWND): HRESULT; stdcall;
    function SetActiveAlt(hwnd: HWND): HRESULT; stdcall;
  end;

  ITaskbarList2 = interface(ITaskbarList)
    [SID_ITaskbarList2]
    function MarkFullscreenWindow(hwnd: HWND; fFullscreen: BOOL): HRESULT; stdcall;
  end;

type
  THUMBBUTTON = record
    dwMask: DWORD;
    iId: UINT;
    iBitmap: UINT;
    hIcon: HICON;
    szTip: packed array[0..259] of WCHAR;
    dwFlags: DWORD;
  end;
  tagTHUMBBUTTON = THUMBBUTTON;
  TThumbButton = THUMBBUTTON;
  PThumbButton = ^TThumbButton;

// THUMBBUTTON flags
const
  THBF_ENABLED        =  $0000;
  THBF_DISABLED       =  $0001;
  THBF_DISMISSONCLICK =  $0002;
  THBF_NOBACKGROUND   =  $0004;
  THBF_HIDDEN         =  $0008;
  THBF_NONINTERACTIVE = $10;
// THUMBBUTTON mask
  THB_BITMAP          =  $0001;
  THB_ICON            =  $0002;
  THB_TOOLTIP         =  $0004;
  THB_FLAGS           =  $0008;
  THBN_CLICKED        =  $1800;

const
  TBPF_NOPROGRESS    = 0;
  TBPF_INDETERMINATE = $1;
  TBPF_NORMAL        = $2;
  TBPF_ERROR         = $4;
  TBPF_PAUSED        = $8;

  TBATF_USEMDITHUMBNAIL   = $1;
  TBATF_USEMDILIVEPREVIEW = $2;

const
  STPF_NONE                       = $00000000;
  STPF_USEAPPTHUMBNAILALWAYS      = $00000001;
  STPF_USEAPPTHUMBNAILWHENACTIVE  = $00000002;
  STPF_USEAPPPEEKALWAYS           = $00000004;
  STPF_USEAPPPEEKWHENACTIVE       = $00000008;


type
  ITaskbarList3 = interface(ITaskbarList2)
    [SID_ITaskbarList3]
    function SetProgressValue(hwnd: HWND; ullCompleted: ULONGLONG;
      ullTotal: ULONGLONG): HRESULT; stdcall;
    function SetProgressState(hwnd: HWND; tbpFlags: Integer): HRESULT; stdcall;
    function RegisterTab(hwndTab: HWND; hwndMDI: HWND): HRESULT; stdcall;
    function UnregisterTab(hwndTab: HWND): HRESULT; stdcall;
    function SetTabOrder(hwndTab: HWND; hwndInsertBefore: HWND): HRESULT; stdcall;
    function SetTabActive(hwndTab: HWND; hwndMDI: HWND;
      tbatFlags: Integer): HRESULT; stdcall;
    function ThumbBarAddButtons(hwnd: HWND; cButtons: UINT;
      pButton: PThumbButton): HRESULT; stdcall;
    function ThumbBarUpdateButtons(hwnd: HWND; cButtons: UINT;
      pButton: PThumbButton): HRESULT; stdcall;
    function ThumbBarSetImageList(hwnd: HWND; himl: HIMAGELIST): HRESULT; stdcall;
    function SetOverlayIcon(hwnd: HWND; hIcon: HICON;
      pszDescription: LPCWSTR): HRESULT; stdcall;
    function SetThumbnailTooltip(hwnd: HWND; pszTip: LPCWSTR): HRESULT; stdcall;
    function SetThumbnailClip(hwnd: HWND; var prcClip: TRect): HRESULT; stdcall;
  end;

  ITaskbarList4 = interface(ITaskbarList3)
    [SID_ITaskbarList4]

    function SetTabProperties (hwnd: HWND; stpFlags: Integer): HRESULT; stdcall;

  end;

implementation

end.

BaseForm.pas:

unit BaseForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  TaskBarList;

type
  TBaseForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    class var FTaskBarList: ITaskbarList;
  protected
    class function TaskBarList: ITaskbarList;
    procedure DoShow;override;
    procedure DoHide;override;
    procedure Activate;override;
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

var
  BaseForm: TBaseForm;

implementation

uses
  ComObj;

{$R *.dfm}

{ TBaseForm }

procedure TBaseForm.Activate;
begin
  inherited;
  TaskBarList.ActivateTab(Handle);
end;

procedure TBaseForm.Button1Click(Sender: TObject);
begin
  TBaseForm.Create(Self).Show;
end;

constructor TBaseForm.Create(AOwner: TComponent);
begin
  inherited;
  // remove taskbar button for Application.Handle
  TaskBarList.DeleteTab(Application.Handle);
end;

procedure TBaseForm.CreateParams(var Params: TCreateParams);
begin
  inherited;
  if (Parent = nil) and (ParentWindow = 0) then // don't use on docked Windows
    Params.WndParent := 0;
end;

procedure TBaseForm.DoHide;
begin
  inherited;
  TaskBarList.DeleteTab(Handle);
end;

procedure TBaseForm.DoShow;
begin
  inherited;
  TaskBarList.AddTab(Handle);
end;

class function TBaseForm.TaskBarList: ITaskbarList;
var
  pIntF: IInterface;
begin
  if not assigned(FTaskBarList) then
  begin
    pIntF := CreateComObject(CLSID_TaskbarList);
    pIntF.QueryInterface(ITaskBarList, FTaskBarList);

    FTaskBarList.HrInit;
  end;
  Result := FTaskBarList;
end;

end.

注意: 我没有使用OleCheck检查HRESULTS,因为我希望它在现场静默失败。


0

谢谢,但是我的子窗体的formstyle属性已经设置为fsNormal,而且我不想使用MDI界面。 - DelphiNewbie
你不需要一个始终置顶的窗体来实现这个功能。你只需要让子窗体被主窗体所拥有(在Windows术语中)。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接