web-dev-qa-db-fra.com

les formulaires wsMaximized n'apparaissent pas maximisés

Définir un formulaire sur WindowState = wsMaximized entraînera parfois son agrandissement, mais pas:

enter image description here

Bug de longue date: voici une question que j'ai posée pour la première fois dans les groupes de discussion Borland en 2003:

et puis encore en 2006:

et puis encore en 2008:

Quelqu'un l'a demandé sur les forums Embarcadero en 2012:

Il est maintenant temps de porter le bogue de 18 ans dans Stackoverflow. Peut-être que quelqu'un a finalement trouvé une solution de contournement.

Etapes pour reproduire:

Mes messages contenaient une demi-douzaine de modes d'échec, mais le plus simple est:

  • Déposez une Label et une Edit sur un formulaire:

    enter image description here

  • Ajoutez un événement OnEnter pour la TEdit:

    procedure TForm1.Edit1Enter(Sender: TObject);
    begin
       Label1.Font.Style := Label1.Font.Style + [fsBold];
    end;
    
  • et définir le formulaire: 

    • WindowState à wsMaximized
    • AutoScroll à False

Et bazinga, échoue.

Une des autres étapes du post de 2008:

  1. Créez une nouvelle application et un formulaire.
  2. Définissez le formulaire sur maximisé (WindowState = wsMaximized) au moment de la conception.
  3. Déposer un contrôle ListView sur le formulaire
  4. Pendant OnShow, ajoutez 20 éléments vides à la vue liste:

    procedure TForm1.FormShow(Sender: TObject);
    var
         i: Integer;
    begin
         for i := 1 to 20 do
              ListView1.Items.Add;
    
    end;
    
  5. Définissez la propriété AutoScroll du formulaire sur false (AutoScroll = False) au moment du design

Bien sûr, je suis non après _ "corrigé dans la version n de RadStudio. Utilisez simplement cela". Je cherche un correctif réel (s'il en existe un); ce qui pourrait inclure des modifications pertinentes apportées à la source VCL lorsque CodeGear a finalement résolu le problème. (Si c'est même fixé).

Note: Changer Position de poDesigned en toute autre chose ne résout pas le problème.

Workaround

Une solution de contournement horrible, laide, horrible, dégueulasse que j'utilisais était de démarrer une minuterie pendant OnShow, puis, lorsque la minuterie se déclenche, maximisait la forme:

procedure TForm1.tmrVclMaximizeHackTimer(Sender: TObject);
begin
   Self.WindowState := wsMaximized;
end;

J'ai plus tard amélioré ce bidouillage pour poster un message pendant OnShow; qui est essentiellement identique à un message de temporisateur, sans avoir à utiliser de temporisateur:

const
  WM_MaximizeWindow = WM_APP + $03;

procedure TForm1.FormShow(Sender: TObject);
begin
  if (Self.WindowState = wsMaximized) then
  begin
     Self.WindowState := wsNormal;
     PostMessage(Self.Handle, WM_MaximizeWindow , 0, 0);
  end;
end;

private
   procedure WMMaximizeWindow(var Message: TMessage); message WM_MaximizeWindow;

procedure TForm1.WMMaximizeWindow(var Message: TMessage);
begin
   Self.WindowState := wsMaximized;
end;

Parfois, j'invente l'événement OnAfterShow que Delphi n'a jamais fait:

const
  WM_AfterShow = WM_APP + $02;

procedure TForm1.FormShow(Sender: TObject);
begin
  PostMessage(Self.Handle, WM_AfterShow, 0, 0);
  if (Self.WindowState = wsMaximized) then
  begin
     Self.WindowState := wsNormal;
     FMaximizeNeeded := True;
  end;
end;

private
   procedure WMAfterShow(var Message: TMessage); message WM_AfterShow;

procedure TForm1.WMAfterShow(var Message: TMessage);
begin
   if FMaximizeNeeded then
   begin    
      FMaximizeNeeded := False;
      Self.WindowState := wsMaximized;
   end;
end;

Mais aucun hacks ne vaut mieux que des hacks.

34
Ian Boyd

Je peux reproduire avec D7/Win7. 

Je n'utilise pas wsMaximized du tout (problèmes aléatoires similaires à ceux que vous décrivez). 

Solution de contournement:} _ utilise OnActivate -> ShowWindow(Handle, SW_MAXIMIZE) e.g .: 

procedure TForm1.FormActivate(Sender: TObject);
begin
  // Maximize only once when the Form is first activated
  if not FMaxsimized then
  begin
    FMaxsimized := True;
    ShowWindow(Handle, SW_MAXIMIZE);
  end;
