transferir reiniciar pasar mac como cambiar archivos windows delphi windows-7 delphi-xe6 windows-95

reiniciar - como pasar de mac a windows



Cómo aparecer correctamente la forma no modal en la barra de tareas (1)

Estoy tratando de alcanzar el antiguo sueño de Delphi de tener una forma no modal en la barra de tareas.

¿Cuál es la forma correcta de tener una forma no modal en la barra de tareas?

Esfuerzo de investigación

Estos son mis intentos de resolver el problema. Hay muchas cosas que se necesitan para que se comporte correctamente , simplemente tener un botón en la barra de tareas no es una solución. Tener una aplicación de Windows se comporta correctamente como una aplicación de Windows debería ser mi objetivo.

Para aquellos que me conocen y cuán profundo es mi "esfuerzo de investigación" , aguarden porque será un paseo salvaje por un agujero de conejo.

La pregunta está en el título, también arriba de la línea horizontal de arriba. Todo a continuación solo sirve para mostrar por qué algunas de las sugerencias frecuentemente repetidas son incorrectas.

Windows solo crea como botón de barra de tareas para ventanas sin propietario

Inicialmente tengo mi "Forma Principal" , de la que muestro esta otra forma no modal:

procedure TfrmMain.Button2Click(Sender: TObject); begin if frmModeless = nil then Application.CreateForm(TfrmModeless, frmModeless); frmModeless.Show; end;

Esto muestra correctamente el nuevo formulario, pero no aparece un nuevo botón en la barra de tareas:

La razón por la que no se crea ningún botón en la barra de tareas es porque eso es por diseño. Windows solo mostrará un botón de la barra de tareas para una ventana que "no posee" . Esta forma no modal de Delphi es definitivamente propiedad . En mi caso, es propiedad de Application.Handle :

El nombre de mi proyecto es ModelessFormFail.dpr , que es el origen del nombre de clase de Windows Modelessformfail asociado con el propietario.

Afortunadamente, hay una forma de forzar a Windows a crear un botón de barra de tareas para una ventana, aunque la ventana sea propiedad de:

Solo usa WS_EX_APPWINDOW

La documentación de WS_EX_APPWINDOW de WS_EX_APPWINDOW dice:

WS_EX_APPWINDOW 0x00040000L Fuerza una ventana de nivel superior en la barra de tareas cuando la ventana está visible.

También es un truco conocido de Delphi para anular CreateParams y agregar manualmente el estilo WS_EX_APPWINDOW :

procedure TfrmModeless.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; //force owned window to appear in taskbar end;

Cuando ejecutamos esto, el formulario no modal recién creado obtiene su propio botón de barra de tareas:

Y hemos terminado? No, porque no se comporta correctamente.

Si el usuario hace clic en el botón de la barra de tareas frmMain , esa ventana no se adelanta. En cambio, se presenta la otra forma ( frmModeless ):

Esto tiene sentido una vez que comprenda el concepto de propiedad de Windows. Windows, por su diseño, traerá adelante cualquier formulario propiedad de un niño. Era todo el propósito de la propiedad: mantener los formularios propios sobre sus propietarios.

Haga que la forma realmente no poseída

La solución, como algunos de ustedes saben, es no luchar contra la heurística y las ventanas de la barra de tareas. Si quiero que el formulario no sea propietario, hazlo sin propietario.

Esto es (bastante) simple. En CreateParam obligue a las ventanas del propietario a ser 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;

Como PopupMode PopupParent , quería investigar si había una forma de usar las propiedades PopupMode y PopupParent para hacer que una ventana no PopupParent propiedad. Juro que leí un comentario (de usted, David) en algún lugar de SO, que dice que si pasó Self como el PopupParent , por ejemplo:

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;

se suponía que era la manera súper secreta de indicarle a Delphi que desea formar para tener "ningún dueño" . Pero no puedo encontrar el comentario en ningún lado ahora. Lamentablemente, ninguna combinación de PopupParent y PopupMode hace que un formulario no sea realmente propiedad:

  • PopupMode: pmNone
    • Propietario hwnd: Application.Handle/Application.MainForm.Handle
  • PopupMode: pmAuto
    • Propietario hwnd: Screen.ActiveForm.Handle
  • PopupMode: pmExplicit
    • PopupParent: nada
      • Propietario hwnd: Application.MainForm.Handle
    • PopupParent: AForm
      • Propietario hwnd: AForm.Handle
    • PopupParent: Self
      • Propietario hwnd: Application.MainForm.Handle

Nada de lo que podría hacer podría hacer que el formulario no tenga propietario (cada vez que compruebe con Spy ++).

Configuración de WndParent manualmente durante CreateParams :

  • hace que la forma no poseída
  • tiene un botón de barra de tareas
  • y ambos botones de la barra de tareas se comportan correctamente:

Y hemos terminado, ¿verdad? Ya me lo imaginaba. Cambié todo para usar esta nueva técnica.

Excepto que hay problemas con mi solución que parecen causar otros problemas: a Delphi no le gustó que cambiara a la propiedad de un formulario.

Sugerencia de Windows

Uno de los controles en mi ventana no modal tiene una herramienta:

El problema es que cuando aparece esta ventana de información sobre herramientas, provoca que se presente la otra forma ( frmMain , la modal). No gana foco de activación; pero ahora oscurece la forma en que fui visto:

La razón es probablemente lógica. Es probable que Delphi HintWindow sea ​​propiedad de Application.Handle o Application.MainForm.Handle , en lugar de ser propiedad del formulario del que debería ser titular:

Hubiera considerado esto un error en la parte de Delphi; usando el dueño equivocado.

Desvío para ver el diseño de la aplicación real

Ahora es importante que me tome un momento para mostrar que mi aplicación no es una forma principal y una forma no modal:

En realidad es:

  • una pantalla de inicio de sesión (una forma principal de sacrificio que se oculta)
  • una pantalla principal
  • un panel de control modal
  • que muestra la forma no modal

Incluso con la realidad del diseño de la aplicación, todo, excepto la propiedad indirecta de la ventana, funciona. Hay dos botones en la barra de tareas, y al hacer clic en ellos, se adelanta el formulario adecuado:

Pero todavía tenemos el problema de que la propiedad HintWindow traiga el formulario incorrecto hacia adelante:

ShowMainFormOnTaskbar

Fue cuando estaba intentando crear una aplicación mínima para reproducir el problema cuando me di cuenta de que no podía. Había algo diferente:

  • entre mi aplicación Delphi 5 portada a XE6
  • una nueva aplicación creada en XE6

Después de comparar todo, finalmente lo MainFormOnTaskbar := True al hecho de que las nuevas aplicaciones en XE6 agregan MainFormOnTaskbar := True por defecto en cualquier proyecto nuevo (presumiblemente para no romper las aplicaciones existentes):

program ModelessFormFail; //... begin Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TfrmSacrificialMain, frmSacrificialMain); //Application.CreateForm(TfrmMain, frmMain); Application.Run; end.

Cuando agregué esta opción, ¡la aparición de la información sobre herramientas no trajo el formulario incorrecto !:

¡Éxito! Excepto, las personas que saben lo que viene saben lo que viene . Mi formulario de inicio de sesión principal "sacrificial" muestra la forma principal "real", ocultándose:

procedure TfrmSacrificialMain.Button1Click(Sender: TObject); var frmMain: TfrmMain; begin frmMain := TfrmMain.Create(Application); Self.Hide; try frmMain.ShowModal; finally Self.Show; end; end;

Cuando eso sucede, y yo "inicio de sesión" , mi ícono de la barra de tareas desaparece por completo:

Esto sucede porque:

  • la forma principal de sacrificio no poseída no es invisible: así que el botón va con ella
  • la forma principal real es propiedad por lo que no obtiene un botón de barra de herramientas

Use WS_APP_APPWINDOW

Ahora tenemos la oportunidad de usar WS_EX_APPWINDOW . Quiero forzar que mi formulario principal, que es de mi propiedad, aparezca en la barra de tareas. Así que CreateParams y lo CreateParams a aparecer en la barra de tareas:

procedure TfrmMain.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; //force owned window to appear in taskbar end;

y le damos un giro:

¡Se ve muy bien!

  • dos botones de la barra de tareas
  • la información sobre herramientas no muestra el formulario de propietario incorrecto hacia adelante

excepto cuando hago clic en el primer botón de la barra de herramientas, aparece el formulario incorrecto. Muestra el modal frmMain , en lugar del modal actual frmControlPanel :

Presumiblemente porque el frmControlPanel recién creado era PopupParented a Application.MainForm en lugar de Screen.ActiveForm . Comprobar en Spy ++:

Sí, el padre es MainForm.Handle . Esto resulta ser debido a otro error en el VCL. Si el PopupMode del PopupMode es:

  • pmAuto
  • pmNone (si se trata de un formulario modal)

el VCL intenta utilizar Application.ActiveFormHandle como hWndParent . Desafortunadamente, luego verifica si el padre modal del formulario está habilitado:

if (WndParent <> 0) and ( IsIconic(WndParent) or not IsWindowVisible(WndParent) or not IsWindowEnabled(WndParent)) then

Por supuesto, el padre del formulario modal no está habilitado. Si lo fuera, no sería una forma modal. Entonces VCL vuelve a usar:

WndParent := Application.MainFormHandle;

Crianza manual

Esto significa que probablemente tenga que asegurarme manualmente (?) Configurar la crianza de emergencia.

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;

Excepto que tampoco funcionó. Al hacer clic en el primer botón de la barra de tareas, se activa el formulario incorrecto:

En este punto estoy completamente confundido. El padre de mi formulario modal debe ser frmMain , y lo es !:

¿Y ahora qué?

Tengo una idea de lo que podría estar pasando.

El botón de la barra de tareas es una representación de frmMain . Windows está trayendo eso para adelante.

Excepto que se comportó correctamente cuando MainFormOnTaskbar se estableció en falso.

Debe haber un poco de magia en Delphi VCL que causó corrección antes, pero se desactiva con MainFormOnTaskbar: = True , pero ¿qué es?

No soy la primera persona que quiere que una aplicación Delphi se comporte bien con la barra de herramientas de Windows 95. Y ya hice esta pregunta en el pasado, pero esas respuestas siempre estuvieron orientadas a Delphi 5 y es la ventana de enrutamiento central antigua.

Me dijeron que todo estaba arreglado en el marco de tiempo de Delphi 2007.

Entonces, ¿cuál es la solución correcta?

Lectura de bonificación


Me parece que el problema fundamental es que su forma principal es, a los ojos del VCL, no su forma principal. Una vez que arreglas eso, todos los problemas desaparecen.

Debieras:

  1. Llame a Application.CreateForm exactamente una vez, para obtener la forma principal real. Esa es una buena regla a seguir. Considere el trabajo de Application.CreateForm para crear la forma principal de su aplicación.
  2. Cree el formulario de inicio de sesión y configure su WndParent en 0 . Eso asegura que aparezca en la barra de tareas. Entonces muéstralo modalmente.
  3. Cree el formulario principal de la forma habitual llamando a Application.CreateForm .
  4. Establecer MainFormOnTaskbar para ser True .
  5. Establezca WndParent en 0 para la forma no modal.

Y eso es. Aquí hay un ejemplo completo:

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, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, 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, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, 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

Si prefiere que la forma no modal pertenezca al formulario principal, puede lograr eso al reemplazar TModelessForm.CreateParams con:

procedure TModelessForm.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; end;