формы wsMaximized не отображаются развернутыми

32

Установка формы в WindowState = wsMaximized иногда приведет к максимизации формы, но не:

Изображение 687

Долгосрочная ошибка: это вопрос, который я впервые задал в группах новостей Borland в 2003 году:

а затем снова в 2006 году:

а затем снова в 2008 году:

Кто-то спросил его на форумах Embarcadero в 2012 году:

Теперь пришло время перенести 18-летнюю ошибку в Stackoverflow. Возможно, кто-то, наконец, выяснил обходное решение.

Шаги по воспроизведению:

Мои сообщения содержали полдюжины отказов, но самый легкий из них:

  • Отпустите Label и Edit в форме:

    Изображение 688

  • Добавьте OnEnter событие TEdit:

    procedure TForm1.Edit1Enter(Sender: TObject);
    begin
       Label1.Font.Style := Label1.Font.Style + [fsBold];
    end;
    
  • и задайте форму:

    • WindowState wsMaximized
    • AutoScroll до 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;

Но никакие хаки не лучше хаков.

  • 0
    Есть ли различия версий, о которых следует знать? Помечены D5 и D7? Терпит неудачу в обоих? У меня тоже нет, только D6. Если я отлажу там, этого будет достаточно.
  • 0
    Ну, я не могу воспроизвести здесь, в D6. Нужно ли использовать Windows 2000 или что-то? ;-)
Показать ещё 4 комментария
Теги:
delphi-7
vcl
delphi-5

5 ответов

7

Я тестировал только первый случай воспроизведения (с D7, D2007, XE2) и умею дублировать проблему с D7 и D2007, но не с XE2.

Проблема, как я вижу, заключается в том, что ярлык с измененным шрифтом требует, чтобы его родительский элемент снова выравнивался. Это в конечном итоге приводит к вызову SetWindowPos в форме (в TWinControl.AdjustSize) с восстановленной шириной/высотой, даже если форма уже максимизирована, что приводит к тому, что на экране сидит странная, поведенчески максимизированная, но не визуально максимизированная форма.


Я проследил код в D2007 и XE2, чтобы иметь возможность разобраться с чем-то другим. Код в 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 не будет отображать форму.

  • 1
    +1! Я предпочитаю ваше решение с SetWindowPlacement , потому что форма отображается сразу с максимизированным состоянием (в отличие от моего решения, где окно показывается нормальным, пока оно не развернуто). Кроме того, Ваш метод работает независимо wsMaximized того, wsMaximized ли wsMaximized или нет. (лично я бы не использовал wsMaximized , пропустил if not AutoScroll проверку wsMaximized , if not AutoScroll , и использовал SetWindowPlacement для максимизации). Также молодец для анализа :).
  • 0
    @kobik - Спасибо! - "... опустите если не AutoScroll .." - Просто хотел быть как можно менее навязчивым к VCL.
Показать ещё 1 комментарий
7

Я могу воспроизвести с D7/Win7.

Я вообще не использую wsMaximized (подобные случайные проблемы, которые вы описываете).

Обходной путь: использовать OnActivateShowWindow(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;

Это устанавливает состояние отображения окна, не дожидаясь завершения операции.

  • 0
    Вы не захотите максимизировать форму во время OnActivate , иначе форма будет пытаться максимизировать, когда она активирована (т.е. каждый раз, когда она активируется)
  • 5
    @ Ян Бойд, обходной путь с использованием ShowWindowAsync довольно элегантный, не так ли? Конечно, лучше, чем взломать.
Показать ещё 2 комментария
1

Я не думаю, что это ошибка в 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;
  • 0
    Я так не думаю. Переключение «автопрокрутки» для исправления проблемы предполагает, что ОС не имеет никаких проблем с ней. Если вы проследите код VCL, вы увидите, что сценарий в вопросе приводит к вызову SetWindowsPos для развернутого окна с восстановленной шириной / высотой, что, вероятно, является частью проблемы.
  • 0
    Также обратите внимание, что окно думает, что оно развернуто; значок границы имеет его в развернутом состоянии. За исключением того, что это не совсем так. я даже увижу ошибку в сторонних приложениях; мертвая распродажа, что это приложение Delphi (это и неправильно нарисованные окна всплывающей подсказки)
0

Ничего себе! я не видел в сообщении:

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), я должен проверить его и увидеть, если он работает, это будет большим недостатком в том, что я использовал до сих пор.

0

Надеюсь, что решение, которое я использую, помогает другим (я открыл окно с указанием размера времени разработки):

  • Добавьте таймер с интервалом меньше 1 (не ставьте 0).
  • Код для него: theTimer.Enabled:=False;WindowState:=wsMaximized;

Это никогда не подводится мне.

Как только отображается форма, и все ожидающие задачи для такого шоу закончены, таймеры и окно максимизируются.

Некоторое время назад я использовал трюк для отправки щелчка мыши, где была кнопка максимизации, но я обнаружил, что проверял версию ОС Windows, плагины (на Linux) и т.д. делает так сложно. Это работало точно так, как будто пользователь запрашивает максимальное окно.

Таймер, который я сейчас использую, делает то же самое, но избегайте проверки ОС и т.д.

Не говоря уже: поместите WindowState в wsNormal на DesignTime (не устанавливайте его в wsMinimized, ни в wsMaximized).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню