Delphi: перерисовать форму в FormShow

Я открываю Form2.ShowModal в FormMain. Я хочу, чтобы приложение отображало форму Form2 без изменений при доступе к базе данных (речь не идет о новых отображаемых данных). Однако при выполнении FormShow отображается только внешняя граница и некоторые сломанные части, некоторые сломанные части FormMain просвечивают. Это ужасно.

Мне не удалось найти способ заставить Delphi немедленно перерисовать форму, а затем выполнить трудоемкую процедуру MyOpenData. После заключения MyOpenData все в порядке.

procedure TForm2.FormShow(Sender: TObject);
begin
  Invalidate; 
  Refresh;
  MyOpenData; { needs some seconds of database accesses }
end;

Альтернатива:

procedure TForm2.FormShow(Sender: TObject);
begin
  Invalidate;
  Refresh;
  SendMessage(Handle, wm_paint, 0, 0);
  PostMessage(Handle, wm_OpenMyData, 0, 0); { executes well, but no solution)
end;

Это тоже не работает. Я думал, что SendMessage() ждет сообщения. Но Paint не выполняется до MyOpenData. Форма всегда выглядит сломанной, пока процедуры не закончатся. Кроме того, процедуры выполняются нормально. Я пробовал все эти команды вместе или по отдельности.

Что мне не хватает? Заранее спасибо!

Как запустить трудоемкие процедуры, которые необходимо выполнять при открытии формы?

(Delphi XE7 в 64-разрядной версии Windows 7)


person HJay    schedule 21.07.2015    source источник
comment
Вы должны использовать class(TThread) для подготовки этих данных в фоновом режиме, не нарушая взаимодействие пользователя с формой. Я не занимаюсь Delphi уже много лет, так что я просто оставлю это вам. Вот несколько примеров: stackoverflow.com/a/3456816/156811   -  person Havenard    schedule 22.07.2015
comment
В альтернативе удалите первые три строки, и это ваше решение. Что вы подразумеваете под нет решения?   -  person Sertac Akyuz    schedule 22.07.2015
comment
@Havenard: MyOpenData() не является потокобезопасным, но довольно часто обращается к форме и нескольким объектам. Однако предполагается, что он запускается ПОСЛЕ того, как форма перекрасится, чтобы выглядеть неповрежденной. Вот о чем мой вопрос.   -  person HJay    schedule 22.07.2015
comment
@Sertac: я пробовал все попытки вместе и по отдельности. Это просто не работает. Форма выглядит сломанной, пока MyOpenData не завершится. Должна быть возможность принудительно перерисовать форму еще до запуска MyOpenData.   -  person HJay    schedule 22.07.2015
comment
@HJay - Отсрочка обработки - правильный курс действий, ваша форма даже не видна в OnShow. Удалите первые три строки во втором фрагменте, в процедуре OpenMyData вызовите «Обновить» в качестве первого оператора. Затем откройте свои данные.   -  person Sertac Akyuz    schedule 22.07.2015
comment
@HJay Потоки потокобезопасны, если вы делаете их такими. Потоки в Delphi особенно легко сделать безопасными с помощью метода Synchronize. Проверьте delphi.about.com/od/kbthread/a/thread-gui .htm   -  person Havenard    schedule 22.07.2015
comment
Отсрочка выполнения позволит закрасить окно. Конечно, вы по-прежнему будете блокировать основной поток, поэтому ваше окно не будет отвечать на запросы и, вероятно, будет скрыто. Переместите длинный код в поток. Никогда не пытайтесь синтезировать такие сообщения красками. Вы не можете. Только система может.   -  person David Heffernan    schedule 22.07.2015
comment
Наше предпочтительное решение — добавить пользовательское сообщение WM_AFTERSHOW, которое публикуется в событии OnShow. Делайте данные открытыми. Лучшее решение, конечно, сделать это в потоке, который никогда не блокируется!   -  person mrabat    schedule 22.07.2015
comment
@Sertac: Что ж, перенос обновления в процедуру сообщения кардинально улучшил ситуацию. Не идеально, но вполне.   -  person HJay    schedule 22.07.2015
comment
@mrabat: вы имеете в виду, как в моем альтернативном примере кода? Это тоже не сработало.   -  person HJay    schedule 22.07.2015
comment
@David: Как в моем альтернативном примере кода, или что ты имеешь в виду? -- Да, похоже, действительно невозможно отобразить окно, а затем сделать что-то еще. Странно, ведь очень многим нужно, чтобы такие подпрограммы выполнялись после FormShow?   -  person HJay    schedule 22.07.2015
comment
Конечно, можно нарисовать окно, а затем запустить активность. Отсрочка с опубликованным сообщением - это способ. Но вы собираетесь заблокировать поток пользовательского интерфейса. Не делай этого.   -  person David Heffernan    schedule 22.07.2015
comment
@David: Итак, как лучше всего перерисовать, а затем запустить активность в вашем опыте (без использования потоков)?   -  person HJay    schedule 22.07.2015
comment
@David: использование потоков затруднено, если данные должны отображаться один за другим. Чтобы избежать доступа к VCL, во многих случаях требуется много усилий. -- Я считаю, что блокировка пользовательского интерфейса не самое худшее, если пользователь не может ничего сделать, пока данные не будут готовы. Но, конечно, вы правы, в идеальном мире пользовательский интерфейс не блокируется, а каким-то образом отключается.   -  person HJay    schedule 22.07.2015
comment
Нити - это ответ. Хватит прятаться от правды.   -  person David Heffernan    schedule 22.07.2015
comment
@HJay, что вы имеете в виду, когда перенос обновления в процедуру сообщения резко улучшил ситуацию. Что тебе еще надо? то же самое можно сделать при событии OnActivate (с последующим обновлением и MyOpenData)   -  person kobik    schedule 22.07.2015
comment
@kobik: Да, я пока доволен. Это придется сделать. Большое спасибо всем участникам. -- OnAcitivate вызывается гораздо чаще, чем для обработки начальной процедуры. Чего не хватает, так это события OnFormShowFinished... после того, как форма нарисована, активирована... затем вызовите подпрограмму.   -  person HJay    schedule 22.07.2015
comment
@HJay, вы бы установили начальный флаг OnAcitivate в первый раз. форма становится полностью видимой в этом событии.   -  person kobik    schedule 22.07.2015
comment
@HJay, я думаю, твоя проблема в другом. Ответ Чарльза должен работать. У нас никогда не возникает проблем с использованием этого метода. Обратите внимание, что если форма занята и затемнена, а затем снова сделана видимой, она вернется к частично (или не) окрашенной.   -  person Rohit Gupta    schedule 23.07.2015
comment
@HJay: Да, но без ваших обновлений. Для меня этот механизм aftershow работает - он показывает основную форму, а затем обрабатывает сообщение. Но, конечно, он рисуется только один раз и зависает для более длительной обработки (это то, чего вы хотите избежать???) В любом случае - я думаю, что ответы - это темы.   -  person mrabat    schedule 23.07.2015
comment
Три ответа со всеми отрицательными голосами. Очень интересно. Можно подумать, что люди здесь кое-что знают, но похоже, балом правит эго, а не опыт.   -  person David Schwartz    schedule 25.07.2015


