컴팩트 프레임워크/스레딩 - 옵션을 선택한 후 MessageBox가 다른 컨트롤 위에 표시됩니다.

StackOverflow https://stackoverflow.com/questions/10071

문제

저는 외부 서버에서 여러 업데이트를 가져와 설치하는 앱을 개발 중이며 스레딩에 대한 도움이 필요합니다.사용자는 다음 프로세스를 따릅니다.

  • 버튼 클릭
  • 메서드가 업데이트를 확인하고 개수가 반환됩니다.
  • 0보다 큰 경우 사용자에게 MessageBox.Show()를 사용하여 설치할 것인지 묻습니다.
  • 그렇다면 루프를 통해 실행되고 각 업데이트의 run() 메서드에서 BeginInvoke()를 호출하여 백그라운드에서 실행됩니다.
  • 내 업데이트 클래스에는 진행률 표시줄 등을 업데이트하는 데 사용되는 일부 이벤트가 있습니다.

진행률 표시줄 업데이트는 양호하지만 사용자가 예를 클릭한 직후 업데이트 루프가 시작되기 때문에 MessageBox가 화면에서 완전히 지워지지 않습니다(아래 스크린샷 참조).

  • 업데이트 루프가 시작되기 전에 메시지 상자가 즉시 사라지게 하려면 어떻게 해야 합니까?
  • BeginInvoke() 대신 스레드를 사용해야 합니까?
  • 별도의 스레드에서 초기 업데이트 확인을 수행하고 해당 스레드에서 MessageBox.Show()를 호출해야 합니까?

암호

// Button clicked event handler code...
DialogResult dlgRes = MessageBox.Show(
    string.Format("There are {0} updates available.\n\nInstall these now?", 
    um2.Updates.Count), "Updates Available", 
    MessageBoxButtons.YesNo, 
    MessageBoxIcon.Question, 
    MessageBoxDefaultButton.Button2
);

if (dlgRes == DialogResult.Yes)
{
    ProcessAllUpdates(um2); 
}

// Processes a bunch of items in a loop
private void ProcessAllUpdates(UpdateManager2 um2)
{
    for (int i = 0; i < um2.Updates.Count; i++)
    {
        Update2 update = um2.Updates[i];

        ProcessSingleUpdate(update);

        int percentComplete = Utilities.CalculatePercentCompleted(i, um2.Updates.Count);

        UpdateOverallProgress(percentComplete);
    }
}

// Process a single update with IAsyncResult
private void ProcessSingleUpdate(Update2 update)
{
    update.Action.OnStart += Action_OnStart;
    update.Action.OnProgress += Action_OnProgress;
    update.Action.OnCompletion += Action_OnCompletion;

    //synchronous
    //update.Action.Run();

    // async
    IAsyncResult ar = this.BeginInvoke((MethodInvoker)delegate() { update.Action.Run(); });
}

스크린샷

Windows Mobile Bug

도움이 되었습니까?

해결책

모든 작업이 사용자 인터페이스 스레드에서 발생하기 때문에 UI가 업데이트되지 않습니다.전화번호:

this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); }) 

사용자 인터페이스 스레드인 "this"(사용자 양식)를 생성한 스레드에서 update.Action.Run()을 호출한다고 말합니다.

Application.DoEvents()

실제로 UI 스레드에 화면을 다시 그릴 수 있는 기회가 제공되지만 새 대리자를 만들고 이에 대해 BeginInvoke를 호출하고 싶을 것입니다.

그러면 스레드 풀에서 할당된 별도의 스레드에서 update.Action.Run() 함수가 실행됩니다.그런 다음 업데이트가 완료될 때까지 IAsyncResult를 계속 확인하고, 모든 확인 후에 업데이트 객체의 진행 상황을 쿼리한 다음(다른 스레드에서 진행률 표시줄/UI를 업데이트하도록 할 수 없기 때문에) Application.DoEvents()를 호출할 수 있습니다.

또한 나중에 EndInvoke()를 호출해야 합니다. 그렇지 않으면 리소스가 누출될 수 있습니다.

또한 진행 대화 상자에 취소 버튼을 놓고 시간 제한을 추가하고 싶은 유혹을 느낄 것입니다. 그렇지 않으면 업데이트가 중단되거나 너무 오래 걸리면 응용 프로그램이 영원히 잠길 것입니다.

다른 팁

당신은

Application.DoEvents()

여기에서

if (dlgRes == DialogResult.Yes)
{
   Application.DoEvents(); 
   ProcessAllUpdates(um2); 
}

@ 존 시블리

당신은 도망 갈 수 있습니다 ~ 아니다 부정적인 결과 없이 WinForms를 처리할 때 EndInvoke를 호출합니다.

내가 알고 있는 규칙에 대해 문서화된 유일한 예외는 Windows Forms에 있습니다. Windows Forms에서는 Control.EndInvoke를 호출할 필요 없이 공식적으로 Control.BeginInvoke를 호출할 수 있습니다.

그러나 다른 모든 경우에는 시작/끝 비동기 패턴을 처리할 때 언급한 대로 누출이 발생한다고 가정해야 합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top