Установка формы в WindowState = wsMaximized
иногда приведет к максимизации формы, но не:
Долгосрочная ошибка: это вопрос, который я впервые задал в группах новостей Borland в 2003 году:
а затем снова в 2006 году:
а затем снова в 2008 году:
Кто-то спросил его на форумах Embarcadero в 2012 году:
Теперь пришло время перенести 18-летнюю ошибку в Stackoverflow. Возможно, кто-то, наконец, выяснил обходное решение.
Шаги по воспроизведению:
Мои сообщения содержали полдюжины отказов, но самый легкий из них:
Отпустите Label
и Edit
в форме:
Добавьте OnEnter
событие TEdit
:
procedure TForm1.Edit1Enter(Sender: TObject);
begin
Label1.Font.Style := Label1.Font.Style + [fsBold];
end;
и задайте форму:
WindowState
wsMaximizedAutoScroll
до FalseИ bazinga, терпит неудачу.
Один из других шагов от публикации в 2008 году:
- Создайте новое приложение и форму.
- Задайте форму для максимизации (WindowState = wsMaximized) во время разработки.
- Отбросить элемент управления ListView в форме
Во время OnShow добавьте 20 пустых элементов в список:
procedure TForm1.FormShow(Sender: TObject); var i: Integer; begin for i := 1 to 20 do ListView1.Items.Add; end;
Задайте для свойства AutoScroll формы значение false (AutoScroll = False) во время разработки
Конечно, то, что мне не нужно, "исправлено в версии n
RadStudio. Просто используйте это". Я ищу фактическое исправление (если оно есть); который может включать в себя цитирование соответствующих изменений в источнике VCL, когда CodeGear наконец-то его исправил. (Если он даже фиксирован).
Примечание: Изменение Position
от poDesigned ни к чему еще не исправляет.
Ужасный, уродливый, ужасный, отвратительный, обходной путь, который я использовал, заключался в том, чтобы запустить таймер во время OnShow
, а затем, когда срабатывает таймер, увеличьте форму:
procedure TForm1.tmrVclMaximizeHackTimer(Sender: TObject);
begin
Self.WindowState := wsMaximized;
end;
Я позже улучшил этот хак, чтобы опубликовать сообщение во время OnShow
; который по существу совпадает с сообщением таймера, без использования таймера:
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;
Иногда я придумываю событие OnAfterShow
, которое Delphi никогда не делал:
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;
Но никакие хаки не лучше хаков.
Я тестировал только первый случай воспроизведения (с D7, D2007, XE2) и умею дублировать проблему с D7 и D2007, но не с XE2.
Проблема, как я вижу, заключается в том, что ярлык с измененным шрифтом требует, чтобы его родительский элемент снова выравнивался. Это в конечном итоге приводит к вызову SetWindowPos
в форме (в TWinControl.AdjustSize
) с восстановленной шириной/высотой, даже если форма уже максимизирована, что приводит к тому, что на экране сидит странная, поведенчески максимизированная, но не визуально максимизированная форма.
TWinControl.AlignControls
отличается от двух версий. Что конкретно имеет значение, так это последнее утверждение.
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;
Надеюсь, это так или иначе поможет вам разработать/решить, какое обходное решение использовать.
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;
Вышеупомянутое работает, потому что оно принудительно устанавливает фокус на элемент управления редактированием (OnEnter
event), прежде чем VCL устанавливает видимый флаг для формы. В свою очередь, запрос выравнивания меток не приводит к настройке размера формы. Кроме того, поскольку к моменту, когда VCL вызывает ShowWindow
, окно формы уже видно, оно не вызывает отображение формы в восстановленном состоянии на любом этапе.
Однако я не знаю, поможет ли это с различными сценариями воспроизведения.
Наконец, хотя я вижу, что поведение исправлено в новых версиях Delphi, я бы не счел это ошибкой в VCL. На мой взгляд, пользовательский код должен нести ответственность за то, чтобы вызвать настройку окна, пока отображается состояние, показывающее состояние. Курсом действий, который я бы взял для конкретного сценария, было бы отложить до изменения шрифта ярлыка до тех пор, пока VCL не будет отображать форму.
SetWindowPlacement
, потому что форма отображается сразу с максимизированным состоянием (в отличие от моего решения, где окно показывается нормальным, пока оно не развернуто). Кроме того, Ваш метод работает независимо wsMaximized
того, wsMaximized
ли wsMaximized
или нет. (лично я бы не использовал wsMaximized
, пропустил if not AutoScroll
проверку wsMaximized
, if not AutoScroll
, и использовал SetWindowPlacement
для максимизации). Также молодец для анализа :).
Я могу воспроизвести с D7/Win7.
Я вообще не использую wsMaximized
(подобные случайные проблемы, которые вы описываете).
Обходной путь: использовать OnActivate
→ ShowWindow(Handle, SW_MAXIMIZE)
например.
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;
Этот метод не будет работать во время OnShow
.
Улучшение обходного пути: используйте ShowWindowAsync
во время OnShow
или OnCreate
например:
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowWindowAsync(Handle, SW_MAXIMIZE);
end;
Это устанавливает состояние отображения окна, не дожидаясь завершения операции.
OnActivate
, иначе форма будет пытаться максимизировать, когда она активирована (т.е. каждый раз, когда она активируется)
Я не думаю, что это ошибка в Delphi, а скорее ошибка (или просто странное поведение) в функции Windows CreateWindow. Если вы не работаете с CreateWindow и WS_MAXIMIZE, вы не найдете аналогичных очень старых потоков и обсуждений от людей, вызывающих CreateWindow или CreateWindowEx, передающих WS_MAXIMIZE в параметре стиля и не видящих максимально развернутого окна при запуске приложения.
Выдержка из старого потока gamedev.net
проблема заключается в том, что WS_MAXIMIZE, по-видимому, не применяется при использовании WS_OVERLAPPEDWINDOW. если вы замените WS_OVERLAPPEDWINDOW на WS_POPUP, вы получите максимальное окно. конечно, это может не относиться ко всем версиям Windows или даже к любым версиям пользовательского интерфейса оболочки Windows.
WS_OVERLAPPEDWINDOW - это старое стандартное окно MS по умолчанию, и они, по-видимому, закодировали CreateWindow/Ex, чтобы игнорировать определенные стили, считая, что ShowWindow будет вызываться с SW_SHOWDEFAULT, что заставляет окно отображать в соответствии с файлами начальной загрузки CreateProcess. это в конечном итоге дает пользователю контроль над тем, как будет отображаться главное окно приложения, используя настройки ярлыка оболочки.
Обходной путь - это просто вызвать ShowWindow. Он также должен работать в Delphi:
procedure TForm1.FormShow(Sender: TObject);
begin
ShowWindow(Handle, SW_MAXIMIZE);
end;
Ничего себе! я не видел в сообщении:
ShowWindowAsync (Handle, SW_MAXIMIZE);
Спасибо за это, с этим моя проблема решена намного лучше, чем с таймером, но не идеально, она все еще вызывает небольшое мерцание (окно отображается на неполном рендере, затем оно переходит в максимальное состояние), это намного лучше чем таймер, меньше времени, отображаемого в не максимизированном состоянии.
И он совместим с предыдущим SetBounds() в методе OnShow.
Я хочу: задайте начальный размер (Ширина, Высота) и, возможно, также начальную позицию (Левая, Верхняя) формы до ее отображения, но эта форма должна быть показана максимально; такое положение и размеры, когда пользователь не максимизирует окно.
Это ShowWindowAsync
отлично подходит для такой цели, и нет необходимости добавлять уродливый таймер (с интервалом = 1 и .Enabled = False в его коде в качестве первого предложения).
Как я мог пропустить это, когда прочитал сообщение?
Итак, теперь я буду использовать (как пример исходного размера относительно монитора):
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;
Прекрасно работает! Мне не нужно прикасаться к свойствам времени разработки, я могу позволить им, как они есть (WindowState = wsMaximized, Position = poScreenCenter и т.д.).. 100% -ное решение для решения этой проблемы.
Спасибо большое!
P.D.: Будет ли он работать в Linux? Я имею в виду, когда код скомпилирован для Linux (в Lazarus), я должен проверить его и увидеть, если он работает, это будет большим недостатком в том, что я использовал до сих пор.
Надеюсь, что решение, которое я использую, помогает другим (я открыл окно с указанием размера времени разработки):
theTimer.Enabled:=False;WindowState:=wsMaximized;
Это никогда не подводится мне.
Как только отображается форма, и все ожидающие задачи для такого шоу закончены, таймеры и окно максимизируются.
Некоторое время назад я использовал трюк для отправки щелчка мыши, где была кнопка максимизации, но я обнаружил, что проверял версию ОС Windows, плагины (на Linux) и т.д. делает так сложно. Это работало точно так, как будто пользователь запрашивает максимальное окно.
Таймер, который я сейчас использую, делает то же самое, но избегайте проверки ОС и т.д.
Не говоря уже: поместите WindowState в wsNormal на DesignTime (не устанавливайте его в wsMinimized, ни в wsMaximized).