Définir un formulaire sur WindowState = wsMaximized
entraînera parfois son agrandissement, mais pas:
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:
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
à wsMaximizedAutoScroll
à FalseEt bazinga, échoue.
Une des autres étapes du post de 2008:
- Créez une nouvelle application et un formulaire.
- Définissez le formulaire sur maximisé (WindowState = wsMaximized) au moment de la conception.
- Déposer un contrôle ListView sur le formulaire
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;
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.
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.
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.
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.
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 .
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.
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;
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):
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).
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.