end;

Cette méthode va pas fonctionner pendant OnShow.

Meilleure solution de contournement:} _ utilisez ShowWindowAsync pendant OnShow ou OnCreate e.g:

procedure TForm1.FormCreate(Sender: TObject);
begin
  ShowWindowAsync(Handle, SW_MAXIMIZE);
end;

Ceci définit l'état d'affichage d'une fenêtre sans attendre la fin de l'opération.

11
kobik

Je n'ai testé que le premier boîtier de reproduction (avec D7, D2007, XE2) et je suis en mesure de reproduire le problème avec D7 et D2007, mais pas avec XE2. 

Le problème, à mon avis, est que l'étiquette, ayant sa police de caractère modifiée, demande à son parent de se réaligner. Cela conduit finalement à un appel SetWindowPos sur le formulaire (dans TWinControl.AdjustSize) avec la largeur/hauteur restaurée même si le formulaire est déjà maximisé - ce qui conduit à l'étrange, comportement optimisé au comportement mais pas maximisé visuellement , assis sur l'écran.


J'ai tracé le code dans D2007 et XE2 pour pouvoir trouver ce qui est différent. Le code dans TWinControl.AlignControls est différent entre les deux versions. Ce qui compte spécifiquement, c'est la dernière déclaration.

D2007:

procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);

  ..
  { Apply any constraints }
  if Showing then AdjustSize;
end;

XE2:

procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);

  ..
    // Apply any constraints
    if FAutoSize and Showing then
      DoAdjustSize;
end;

J'espère que cela vous aidera, d'une manière ou d'une autre, à concevoir/décider de la solution de contournement à utiliser .



La solution de contournement que je pourrais suggérer (bien que je n’aie pas fait de test complet) consiste à forcer l’affichage de la forme maximisée au début:

procedure TForm1.FormCreate(Sender: TObject);
var
  wplc: TWindowPlacement;
begin
  if not AutoScroll and (WindowState = wsMaximized) then begin
    wplc.length := SizeOf(wplc);
    GetWindowPlacement(Handle, @wplc);
    wplc.rcNormalPosition.Right := wplc.rcNormalPosition.Left + Width;
    wplc.rcNormalPosition.Bottom := wplc.rcNormalPosition.Top + Height;
    wplc.showCmd := SW_MAXIMIZE;
    SetWindowPlacement(Handle, @wplc);
  end;
end;

Ce qui précède fonctionne parce qu'il oblige à définir le focus sur le contrôle d'édition (événement OnEnter) avant que la VCL ne mette l'indicateur visible pour le formulaire. À son tour, la demande d'alignement de l'étiquette ne résulte pas d'un ajustement de la taille du formulaire. De plus, comme la VCL appelle ShowWindow la fenêtre du formulaire est déjà visible, le formulaire ne sera pas affiché dans un état restauré, à aucun stade.

Cependant, je ne sais pas si cela aiderait avec différents scénarios de reproduction.


Enfin, bien que je puisse voir que le comportement est corrigé dans les nouvelles versions de Delphi, je ne considérerais pas cela comme un bogue de la VCL. À mon avis, le code utilisateur devrait être responsable de ne pas causer d'ajustement de la fenêtre pendant que la fenêtre affichant l'état change. La ligne de conduite que je prendrais pour le scénario spécifique serait de différer la modification de la police de l'étiquette jusqu'à ce que la VCL ait terminé d'afficher le formulaire.

8
Sertac Akyuz

