작성날짜 : 2011-05-11 수정날짜 : 2015-05-28 |
Delegate 실습
시나리오
l 실행하면 먼저 ParentForm 창이 뜨고 현재 시간을 보여준다.
l 추가 버튼을 누르면 ChildForm 창이 뜨고 텍스트박스에 이름을 입력하고 추가 버튼을 누르면 추가된 멤버 수가 증가 된다.
l ChildForm 창을 닫으면 ParentForm ListView 에 ChildForm 에서 추가한 이름들이 전부 추가되어 보여준다.
ParentForm 창
ChildForm 창
Parent.cs
public partial class Parent : Form { System.Timers.Timer timer = null; private delegate void ViewNowTimeDelegate(string date); public Parent() { InitializeComponent(); CurrentTime(); } private void CurrentTime() { timer = new System.Timers.Timer(1000); timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); timer.Enabled = true; } void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { DateTime now; now = DateTime.Now; ViewNowTime(now.ToLongTimeString()); } private void ViewNowTime(string p) { //labelOfTime.Text = p; if (this.InvokeRequired == false) { labelOfTime.Text = p; } else { ViewNowTimeDelegate viewtime = new ViewNowTimeDelegate(ViewNowTime); object[] t = new object[] { p }; this.BeginInvoke(viewtime, p); //비동기 //this.Invoke(viewtime, t); //동기 } } private void buttonOfAdd_Click(object sender, EventArgs e) { Child f2 = new Child(); f2.MemEventHandler += new MemberEventHandler(f2_MemEventHandler); f2.ShowDialog(); } void f2_MemEventHandler(System.Collections.ArrayList mans) { foreach (Man man in mans) { listViewOfMember.Items.Add(man.Name); } } } |
Child.cs
public delegate void MemberEventHandler(ArrayList mans); public partial class Child : Form { public event MemberEventHandler MemEventHandler = null; ArrayList mans = new ArrayList(); int count = 0; public Child() { InitializeComponent(); } private void buttonOfAdd_Click(object sender, EventArgs e) { ++count; AddMan(); } private void AddMan() { Man man = new Man(); man.Name = textBoxOfName.Text; mans.Add(man); textBoxOfName.Text = string.Empty; labelOfCount.Text = count.ToString(); } private void Form2_FormClosed(object sender, FormClosedEventArgs e) { if (MemEventHandler != null) { MemEventHandler(mans); } } } |
실행 화면
실행하고 ParentForm 창에서 추가버튼을 누르면 ChildForm 창이 뜹니다. 그리고 ChildForm에서 이름을 입력하고 추가버튼을 누르면 추가된 멤버 수 count가 증가되고 ArrayList에 추가가 됩니다. 그리고 ChileForm을 닫으면 ParentForm의 ListBox에 ChildForm에서 추가했던 이름들이 뜨는 것을 확인 할 수 있습니다.
| |
크로스 스레드 해결방법
참고 사이트 : MSDN | http://msdn.microsoft.com/ko-kr/library/ms171728
버닝워커 블로그 | http://blog.naver.com/kkdcool?Redirect=Log&logNo=110054020805
크로스 스레드 해결방법에는 3가지가 있다. InvokeRequired 속성을 사용하는 방법, BackgroundWorker를 이용하는 방법, CheckForIllegalCrossThreadCalls = false의 방법이 있습니다. 그 중 InvokeRequired 속성을 사용하여 해결하였습니다.
Parent클래스에서 ViewNowTime 메소드를 보면 아래와 같이 주석처리된 부분이 있는 것을 볼수 있다.
//labelOfTime.Text = p; |
만일 이 부분을 주석 처리하지 않고 나머지 부분을 주석처리 한 후 실행해 보면 다음과 같은 에러를 볼 수 있을 것이다.
하나의 컨트롤에는 하나의 스레드만 작업을 수행할 수가 있는데. labelOfTime 컨트롤을 Parent Form의 스레드가 작업을 수행하려 했기 때문에 에러가 난 것이다. 이에 대한 해결 방안으로 Invoke를 써서 해결하였는데, Invoke를 설명하기에 앞서 InvokeRequired 속성먼저 설명하겠다. MSDN에는 ’InvokeRequired 속성’에 대하여 “호출자가 컨트롤이 만들어진 스레드와 다른 스레드에 있기 때문에 메서드를 통해 컨트롤을 호출하는 경우 해당 호출자가 호출 메서드를 호출해야 하는지 여부를 나타내는 값을 가져옵니다.” 라고 설명되어 있다. 그리고 속성 값에 대해서 “컨트롤의 Handle이 호출 스레드와 다른 스레드에서 만들어져 호출 메서드를 통해 해당 컨트롤을 호출해야 하는 경우 true이고, 그렇지 않으면 false입니다.” 라고 설명 되어 있다. 즉, 호출 메서드를 통해 해당 컨트롤을 호출하지 않을 경우 labelOfTime에 p값을 대입해 주고 그렇지 않을 경우 Invoke를 사용하여 처리하면 된다. ViewNowTimeDelegate 개체를 생성하고 동기식으로 하려면 Invoke를 비동기식으로 하려면BeginInvoke를 사용하면 된다.
InvokeRequired 속성에 대한 참고 : MSDN - Control.InvokeRequired 속성
'Programming > .NET' 카테고리의 다른 글
[ADO.NET] XmlReader - AttributeCount 속성 (0) | 2015.06.05 |
---|---|
[ADO.NET] XmlReader 소개 (0) | 2015.06.05 |
[.NET] Assembly - 2 (0) | 2015.05.19 |
[.NET] Delegate (대리자) - 2 (0) | 2015.05.19 |
[.NET] DataRepeater Control (0) | 2015.05.19 |