2014-06-24 3 views
6

"백그라운드 작업자로부터 GUI 업데이트"이지만 정확한 이름은 다음과 같습니다. "백그라운드 작업자로부터 GUI 업데이트 또는 다중 변수보고 (정수) 배경 직원 "백그라운드 작업자로부터 GUI 업데이트

제 상황을 설명해주십시오. 프로그램에서 나는 정보를 분석하는 배경 작업자가 있습니다.이 분석의 결과 - 양식 GUI 요소는 필요한 데이터로 채워 져야합니다. GUI에서 나는

  • 2 datagridviews
  • (1) 목록 상자를
  • 5 라벨 업데이 트하려는

내가 알고있는 것처럼 - 내가 할 수있는 유일한 기본적으로 보고서 1 ReportProgress() 방법을 통해 int 배경 작업자입니다. ReportProgress()을 통해 : -

그래서 질문은 어떻게 List<> (string, int + 다른 변수)를 통과 할 수 있습니까? 기본적으로 - 나는 정보를 가진 GUI를 업데이트하고 싶지만 "1 정수"는하지 않을 것입니다. 따라서 ReportProgress()을 통해 여러 변수를 전달할 수 있어야합니다. 또는 Invoke을 사용하여 BackgroundWorker 자체에서 업데이트 할 수 있습니다. GUI를 .. 개인적으로 나는 Invoke 접근 방식을 좋아하지 않는다 ... 당신의 의견은 어떻습니까?

여기에 (주석을 참조) 내 코드입니다 : ReportProgress를 호출 할 때

private void button9_Click(object sender, EventArgs e) // start BW 
    { 
     bw.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); 
     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); 
     bw.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged); 

     bw.WorkerReportsProgress = true; 
     bw.WorkerSupportsCancellation = true; 

     bw.RunWorkerAsync(10); 
    } 

    private void button10_Click(object sender, EventArgs e) // cancel BW 
    { 
     bw.CancelAsync(); 
    } 

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     int count = (int)e.Argument; 
     for (int i = 1; i <= count; i++) 
     { 
      if (bw.CancellationPending) 
      { 
       e.Cancel = true; 
       break; 
      } 

      List<List<string>> list_result = new List<List<string>>(); 
      list_result = Proccess(); 

      bw.ReportProgress(list_result.Count()); // right now I can only return a single INT 

      /////////// UPDATE GUI ////////////// 
      // change datagridview 1 based on "list_result" values 
      // change datagridview 2 
      // change listbox 
      // change label 1 
      // change label ..   

      Thread.Sleep(20000); 
     } 

     MessageBox.Show("Complete!"); 
     e.Result = sum; 
    } 

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     prog_count++; 
     listBox1.Items.Add("Count: (" + prog_count.ToString() + "/20). Found: " + e.ProgressPercentage.ToString() + "."); 
    } 
+0

'Invoke'에 대해 마음에 들지 않는 점은 무엇입니까? –

+3

ReportProgress에는 전달할 수있는 [UserState] (http://msdn.microsoft.com/en-us/library/a3zbdb1t (v = vs.110) .aspx) 개체도 있습니다. – LarsTech

+1

@Savanna 그것은 내가 싫어 ... 나는 현재의 솔루션에 대한 대안/더 나은 관점을 찾고있다;) 어쩌면 BW에서 "새로운 스레드"로 이동하여 해결책이 될 것입니다 .. 개인적으로 나는 "If InvokeRequired, then ..."검사를해야하기 때문에 호출하는 것과 비슷하지만 다른 대안이 없다면이 하나를 고수해야한다. – Alex

답변

6

UserState 매개 변수가있다.

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    var userState = (List<List<string>>)e.UserState; 
} 

이와 까다로운 문제, 어떻게 당신이있어 여부를 결정합니까된다

var list_result = new List<List<string>>(); 

new backgroundWorker1.ReportProgress(0, list_result); 

매개 변수 유형은 object 그래서 당신이 필요로하는 유형에 다시 캐스팅 할 것입니다 List 또는리스트 목록 또는 단일 문자열, 숫자 등을 전달하십시오. ProgressChanged 이벤트에서 각 가능성을 테스트해야합니다.

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    var myList = e.UserState as List<List<string>>; 
    if (myList != null) 
    { 
     // use list 
     return; 
    } 

    int myNumber; 
    if (Int32.TryParse(e.UserState.ToString(), out myNumber)) 
    { 
     // use number 
     return; 
    } 

    var myString = e.UserState.ToString(); 
    // use string 
} 

또는, 당신은 당신이 필요로하는 모든 값을 유지하는 클래스를 만들 수 있습니다 (또는 Tuple 사용) 클래스를 채우는 백그라운드에서 실행하는 모든 것을, 다음 RunWorkerCompleted 이벤트에 그것을 통과하고, 모든 UI를 업데이트 거기에서 즉시.

+2

첫 번째 코드 단편은'list_result'라는 변수를 생성하지만 백그라운드 작업자의'ReportProgress' 메쏘드에 대해'myList' 라 불리는 정의되지 않은 것입니다. 'list_result'를 전달하려고 했습니까, 아니면 라인에서 빠져 나갔습니까? –

+0

@ TonyVitabile 감사합니다. 단지 복사/붙여 넣기 오류였습니다. 한 가지 방법으로 입력 한 다음 Alex의 변수와 일치하도록 변수의 이름을 변경했습니다. –

+0

또는 ... "e.ProgressPercentage"를 사용하여 개체 유형을 검색 할 수 있습니다. if (e.ProgressPercentage = 1) => object는 문자열입니다. "2"-> Obj는 Int이므로 다른 일은 "3"-> ... – Alex

0

다른 스레드에서 UI를 업데이트하는 기본 패턴은 다음과 같습니다

If controlItem.InvokeRequired Then 
    controlItem.Invoke(Sub() controlItem.Text = textUpdateValue) 
Else 
    controlItem.Text = textUpdateValue 
End If 

이 ReportProgress을 통해 아무것도 통과 할 필요없이 컨트롤의 목록을 업데이트 할 수 있습니다. 스레드 내에서 컨트롤을 업데이트하려면 InvokeRequired를 확인해야한다고 생각하지 않습니다. 항상 필요하기 때문입니다. 그러나 모범 사례는 속성을 통해 컨트롤의 설정을 노출 한 다음 전체 체크를 수행하여 어디서나 호출 할 수 있도록하는 것입니다.

4

저는 (필요한 경우에만) 코드를 호출 할 수있는 두 가지 매우 쉬운 방법을 작성했으며 코드를 한 번만 작성해야합니다.

1) BeginInvoke를

public static void SafeBeginInvoke(System.Windows.Forms.Control control, System.Action action) 
{ 
    if (control.InvokeRequired) 
     control.BeginInvoke(new System.Windows.Forms.MethodInvoker(() => { action(); })); 
    else 
     action(); 
} 

2)는 다음과 같이 호출 할 수 있습니다

public static void SafeInvoke(System.Windows.Forms.Control control, System.Action action) 
{ 
    if (control.InvokeRequired) 
     control.Invoke(new System.Windows.Forms.MethodInvoker(() => { action(); })); 
    else 
     action(); 
} 

를 호출 :

SafeInvoke(textbox,() => { textbox.Text = "text got changed"; }); 

또는 당신을 나는이 훨씬 친근 호출을 사용 할 수 있습니다 생각 could could just

System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false; 

(디버그 모드에서 동작이 바뀌기 만합니다.) 문제가 발생하면보세요.
실제로는 그렇지 않습니다. 케이스를 찾기에는 꽤 시간이 걸렸습니다. Invoke가 엉망이되지 않도록 꼭 필요합니다.

+2

[CheckForIllegalCrossThreadCalls을 false로 설정하는 것이 안전합니까?] (http://stackoverflow.com/a/13345622/719186) – LarsTech

관련 문제