Je ne pense pas qu'il s'agisse d'un bogue dans Delphi, mais plutôt d'un bogue (ou simplement d'un comportement étrange) dans la fonction Windows CreateWindow. Si vous recherchez CreateWindow et WS_MAXIMIZE ne fonctionnant pas, vous constaterez que des discussions et discussions déjà très anciennes sont proposées par des personnes appelant CreateWindow ou CreateWindowEx en passant WS_MAXIMIZE dans le paramètre de style et ne voyant pas une fenêtre agrandie lors de l'exécution de l'application.

Extrait d'un ancien fil de gamedev.net

le problème est que WS_MAXIMIZE ne s’applique apparemment pas à l’utilisation de WS_OVERLAPPEDWINDOW. Si vous remplacez WS_OVERLAPPEDWINDOW par WS_POPUP, vous obtiendrez une fenêtre agrandie. Bien entendu, cela peut ne pas s'appliquer à toutes les versions de Windows, ni même à toutes les versions de l'interface utilisateur Windows Shell.

WS_OVERLAPPEDWINDOW est l'ancien type de fenêtre par défaut de Microsoft. Apparemment, CreateWindow/Ex a été codé pour ignorer certains styles, pensant que ShowWindow serait appelé avec SW_SHOWDEFAULT, ce qui entraîne l'affichage de la fenêtre conformément aux paramètres d'information de démarrage de CreateProcess. cela donne finalement à l'utilisateur le contrôle de l'affichage de la fenêtre principale d'une application à l'aide des paramètres de raccourci du shell.

La solution de contournement consiste simplement à appeler ShowWindow. Cela devrait aussi fonctionner à Delphi:

procedure TForm1.FormShow(Sender: TObject);
begin
   ShowWindow(Handle, SW_MAXIMIZE);
end;
1
gordy

J'espère que la solution que j'utilise aide les autres (la fenêtre que je connais est d'abord affichée avec la taille de conception):

  • Ajoutez une minuterie avec un intervalle aussi petit que 1 (ne mettez pas 0).
  • Code pour cela: theTimer.Enabled:=False;WindowState:=wsMaximized;

Cela ne me manque jamais.

Dès que le formulaire est affiché et que toutes les tâches en attente sont terminées, le timer se déclenche et la fenêtre est maximisée.

Il y a quelque temps, j’utilisais l’astuce d’envoyer un clic de souris lorsque le bouton d’optimisation était, mais j’ai découvert que vérifier la version de Windows OS, les plugins (sous Linux), etc. rend la chose tellement difficile. Celui-ci a fonctionné exactement comme si l'utilisateur demandait de maximiser la fenêtre.

Le minuteur que j’utilise maintenant fait exactement la même chose, mais évite de vérifier le système d’exploitation, etc.

Sans oublier: Placez WindowState sur wsNormal sur DesignTime (ne le définissez pas sur wsMinimized, ni sur wsMaximized).

0
claudio

Hou la la! je n'ai pas vu sur le post:

ShowWindowAsync (Handle, SW_MAXIMIZE);

Merci pour cela, avec ça mon problème est résolu beaucoup mieux qu'avec un Timer, mais pas parfait, il provoque toujours un petit scintillement (la fenêtre est montrée sur le rendu incomplet, puis passe à l'état maximisé), il est bien meilleur que le timer , moins de temps affiché dans un état non maximisé.

Et il est compatible avec un SetBounds () précédent sur la méthode OnShow.

Je souhaite: définir la taille initiale (largeur, hauteur) et peut-être aussi la position initiale (gauche, haut) d'un formulaire avant de l'afficher, mais ce formulaire doit être affiché au maximum; de telles positions et tailles sont pour quand l'utilisateur dé-maximise la fenêtre.

Cette ShowWindowAsync fonctionne parfaitement pour un tel objectif, et il n'est pas nécessaire d'ajouter un minuteur horrible (avec intervalle = 1 et .Enabled = False sur son code en tant que première phrase).

Comment pourrais-je le manquer quand je lis le post!

Donc, maintenant, je vais utiliser (à titre d'exemple, la taille initiale de l'OS par rapport au moniteur):

procedure TtheForm.FormShow(Sender: TObject);
var
   theInitialDefaultWidth,theInitialDefaultHeight:Integer;
begin
     theInitialDefaultWidth:=Round(Screen.Width*3/5);
     theInitialDefaultHeight:=Round(Screen.Height*3/5);
     WindowState:=wsNormal; // So it can still have at design time wsMaximized, this is for the SetBounds to work on a non maximized state
     SetBounds((Screen.Width-theInitialDefaultWidth)div 2,(Screen.Height-theInitialDefaultHeight)div 2,theInitialDefaultWidth,theInitialDefaultHeight); // Set default position and default size as i wish
     ShowWindowAsync(Handle,SW_MAXIMIZE); // Make the window to be shown maximized when it will be visible
     // ... // Rest of actions for the FormShow method
end;

Fonctionne parfaitement! Je n'ai pas besoin de toucher aux propriétés de conception, je peux les laisser telles quelles (WindowState = wsMaximized, Position = poScreenCenter, etc.) .. Solution de code à 100% pour le problème.

Merci beaucoup!

P.D .: Cela fonctionnera-t-il sur Linux? Je veux dire quand le code est compilé pour Linux (dans Lazarus), je dois le tester et voir, si cela fonctionne, ce sera un excellent moyen d'améliorer ce que j'utilisais jusqu'à maintenant.

0
claudio