2012-12-31 3 views
5

WinForms C# 응용 프로그램에서 별도의 스레드를 사용하여 ProgressBar (마키)를 제어하는 ​​백그라운드 작업자를 시작하려고합니다. 문제는 내가 눈금 ​​표시를 설정하려고 할 때 아무 것도하지 않고 다양한 형태의 Invoke를 시도했지만 도움이되지 않는다는 것입니다.다른 스레드 내에서 UI 요소 조작

다음 방법 progressBarCycle은 별도 스레드에서 호출됩니다.

BackgroundWorker backgroundWorker = new BackgroundWorker(); 

    public void progressBarCycle(int duration) 
    { 
     backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); 
     backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); 
     backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted); 
     backgroundWorker.WorkerReportsProgress = true; 
     backgroundWorker.WorkerSupportsCancellation = true; 
     backgroundWorker.RunWorkerAsync(duration); 
    } 

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 

     worker.ReportProgress(0); 

     DateTime end = DateTime.Now.AddMilliseconds((int)e.Argument); 
     while (DateTime.Now <= end) 
     { 
      System.Threading.Thread.Sleep(1000); 
     } 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (!this.IsHandleCreated) 
      this.CreateHandle(); 
     statusStrip1.Invoke((MethodInvoker)delegate 
     { 
      progressBar1.Visible = false; 
     }); 
     // if (!this.IsHandleCreated) 
     // { 
     //  this.CreateHandle(); 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false)); 
     //  else progressBar1.Visible = false; 
     // } 
     // else 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false)); 
     //  else progressBar1.Visible = false; 
    } 

    private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     if (!this.IsHandleCreated) 
      this.CreateHandle(); 
     statusStrip1.Invoke((MethodInvoker)delegate 
     { 
      progressBar1.Visible = true; 
     }); 
     // if (!this.IsHandleCreated) 
     // { 
     //  this.CreateHandle(); 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true)); 
     //  else progressBar1.Visible = true; 
     // } 
     // else 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true)); 
     //  else progressBar1.Visible = true; 
    } 

내가 여기에 분명한 것을 놓치고 있습니까? 코멘트 섹션은 제가 시도한 다른 것들입니다.

답변

6

ProgressChanged입니다. 이미이 UI 스레드에서 발생했습니다 (sync-context를 통해). 귀하의 ProgressChanged은 그럴 필요가 없습니다. Invoke - UI를 직접 조작 할 수 있습니다 (대조적으로 DoWork은 절대적으로 이 아니라입니다). 아마도 실제 문제는 worker.ReportProgress(...)내부에 내부에 아무 것도하지 않으므로 시작시 한 번만 발생합니다. UI 스레드에서

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 
static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     using (var worker = new BackgroundWorker { 
      WorkerReportsProgress = true }) 
     using (var progBar = new ProgressBar { 
      Visible = false, Step = 1, Maximum = 100, 
      Dock = DockStyle.Bottom }) 
     using (var btn = new Button { Dock = DockStyle.Top, Text = "Start" }) 
     using (var form = new Form { Controls = { btn, progBar } }) 
     { 
      worker.ProgressChanged += (s,a) => { 
       progBar.Visible = true; 
       progBar.Value = a.ProgressPercentage; 
      }; 
      worker.RunWorkerCompleted += delegate 
      { 
       progBar.Visible = false; 
      }; 
      worker.DoWork += delegate 
      { 
       for (int i = 0; i < 100; i++) 
       { 
        worker.ReportProgress(i); 
        Thread.Sleep(100); 
       } 
      }; 
      btn.Click += delegate 
      { 
       worker.RunWorkerAsync(); 
      }; 
      Application.Run(form); 
     } 
    } 
} 
+0

전적으로 ProgressChanged 및 RunWorkerCompleted에 넣어서 문제가 해결되는지 확인합니다. DoWork에 코드를 모두 포함 시켰을 때 똑같은 일을 할 수있었습니다.Visible의 상태는 호출의 유무에 관계없이 변경되지 않습니다. - 아, 그리고 이것은 아마도 Marquee라고 언급 할 가치가 있습니다. 그래서 진도 보고서를 사용하지 않을 것입니다. – UncleDave

+0

@UncleDave 그러면 진행률 표시 줄이 보이지 않는 다른 컨트롤 (패널 일 가능성이 있음) 안에 있음을 알 수 있습니다. –

+0

진행률 표시 줄은 응용 프로그램의 시작 양식이기도 한이 코드를 실행하는 폼에 있습니다. – UncleDave

2
  1. 실행 progressBarCycle :

    여기에 전체 예입니다. RunWorkerAsync 새 스레드를 생성합니다.

  2. backgroundWorker_ProgressChanged에서 progressBar1.Visible = true;으로 간단히 전화하십시오. Invoke은 필요하지 않습니다.
  3. 또한 progressBar1.Refresh();을 추가하십시오.
2

또 다른 가능성은 진행률 표시 줄이 UI 스레드에서 실행되고 있다는 것입니다. 진행률 표시 줄을 표시하고 새로 진행률을 표시하기 위해 다시 그리기 위해 UI 스레드는 주 응용 프로그램 루프에서 Windows 메시지를 처리하면서 자유롭게 실행되어야합니다.

백그라운드 작업자 스레드를 시작한 후 UI 스레드가 완료 될 때까지 기다리는 바쁜 대기 루프에 있거나 그 밖의 많은 작업을 수행하면 Windows 메시지가 처리되지 않고 진행률 표시 줄에 "응답 없음"이 표시됩니다. 이 업데이트가 계속 발생하도록 (즉, 이벤트 핸들러에서 돌아와 정상적으로 UI가 계속 실행되도록) UI 스레드를 릴리스해야합니다.

UI가 활성화되어 있으면 사용자가 계속해서 대화 할 수 있습니다. 따라서 백그라운드 작업자가 활성 상태 일 때 알 수있는 UI를 작성하고 상황을 적절히 처리해야합니다 (문제는 다음을 포함 할 수 있음 : 이미 실행중인 백그라운드 작업자를 다시 시작하도록 허용, 작업자가 작업하는 동안 정보를 표시하려고하는 UI 스레드가 바쁘게 업데이트하는 중, 사용자가 새 문서를로드하기로 결정하거나 백그라운드 작업자가 통화 중일 때 종료하는 등). 이 두 가지 주요 솔루션은 백그라운드 작업이 실행되는 동안 위험한 것을 막는 보호막에 UI의 모든 비트를 래핑하는 것입니다 (이렇게하면 많은 방법으로 래핑 할 수 있습니다. 버그가 빠져 나가는 실수를하기 쉽습니다.) UI를 "비보호"로 남기고 IMessageFilter를 추가하여 들어오는 창 메시지 (WM_KEYPRESS 등)를 억제하여 모든 "위험한"사용자 상호 작용 (클릭 및 키 누르기)을 중지합니다. 백그라운드 처리가 활성화됩니다.