Ответы (3)


Здесь просто недостаточно информации, чтобы давать какие-либо конкретные рекомендации, ИМО.

Я собираюсь предположить, что MyOpenData() устанавливает какое-то состояние данных, на которое опирается Form2. Если это так, то вы, вероятно, захотите вызвать его ДО вызова Form2.ShowModal. Ни в коем случае вы не должны вызывать аннулирование или обновление внутри обработчика OnShow, потому что они оба запускают OnShow.

Посмотрите видео, которое я сделал для CodeRage 9, под названием «Вы в последнее время занимались программным обеспечением?» (поищите на YouTube «coderage software plumber»), так как это именно та тема, к которой я обращаюсь в этом видео — целое множество проблем, связанных с инициализацией форм и объектов, а также проблемы синхронизации, характерные для форм.

Я не обсуждаю конкретно проблемы с элементами управления с учетом данных, но они почти одинаковы. Может быть проблематично настроить состояние БД внутри формы, которая зависит от этого состояния, чтобы правильно инициализировать себя. Существует неотъемлемое состояние гонки, которого легко избежать, сначала выполнив зависимую инициализацию, а затем внедрив эту зависимость в форму.

Если задействованы БД, вам нужно ЧТО-ТО внедрить в форму: либо ссылку на БД (обычно через глобальную переменную); таблица (либо глобальная переменная, либо переменная формы); или текущая запись (обычно переменная формы). Преимущество использования элементов управления с поддержкой БД заключается в том, что инициализация всегда выполняется неявно, и вам не нужно ничего вводить. Плохая часть использования элементов управления с поддержкой БД заключается в том, что инициализация ВСЕГДА НЕЯВНАЯ, и у вас нет ЯВНОГО контроля над последовательностями инициализации. Выполняя ЯВНУЮ инъекцию зависимостей БД, вы обходите проблемы синхронизации. Это немного больше работы (не много), но вам не нужно иметь дело с такими вещами.

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

person David Schwartz    schedule 22.07.2015
comment
Почему Invalidate запускает OnShow. Я ожидаю, что это вызовет OnPaint. Кроме того, проблема, с которой сталкивается спрашивающий, кажется, не связана с вашим видео. Проблема — не что иное, как наш старый друг — длительная задача, блокирующая поток графического интерфейса. - person David Heffernan; 22.07.2015
comment
Проблема в основном не в блокировке графического интерфейса, а в том, как заставить форму полностью отрисовываться перед переходом к следующей команде. Жаль, что нет команды, которая заставила бы форму рисовать себя именно в этот момент. В этом проблема. -- Конечно, вы правы, трудоемкие подпрограммы можно было бы выполнять в отдельном потоке, если бы необходимые действия были потокобезопасными, чего в моем случае нет. Вот почему поток здесь не является решением. - person HJay; 22.07.2015
comment
Нить - это решение. GUI не может рисовать сам себя, если его поток заблокирован. - person David Heffernan; 22.07.2015
comment
Проблема в том, как заставить форму полностью закрасить себя перед переходом к следующей команде. Было бы очень полезно узнать, как инициализируются поля формы. Являются ли они компонентами с поддержкой БД или просто компонентами, заполненными каким-то другим способом? У вас есть классическое состояние гонки, когда поля в форме инициализируются в то же время, когда форма пытается их отобразить. OnShow — это неправильное место для размещения кода, который ИНИЦИАЛИЗИРУЕТ поля! Их нужно инициализировать ДО вызова OnShow, ИМХО. OnCreate может быть лучшим местом для этого. Откуда данные? - person David Schwartz; 24.07.2015
comment
Поток, безусловно, является одним из решений. Но немного изменить структуру вашего дизайна, чтобы все происходило в ожидаемой последовательности, вероятно, проще. - person David Schwartz; 24.07.2015
comment
@David Никакая реструктуризация не поможет, если поток пользовательского интерфейса заблокирован - person David Heffernan; 25.07.2015
comment
Раньше я работал над редактором, который обрабатывал массу строк в области генетического и геномного анализа. Некоторые статистические функции будут работать в течение нескольких минут. Мы нигде не использовали нити, и у нас не было проблем с зависанием пользовательского интерфейса. Это был 100% Делфи. Еще одно большое приложение, над которым я работал, делало макеты страниц с повторным потоком, который занимал 15-30 секунд. Никакие потоки не использовались, и пользовательский интерфейс никогда не блокировался. Другое приложение будет выдавать внутренние запросы перед открытием данных в форме; они занимают 10-15 секунд. Никогда ничего не закрывалось. Элементы управления с учетом данных не используются. Все дело в том, как вы проектируете решение. - person David Schwartz; 25.07.2015

Это должно работать.

  • Поместите таймер на форму и установите для Enabled значение False в инспекторе объектов.
  • В FormCreate установите для Enabled значение True
  • В событии OnTimer поместите свой код.

Вы должны угадать значение Interval. Если в FormCreate не так много компонентов или много кода, то достаточно нескольких 100 мс. В противном случае ставьте то, что работает - 500 или даже 1000 мс.

person Rohit Gupta    schedule 22.07.2015
comment
Это почти так же плохо, как и ваша первая попытка. У нас все еще есть реентерабельная обработка сообщений. Теперь мы можем взаимодействовать с формой до того, как она начнет работать. Примите еще один минус от меня. - person David Heffernan; 23.07.2015
comment
Вы получили голос за ответ ProcessMessages (который вы удалили) и один за этот ответ. Оба ответа обычно вызывают бурю отрицательных голосов. Мне это довольно трудно объяснить. - person David Heffernan; 23.07.2015
comment
@DavidHeffernan, это не реентерабельная обработка сообщений. Другой был, поэтому я удалил. Новое сообщение помещается в очередь и обрабатывается упорядоченным образом. Мне кажется, что вы позволяете своему мнению обо мне окрашивать ваше суждение о моем ответе. В любом случае, что вас больше озадачивает: отсутствие бури отрицательных голосов или наличие одного положительного голоса. Не все будут думать, как вы. :-) - person Rohit Gupta; 23.07.2015
comment
У меня нет мнения о тебе. У меня есть мнение об ответе. Он реентерабельный в том смысле, что вы позволяете другим сообщениям обрабатываться до начала работы. Точно так же, как ваша первая попытка. За исключением того, что этот вариант хуже, потому что он представляет большее окно возможностей. - person David Heffernan; 23.07.2015

person    schedule
comment
'PostMessage(Self.Handle, WM_AFTER_SHOW, 0, 0);' это именно то, что я делаю в своем альтернативном примере кода. Это не решило проблему. Однако, включая «Обновление;» в AfterShow это почти решил. - person HJay; 22.07.2015
comment
Вам не нужно два из них, оба сообщения будут получены последовательно. - person Sertac Akyuz; 22.07.2015