windows – 如何正确使用无模式窗体出现在任务栏中
我试图实现古老的Delphi梦想,在任务栏中出现一个无模式的形式.
在任务栏中显示无模式表单的正确方法是什么? 研究工作 这些是我尝试解决问题的方法.要使其正常运行需要很多东西 – 只需在任务栏上显示一个按钮就不是解决方案.让Windows应用程序作为Windows应用程序正常运行应该是我的目标. 对于那些了解我的人,以及我的“展示研究成果”的深度,坚持下去,因为它将是一个疯狂的骑兔子洞. 问题在于标题,以及上面的水平线.以下所有内容仅用于说明为什么有些经常重复的建议是不正确的. Windows仅为无主窗口创建任务栏按钮 最初我有我的“主要表格”,从中我展示了另一种无模式形式: procedure TfrmMain.Button2Click(Sender: TObject); begin if frmModeless = nil then Application.CreateForm(TfrmModeless,frmModeless); frmModeless.Show; end; 这会正确显示新表单,但任务栏上不会显示任何新按钮: 没有创建任务栏按钮的原因是因为这是设计的. Windows will only show a taskbar button for a window that “unowned”.这种无模式的Delphi表格绝对拥有.在我的情况下,它由Application.Handle拥有: 我的项目名称是ModelessFormFail.dpr,它是与所有者关联的Windows类名称Modelessformfail的来源. 幸运的是,有一种方法可以强制Windows为窗口创建任务栏按钮,即使窗口是拥有的: 只需使用WS_EX_APPWINDOW即可
它也是一个覆盖CreateParams并手动添加WS_EX_APPWINDOW样式的well-known Delphi技巧: procedure TfrmModeless.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; //force owned window to appear in taskbar end; 当我们运行它时,新创建的无模式窗体确实获得了自己的任务栏按钮: 我们完成了吗?不,因为它的行为不正确. 如果用户单击frmMain任务栏按钮,则不会提示该窗口.而是提出了另一种形式(frmModeless): 一旦理解了Windows的所有权概念,这就有意义了.根据设计,Windows将带来任何儿童拥有的表格.这是所有权的全部目的 – 将拥有的表格保留在其所有者之上. 使表格实际上无主 解决方案,as some of you know不打击任务栏启发式和Windows.如果我希望表单是无主的,那就让它无主. 这(相当)简单.在CreateParam中强制所有者窗口为null: procedure TfrmModeless.CreateParams(var Params: TCreateParams); begin inherited; //Doesn't work,because the form is still owned // Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; //force owned windows to appear in taskbar //Make the form actually unonwed; it's what we want Params.WndParent := 0; //unowned. Unowned windows naturally appear on the taskbar. //There may be a way to simulate this with PopupParent and PopupMode. end; 顺便说一句,我想调查是否有一种方法可以使用 procedure TfrmMain.Button1Click(Sender: TObject); begin if frmModeless = nil then begin Application.CreateForm(TfrmModeless,frmModeless); frmModeless.PopupParent := frmModeless; //The super-secret way to say "unowned"? I swear David Heffernan mentioned it somewhere on SO,but be damned if i can find it now. frmModeless.PopupMode := pmExplicit; //happens automatically when you set a PopupParent,but you get the idea end; frmModeless.Show; end; 这应该是向德尔福表明你要形成“没有所有者”的超级秘密方式.但我现在无法在任何地方找到评论.不幸的是,没有PopupParent和PopupMode的组合导致表单实际上是非拥有的: > PopupMode:pmNone >所有者hwnd:Application.Handle / Application.MainForm.Handle > PopupMode:pmAuto >所有者hwnd:Screen.ActiveForm.Handle > PopupMode:pmExplicit > PopupParent:无 >所有者hwnd:Application.MainForm.Handle > PopupParent:AForm >所有者hwnd:AForm.Handle > PopupParent:自我 >所有者hwnd:Application.MainForm.Handle 我无能为力可能导致表单实际上没有所有者(每次检查Spy). 在 >确实使表格无主 我们完成了,对吧?我是这么想的.我改变了一切来使用这种新技术. 除了我的修复程序有问题似乎导致其他问题 – Delphi不喜欢我改变为表单的所有权. 提示Windows 我的无模式窗口上的一个控件有一个tooltop: 问题是当这个工具提示窗口出现时,它会导致另一种形式(frmMain,模态)出现.它没有获得激活焦点;但它现在确实模糊了我看到的形式: 原因可能是合乎逻辑的. Delphi HintWindow可能由Application.Handle或Application.MainForm.Handle拥有,而不是由它应该拥有的表单所拥有: 我会认为这是Delphi的一个错误;使用错误的所有者. 转移以查看实际的应用布局 现在重要的是,我花一点时间来证明我的应用程序不是主要形式和非模态形式: 它实际上是: >登录屏幕(隐藏的牺牲主表格) 即使应用程序布局的实际情况,除提示窗口所有权之外的所有内容都有效.有两个任务栏按钮,点击它们会带来正确的表格: 但我们仍然存在HintWindow所有权带来错误形式的问题: ShowMainFormOnTaskbar 当我意识到我无法创建一个最小的应用程序来重现问题时.有一些不同的东西: >我的Delphi 5应用程序之间移植到XE6 在comparing之后,我终于追溯到XE6中的新应用程序在任何新项目中默认添加MainFormOnTaskbar:= True这一事实(可能是为了不破坏现有应用程序): program ModelessFormFail; //... begin Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TfrmSacrificialMain,frmSacrificialMain); //Application.CreateForm(TfrmMain,frmMain); Application.Run; end. 当我添加此选项时,工具提示的外观并没有带来错误的表格!: 成功!除了知道将要发生什么的人know what’s coming.我的“牺牲”主登录表单显示“真正的”主要形式,隐藏自己: procedure TfrmSacrificialMain.Button1Click(Sender: TObject); var frmMain: TfrmMain; begin frmMain := TfrmMain.Create(Application); Self.Hide; try frmMain.ShowModal; finally Self.Show; end; end; 当这种情况发生时,我“登录”,我的任务栏图标完全消失了: 这是因为: >非拥有的牺牲主形式不是不可见的:所以按钮随之而来 使用WS_APP_APPWINDOW 现在我们有机会使用WS_EX_APPWINDOW.我想强制我的主窗体出现在任务栏上.所以我重写CreateParams并强制它出现在任务栏上: procedure TfrmMain.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; //force owned window to appear in taskbar end; 我们给它一个旋转: 看起来还不错! >两个任务栏按钮 除了,当我点击第一个工具栏按钮时,出现错误的表单.它显示模态frmMain,而不是当前模态frmControlPanel: 大概是因为新创建的frmControlPanel是PopupParented到Application.MainForm而不是Screen.ActiveForm.签入间谍: 是的,父母是MainForm.Handle.原来这是因为VCL中的另一个错误.如果表单的PopupMode是: > pmAuto VCL尝试使用Application.ActiveFormHandle作为hWndParent.不幸的是,它会检查是否启用了模态窗体的父级: if (WndParent <> 0) and ( IsIconic(WndParent) or not IsWindowVisible(WndParent) or not IsWindowEnabled(WndParent)) then 当然,模式窗体的父级未启用.如果是,那就不是一种模态形式.所以VCL回归使用: WndParent := Application.MainFormHandle; 手动育儿 这意味着我可能必须确保手动(?)设置弹出父母? procedure TfrmMain.Button2Click(Sender: TObject); var frmControlPanel: TfrmControlPanel; begin frmControlPanel := TfrmControlPanel.Create(Application); try frmControlPanel.PopupParent := Self; frmControlPanel.PopupMode := pmExplicit; //Automatically set to pmExplicit when you set PopupParent. But you get the idea. frmControlPanel.ShowModal; finally frmControlPanel.Free; end; end; 除此之外也没有用.单击第一个任务栏按钮会导致错误的表单激活: 此时我完全糊涂了.我的模态形式的父亲应该是frmMain,它是!: 所以现在怎么办? 我对可能发生的事情有所了解. 该任务栏按钮是frmMain的表示. Windows正在推动这一进程. 当MainFormOnTaskbar设置为false时,它的行为正常. Delphi VCL中一定有一些神奇之处导致正确性,但是使用MainFormOnTaskbar得到禁用:= True,但它是什么? 我不是第一个希望Delphi应用程序与Windows 95工具栏表现良好的人.我过去曾问过这个问题,但这些答案总是面向Delphi 5,它是旧的中央路由窗口. 我被告知,所有内容都是在Delphi 2007时间框架内修复的. 那么正确的解决方案是什么? 奖金阅读 > http://blogs.msdn.com/b/oldnewthing/archive/2003/12/29/46371.aspx
在我看来,根本的问题是,在VCL眼中,你的主要形式不是你的主要形式.一旦你解决了这个问题,所有的问题就会消失.
你应该: >对于真正的主窗体,只调用一次Application.CreateForm.这是一个很好的规则.考虑Application.CreateForm的工作是创建应用程序的主要形式. 就是这样.这是一个完整的例子: Project1.dpr program Project1; uses Vcl.Forms,uMain in 'uMain.pas' {MainForm},uLogin in 'uLogin.pas' {LoginForm},uModeless in 'uModeless.pas' {ModelessForm}; {$R *.res} begin Application.Initialize; Application.ShowHint := True; Application.MainFormOnTaskbar := True; with TLoginForm.Create(Application) do begin ShowModal; Free; end; Application.CreateForm(TMainForm,MainForm); Application.Run; end. uLogin.pas unit uLogin; interface uses Winapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,Vcl.Controls,Vcl.Forms,Vcl.Dialogs; type TLoginForm = class(TForm) protected procedure CreateParams(var Params: TCreateParams); override; end; implementation {$R *.dfm} procedure TLoginForm.CreateParams(var Params: TCreateParams); begin inherited; Params.WndParent := 0; end; end. uLogin.dfm object LoginForm: TLoginForm Left = 0 Top = 0 Caption = 'LoginForm' ClientHeight = 300 ClientWidth = 635 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 end uMain.pas unit uMain; interface uses Winapi.Windows,Vcl.Dialogs,Vcl.StdCtrls,uModeless; type TMainForm = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.Button1Click(Sender: TObject); begin with TModelessForm.Create(Self) do begin Show; end; end; end. uMain.dfm object MainForm: TMainForm Left = 0 Top = 0 Caption = 'MainForm' ClientHeight = 300 ClientWidth = 635 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 288 Top = 160 Width = 75 Height = 23 Caption = 'Button1' TabOrder = 0 OnClick = Button1Click end end uModeless.pas unit uModeless; interface uses Winapi.Windows,Vcl.StdCtrls; type TModelessForm = class(TForm) Label1: TLabel; protected procedure CreateParams(var Params: TCreateParams); override; end; implementation {$R *.dfm} procedure TModelessForm.CreateParams(var Params: TCreateParams); begin inherited; Params.WndParent := 0; end; end. uModeless.dfm object ModelessForm: TModelessForm Left = 0 Top = 0 Caption = 'ModelessForm' ClientHeight = 300 ClientWidth = 635 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False ShowHint = True PixelsPerInch = 96 TextHeight = 13 object Label1: TLabel Left = 312 Top = 160 Width = 98 Height = 13 Hint = 'This is a hint' Caption = 'I'#39'm a label with a hint' end end 如果您更喜欢无模式表单由主表单拥有,则可以通过将TModelessForm.CreateParams替换为: procedure TModelessForm.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; end; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |