2009-10-02 5 views
0

그래서 작은 Twitter 클라이언트를 쓰고 있습니다. 나는 하나의 커다란 패널과 개별 트윗을 나타내는 작은 패널의 조합을 사용하고있다. 더 작은 각각의 패널에는 PictureBox와 RichTextBox가 있습니다.BackgroundWorker의 컨트롤로 패널 채우기

내 문제는 동적으로 패널을 생성하기 때문에 10 개 이상의 트윗을로드하면 속도가 느려지 게된다는 것입니다. 그래서 BackgroundWorker를 사용하여이 작업을 수행 한 다음이 패널을 기본 패널에 추가하기로 결정했습니다.

저는이 텍스트를 다른 thead의 텍스트 상자에 쓰는 것을 여러 번했습니다 (자습서도 썼습니다). 그러나 나는 이것을 작동시킬 수 없다.

Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.

코드 :

List<Panel> panelList = new List<Panel>(); 

foreach (UserStatus friendStatus in list) 
{ 
    PictureBox pbTweet = new PictureBox(); 
    // ... 
    // code to set numerous properties 
    // ... 

    RichTextBox rtbTweet = new RichTextBox(); 
    // ... 
    // code to set numerous properties 
    // ... 

    Panel panelTweet = new Panel(); 
    // ... 
    // code to set numerous properties 
    // ... 

    panelTweet.Controls.Add(pbTweet); 
    panelTweet.Controls.Add(rtbTweet); 

    panelList.Add(panelTweet); 
} 

if (panelMain.InvokeRequired) 
    panelMain.BeginInvoke((MethodInvoker)delegate { foreach (Panel p in panelList) { panelMain.Controls.Add(p); } }); 

누구나 어떤 문제가 나타날 나는 오류 메시지가?

+0

어떤 줄이 오류를 던지고 있습니까? –

+0

BeginInvoke 메서드는 foreach를 수행 할 때 오류를 throw합니다. –

답변

1

백그라운드 스레드를 사용하는 경우 양식 컨트롤을 수정하는 부분에서 데이터를 검색하는 부분을 완전히 분리해야합니다. 폼 컨트롤을 수정하는 모든 코드는 UI 스레드에서 호출해야하는데, 수행하는 데는 시간이 걸릴 수도 있습니다. 이 방법은 없습니다.

일반적으로 데이터를 메모리로 가져 오는 것이 느린 부분이고 UI를 업데이트하는 것이 (서로 상대적으로) 빠른 부분입니다.

코드 예제에서 모든 코드는 UI 수정 부분이므로 모두 UI 스레드에 있어야합니다.

편집 :는 UI 부분을 최적화하기 위해, 당신은 당신이 수정하는 패널에 SuspendLayoutResumeLayout를 호출 실험 할 수있다.

+0

감사. SuspendLayout 및 ResumeLayout을 가지고 놀기 시작했고, 내가 원하는 것에 가깝게 작동하는 것 같습니다. –

4

panelTweetBackgroundWorker의 스레드에 생성되며 위임자의 기본 스레드 (panelMain.Controls.Add(p);// p = panelTweet)에서 액세스합니다.

마지막 부분뿐만 아니라 메인 스레드에서 모든 기능을 호출해야합니다.


이 같은 기능을 다시 작성할 수 있습니다 :

private void AddControls() 
{ 
    if(panelMain.InvokeRequired) 
    { 
     panelMain.BeginInvoke(new MethodInvoker(AddControls)); 
     return; 
    } 

    foreach (UserStatus friendStatus in list) 
    { 
     PictureBox pbTweet = new PictureBox(); 
     // ... 
     // code to set numerous properties 
     // ... 

     RichTextBox rtbTweet = new RichTextBox(); 
     // ... 
     // code to set numerous properties 
     // ... 

     Panel panelTweet = new Panel(); 
     // ... 
     // code to set numerous properties 
     // ... 

     panelTweet.Controls.Add(pbTweet); 
     panelTweet.Controls.Add(rtbTweet); 

     panelMain.Controls.Add(panelTweet) 
    } 
} 
+0

아. 나는 바보처럼 느껴진다. Ok, 하나의 컨트롤이 UI 스레드로 생성되고 다른 컨트롤이 Backgroundworker 스레드에서 만들어 지므로 어떻게 추가합니까? 내가 붙어있는 것처럼 보일 것이다. –

+0

UI 스레드에서 모든 컨트롤을 만드는 것이 좋습니다. 내 ansewer에 UI 스레드에서이 모든 작업을 수행하는 방법의 예를 추가했습니다. – manji

0

당신은에서 모든 피조물을 넣어 다시의 WinForm 스레드 Y.

외부 스레드 X에 생성 패널의 페이지를 추가하려고를 BeginInvoke 핸들러. 이렇게하면 모든 컨트롤이 winform 스레드에서 생성됩니다.

+0

BackgroundWorker를 사용하는 목적을 상실하지 않습니까? 컨트롤을 추가 할 때 여전히 UI가 응답하지 않게됩니까? –

+0

내 대답은 najmeddine과 동일합니다. 나는 컨트롤의 생성이 로딩 속도를 늦추지 않고 오히려 데이터를 얻는다고 생각한다. –

0

그래, 답을 보면, 나는 단지 SOL 인 것처럼 보입니다. UI 스레드에서 모든 처리를해야하므로 응답하지 않게됩니다.

+0

elder_george가 말했듯이 가벼운 컨트롤을 만들어야합니다. Win32 컨트롤 (따라서 WinForms 래퍼)은 실제로 리소스를 잡습니다. OnPaint를 호출 할 때 사용자의 이미지 (graphics.DrawImage (...))와 해당 트윗 (TextRenderer.DrawText (...))을 그릴 수있는 자체 컨트롤을 만들 수 있습니다. 그것은 더 가볍습니다. 또는 WP32를 사용할 수 있습니다. WPF는 Win32 컨트롤 주위의 래퍼가 아니며 이론적으로 더 가볍습니다. –

+0

나는 이미 UserControls가 표준 컨트롤보다 항상 "무겁다"는 것을 읽었습니다. –

1

ProgressChanged 처리기에서 컨트롤을 만들려고 할 수 있습니다. 이렇게하면 백그라운드 스레드에서 초기화 (사용자 그림 검색 등)를 수행하고 GUI 스레드에서 시각화를 수행 할 수 있습니다.

성능 문제는 RichTextEdit 및 PictureBox를 만드는 데 필요한 큰 리소스 때문일 수 있습니다. Paint 이벤트에 렌더링 된 사용자 및 텍스트의 이미지 만 포함하는 맞춤 컨트롤 만들기를 생각해보십시오.

1

백그라운드 스레드에서 WinForms UI 컨트롤을 만들 수 없습니다.

이 주위에 몇 가지 방법이있다 - I로 시작하는 것 : - 그것은 가치가있을 수

foreach (UserStatus friendStatus in list) 
    panelMain.BeginInvoke(
     delegate (object o) { 
      panelMain.Controls.Add(getPanelForUser(o as UserStatus)); 
     }, 
     friendStatus); 

여전히 비록 속도가 느릴 수 있습니다 귀하의 배경 작업자 그런

Control getPanelForUser(UserStatus friendStatus) { 
    PictureBox pbTweet = new PictureBox { /* set props */ }; 
    RichTextBox rtbTweet = new RichTextBox { /* set props */ }; 

    Panel panelTweet = new Panel { /* set props */ }; 

    panelTweet.Controls.Add(pbTweet); 
    panelTweet.Controls.Add(rtbTweet); 

    return panelTweet; 
} 

하위 집합을로드 한 다음 다른 곳으로 급지합니다. 또한 보이는 목록 만로드 할 수 있습니다. 스크롤 할 때까지 숨길 수 있습니다. 그런 다음 한 번에 한 페이지 씩로드합니다.