2008-11-03 4 views
4

내 응용 프로그램에는 DataGridView 개체와 MousePos 형식의 목록이 있습니다. MousePos는 마우스 X, Y 좌표 ("Point"유형) 및이 위치의 실행 횟수를 보유하는 사용자 정의 클래스입니다. 매초마다 이벤트를 발생시키고, 마우스 위치를 확인하고,이 목록에서 마우스 위치의 수를 추가 및/또는 업데이트하는 스레드 (System.Timers.Timer)가 있습니다.데이터를 안전하게 채우고 다중 스레드 응용 프로그램에서 DataGridView를 새로 고치려면 어떻게해야합니까?

사용자가 볼 수 있도록 DataGridView를 자동으로 Refresh()하는 두 번째 이벤트를 다시 발생시키는 비슷한 실행 스레드 (다시 말하면 System.Timers.Timer가 좋은 선택입니다)를 갖고 싶습니다. 화면의 데이터가 업데이트됩니다. (TaskManager와 마찬가지입니다.)

DataGridView.Refresh() 메서드를 호출하면 VS2005가 실행을 중지하고 크로스 스레딩 상황에 처해 있음을 알 수 있습니다.

내가 제대로 이해 해요, 난 지금 3 개 스레드가 :

  • 기본 UI 스레드
  • MousePos 목록 스레드 (타이머)
  • 된 DataGridView 새로 고침 스레드 (타이머)

을 내가 주 스레드에서 DataGridView를 새로 고침() 할 수 있는지 확인하려면 DataGridView.Refresh()라는 양식에 단추를 추가했지만이 (이상하게) 아무것도 수행하지 않았습니다. DataGridView.DataSource = null로 설정하고 내 목록으로 돌아 가면 DataGrid를 새로 고칩니다.

  1. 가 DataGridView.DataSource 설정되어 : 그리고 실제로이 있지만 버튼을 통해 (. 어떤이 기본 스레드에서 처리됩니다)


    그래서이 질문은 두 파트너 사에 설정되어있다, 일 null 및 다시 내 목록에 데이터 그리드를 새로 고칠 수있는 방법? (나에게 비효율적 인 것 같습니다 ...)

  2. 안전하게 멀티 스레드 환경에서 어떻게해야합니까?

다음은 다른 모든 컨트롤과 같은 메인 UI 스레드에서 그리드를 업데이트해야 내가 지금까지 작성한 코드 (C#을 /. 닷넷 2.0)

public partial class Form1 : Form 
{ 
    private static List<MousePos> mousePositionList = new List<MousePos>(); 
    private static System.Timers.Timer mouseCheck = new System.Timers.Timer(1000); 
    private static System.Timers.Timer refreshWindow = new System.Timers.Timer(1000); 

    public Form1() 
    { 
     InitializeComponent(); 
     mousePositionList.Add(new MousePos()); // ANSWER! Must have at least 1 entry before binding to DataSource 
     dataGridView1.DataSource = mousePositionList; 
     mouseCheck.Elapsed += new System.Timers.ElapsedEventHandler(mouseCheck_Elapsed); 
     mouseCheck.Start(); 
     refreshWindow.Elapsed += new System.Timers.ElapsedEventHandler(refreshWindow_Elapsed); 
     refreshWindow.Start(); 
    } 

    public void mouseCheck_Elapsed(object source, EventArgs e) 
    { 
     Point mPnt = Control.MousePosition; 
     MousePos mPos = mousePositionList.Find(ByPoint(mPnt)); 
     if (mPos == null) { mousePositionList.Add(new MousePos(mPnt)); } 
     else { mPos.Count++; } 
    } 

    public void refreshWindow_Elapsed(object source, EventArgs e) 
    { 
     //dataGridView1.DataSource = null;    // Old way 
     //dataGridView1.DataSource = mousePositionList; // Old way 
     dataGridView1.Invalidate();      // <= ANSWER!! 
    } 

    private static Predicate<MousePos> ByPoint(Point pnt) 
    { 
     return delegate(MousePos mPos) { return (mPos.Pnt == pnt); }; 
    } 
} 

public class MousePos 
{ 
    private Point position = new Point(); 
    private int count = 1; 

    public Point Pnt { get { return position; } } 
    public int X { get { return position.X; } set { position.X = value; } } 
    public int Y { get { return position.Y; } set { position.Y = value; } } 
    public int Count { get { return count; } set { count = value; } } 

    public MousePos() { } 
    public MousePos(Point mouse) { position = mouse; } 
} 
+0

: 다른 스레드에서 DataGridView에 전화를하려고한다면 그래서, 당신은 다음을 수행해야합니다? – Pretzel

답변

5

업데이트! - 난 부분적으로이 책에 부분에 # 1을 답을 알아 낸 "프로 .NET 2.0 C#에서 Windows Forms의 및 고객 제어"

내가 원래 새로 고침()이 아니라고 생각했다 무엇이든하고 난 Invalidate() 메서드를 호출하여 Windows가 내 컨트롤을 여유롭게 다시 칠하도록해야한다고했습니다. (대개는 곧바로 적용되지만 다시 칠하기위한 보증이 필요하면 ), 즉시 Update() 메소드를 호출하십시오.)

dataGridView1.Invalidate(); 

그러나, 그것은 새로 고침() 방법은 단지의 별칭입니다 밝혀 :

나는이 발견 유일한 결함이었다
dataGridView1.Invalidate(true); 
    dataGridView1.Update();    // <== forces immediate redraw 

그에 데이터가 없었다 경우 dataGridView, 아무리 많은 양의 무효화해도 컨트롤을 새로 고칩니다. 데이터 소스를 재 할당해야했습니다. 그 후 그것은 잘 작동했습니다. 그러나 행의 양 (또는 내 목록의 항목)에 대해서만 - 새 항목이 추가되면 dataGridView는 표시 할 행이 더 많다는 것을 알지 못합니다.

데이터 소스 (목록 또는 테이블)를 데이터 소스에 바인딩 할 때 dataGridView가 항목 (행)을 계산 한 다음 내부적으로 설정하고 새로운 행/항목 또는 행이 있는지 확인하지 않는 것으로 보입니다/items 삭제됨 이것이 데이터 소스를 반복적으로 바인딩하는 것이 이전에 작동했던 이유입니다.

데이터 소스를 다시 바인딩하지 않고도 dataGridView에 표시 할 행 수를 업데이트하는 방법을 알아 냈습니다. 재미 있고 재미 있고 재미 있습니다!

보다는 System.Timers를 사용하여 : :-)


뒷조사를하고 후, 난 내 질문의 2은 (. 일명 안전한 멀티 스레딩) 부품 번호에 대한 내 대답을 생각 .Timer, 나는 System.Windows.Forms.Timer을 사용해야합니다.

이벤트는 콜백에서 사용되는 메서드가 주 스레드에서 자동으로 발생하도록 발생합니다. 크로스 스레딩 문제가 없습니다!

private static System.Windows.Forms.Timer refreshWindow2; 
refreshWindow2 = new Timer(); 
refreshWindow2.Interval = 1000; 
refreshWindow2.Tick += new EventHandler(refreshWindow2_Tick); 
refreshWindow2.Start(); 

을 그리고 그 방법은 다음과 같습니다 :

선언은 다음과 같습니다 당신이 바로 답을 가지고있는 것처럼

private void refreshWindow2_Tick(object sender, EventArgs e) 
{ 
    dataGridView1.Invalidate(); 
} 
+2

DataSource를 재설정하는보다 우아한 방법은 BindingContext를 사용하고 대신 새로 고치는 것입니다. 매번 null로 재설정 할 필요가 없다는 의미입니다. 자동 업데이트에 관해서는 INotifyPropertyChanged를 구현하는 데이터 소스가 필요하다고 생각합니다. – Quibblesome

5

입니다. control.Invoke 또는 Control.BeginInvoke를 참조하십시오.

3

가 보이는! UAW에 크로스 스레드 호출을 수행하는 방법에 대해 궁금합니다. 모든 컨트롤에는 Invoke() 메서드 (또는 BEginInvoke() - 비동기로 작업하기를 원할 경우를 대비하여)가 있습니다. 메서드를 주 UI 스레드의 컨텍스트 내에서 제어합니다. 나는 허, 금지 사항입니다 제목에 [C 번호]를 넣어 같아요

public void refreshWindow_Elapsed(object source, EventArgs e) 
{ 

    // we use anonymous delgate here as it saves us declaring a named delegate in our class 
    // however, as c# type inference sometimes need a bit of 'help' we need to cast it 
    // to an instance of MethodInvoker 
    dataGridView1.Invoke((MethodInvoker)delegate() { dataGridView1.Invalidate(); }); 
}