2014-06-17 3 views
-1

프로그램 시작시 OnActivate 이벤트 처리기에서 몇 초 동안 프로그램을 차단하는 작업을 수행해야합니다. 이 시간 동안 양식의 클라이언트 영역은 여전히 ​​완벽하게 채색되지 않았으므로 사용자에게는보기 흉한 것처럼 보입니다. (이 차단 된 시간 동안 나는 프로그램이 클릭이나 다른 사용자 동작에 응답 할 필요가 없으므로 차단 작업을 스레드에 넣을 필요가 없습니다. 완전히 칠하려면 폼이 필요합니다.) 그래서 난 아주 잘 작동 차단 작업을하기 전에 양식을 업데이트 TForm.UpdateApplication-ProcessMessages를 사용TForm에 대한 OnShown 이벤트?

procedure TForm1.FormActivate(Sender: TObject); 
begin 
    Form1.Update; 
    Application.ProcessMessages; 
    Sleep(7000); 
end; 

그러나, 나는이 문제에 대한 또 다른 더 우아한 해결책이 아닌지 궁금합니다. 예를 들어, OnShown 이벤트가 TForm의 자손에 구현되어있을 수 있습니다.이 이벤트는 양식이 완전히 그려진 후에 시작됩니다. 그러한 사건은 어떻게 실행될 수 있습니까?

+1

예를 들어 http://www.swissdelphicenter.com/torry/showcode.php?id=1276을 참조하십시오. 하지만 나는 Stack Overflow에 관한 질문이 있다고 확신한다. – TLama

+0

메시지 게시 (제공된 링크와 마찬가지로) 특정 상황에서 메시지 (AFAIK)가 손실 될 수 있으므로 IMO는 완전히 신뢰할 수 없습니다. 200 % 신뢰할 수있는 무언가가 필요합니다. – user1580348

+0

메시지가 손실되지 않습니다. 그러나'PostMessage'는 큐가 꽉 차면 큐에 넣을 수 없으며'PostMessage' 결과 매개 변수로 그 큐를 검색 할 수 있습니다. –

답변

2

실제 문제는 UI 스레드를 차단하고 있다는 것입니다. 간단히 말해서, 절대 그렇게해서는 안됩니다. 장시간 실행중인 작업을 다른 스레드로 옮기면 UI가 응답을 유지할 수 있습니다.

+0

일반적으로 동의합니다. 내 구체적인 목적을위한이 특별한 경우에는 이것이 문제가되지 않습니다. – user1580348

+1

응용 프로그램이 UI 메시지 대기열을 펌프하지 못하게하고 시스템 고스트가 응답하지 않는 것처럼 보이게하는 것은 분명 당신에게 달려 있습니다. 나는 그렇게하지 않을 것이다. 그래서 나는 위의 것을 썼다. 그러나 당신이 오히려 그렇게한다면 그것은 분명히 당신의 선택입니다. –

0

당신이 응용 프로그램은 응용 프로그램이 사용자 입력을받을 읽어되면이 이벤트가 발생한다

http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Forms.TApplication.OnIdle

TApplication.OnIdle 이벤트를 사용해야 재 도장 /로드가 완료되면 해고 이벤트를 찾고 있습니다. 참고이 이벤트는 응용 프로그램이 유휴 상태가 될 때마다 발생하므로 OnIdle이 처음으로 실행되었을 때 전화 할 수있는 일부 제어 변수를 구현해야합니다.

David가 이미 지적했듯이 UI (메인 스레드)를 차단하는 것은 좋지 않습니다. 왜? 주 스레드를 차단하면 응용 프로그램이 정상적으로 메시지를 처리 ​​할 수 ​​없습니다. 이로 인해 OS가 응용 프로그램을 "Hanged"로 인식 할 수 있습니다. 그리고 아마도 사용자가 데이터 유실로 이어질 가능성이있는 응용 프로그램을 강제로 종료하게 만들 수 있기 때문에이 문제를 피하고 싶을 것입니다. 또한 Windows 이외의 다른 플랫폼을위한 응용 프로그램을 설계하고 싶다면 응용 프로그램이 실패 할 수도 있습니다.

+0

헉. OnIdle은 포스터가 찾고있는 것이 아닙니다. OnActivate를 사용하는 것보다 훨씬 자주 발생하며 악화됩니다. 백그라운드 스레드로 이동할 수있는 모든 것은 아닙니다. 예를 들어, DBGrid에 데이터를 표시하는 CRUD 응용 프로그램은 먼저 DB에 연결해야하며 DB 연결은 액세스 가능하도록 주 스레드에 속해야합니다. 이 연결은 사용자의 UI에 액세스 할 수 없기 때문에이 연결을 보조 스레드로 이동할 수 없습니다. –

0

과거에 간단한 PostMessage가 트릭을 수행했습니다. 은 기본적으로 당신은 기본 폼의 DoShow 중에 화재 :

procedure TBaseForm.DoShow; 
begin 
    inherited; 
    PostMessage(Handle, APP_AFTERSHOW, 0, 0); 
end; 

다음 MSG를 잡아이 기본 폼에서 상속 된 모든 형태에 대한 AfterShow 이벤트를 만들 수 있습니다.

하지만 더 이상 작동하지 않습니다. 스키닝하고 많은 수의 VCL 컨트롤이있는 경우에는 좋지 않습니다.

내 다음 트릭은 DoShow에서 간단한 스레드를 생성하고 IsWindowVisible (Handle) 및 IsWindowEnabled (Handle)을 확인하는 것이 었습니다. DB 열기와 다른 것들은 이미 AfterShow 이벤트에 있었기 때문에로드 시간으로부터 250ms 가량 줄어 들었습니다.

그런 다음 마침내 madHooks를 생각해 보았습니다. API ShowWindow를 응용 프로그램에 연결하고 APP_AFTERSHOW를 실행하기에 충분했습니다.

function ShowWindowCB(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall; 
begin 
    Result := ShowWindowNext(hWnd, nCmdShow); 
    PostMessage(hWnd, APP_AFTERSHOW, 0, 0); 
end; 

procedure TBaseForm.Loaded; 
begin 
    inherited; 
    if not Assigned(Application.MainForm) then // Must be Mainform it gets assigned after creation completes 
    HookAPI(user32, 'ShowWindow', @ShowWindowCB, @ShowWindowNext); 
end; 

는 모든 것이 완전히 여전히 ProcessPaintMessages 내 최종 테스트가 AfterShow 이벤트에 잠을 추가하고 완전히 빈 그린 양식을 볼 수

procedure TBaseForm.APPAFTERSHOW(var AMessage: TMessage); 
begin 
    ProcessPaintMessages; 
    AfterShow; 
end; 

procedure ProcessPaintMessages; // << not tested, pulled out of code 
var 
    msg: TMsg; 
begin 
    while PeekMessage(msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) do 
     DispatchMessage(msg); 
end; 

이었다 전화를 필요 AfterShow 전에 페인트를 얻으려면 AfterShow 이벤트가 아직 완료되지 않았으므로 db 컨테이너.

procedure TMainForm.AfterShow; 
begin 
    inherited; 
    Sleep(8*1000); 
......