Right now do !

[C#응용] WinForm의 MDI 그리고 Child와의 이상한 관계

by 지금당장해

 요즘 필자가 연재 하겠다고 뻥뻥 거렸던 예외에 대한 글도 마무리를 못지을 만한 일이 있다. (사실 엄격히 말하면 이 역식 변명인데 ^^) 수년을 끌어온 모 병원정보 시스템을 드디어 오픈 했기 때문이다. 현장에 주니어 개발자 한 분과 시니어 개발자 한 분이 몇 개월동안 오픈 준비를 했다. 워낙 그간 경험 해왔던 사이트 보다 병원 사이즈가 커서 이번에는 나도 몇 주간 시스템 오픈 지원 및 상황 통제를 하기 위해 현장에 나와 있다. 

 

 상당 기간동안 준비를 해왔고 기존 시스템과 연동하여 우리 제품의 사용 검증이 끝났음에도 불구하고 차세대 시스템에 적용중에 생기는 문제는 또 다른것 이였다. 현재 크고 작은 이슈들이 지나가고 있는 중이다. 그런데 몇 일 전에 이상한 이슈가 제기 되었다. 사용자가 우리 솔루션이 적용된 MDI Child Window를 사용하다가 자리를 뜨기 위해 화면 잠금 기능을 키고 다시 자리에 돌와와 이 창의 기능을 쓰려과 하면 소위 먹통인 상태가 된다는 것이다.

 

 우리 시니어 개발자가 "먹통"을 좀더 자세히 인터뷰 해봤다. 사용자 말이 화면은 정상적으로 보이는데 커맨드 버튼들이 동작을 안한다고 했단다. 우리 코드에서 이런 상황이 일어날 수 있는 상황은 HandleDestroyed 이벤트 함수에서 좀더 확실하고 빠른 가비지 컬렉션을 위해 이벤트 바인딩을 해제 하는 처리를 하고 있다는 것이다. 그런데 이상하다. 창이 버젓이 살아 있는데 파괴 이벤트가 전달될리가 있는가? 결론은 설마가 사람 잡았다. (참고로 우리 코드는 User control이며 이를 고객 시스템에 제공 했다.)

 

 원인은 이렇다. 위에서 이야기 한 고객 시스템의 "화면 잠금" 기능은 MDI화면 내의 Child Window를 Visible = false; 혹은 Hide함수를 호출하여 구현 하였단다. 똑 같이 재현 해봤다 . MDI 메인 창을 구성하고 Child 창을 구성하고 그 내부에 간단한 User control을 만들어 이런 상황을 재현 해봤다. ChildWin.Visible = false;를 만나는 순간 User control에 HandleDestroyed 이벤트 함수가 호출되는 것이 아닌가. 이를 MDI가 아닌 Main 창과 Child Window관계를 만들어 같은 실험을 해봤다. 그냥 창만 사라지고 HandleDestroyed 이벤트 함수 호출은 일어나지 않는다.

 

 

public partial class TestCtrl : UserControl
{

     protected override void OnHandleDestroyed(EventArgs e)
     {
         base.OnHandleDestroyed(e);

         MessageBox.Show("OnHandleDestroyed");

     }
}

<<실험 결과>>

 
우리 제품 컨트롤을 가정한 컨트롤(이 Class에 위와 같은 OnHandleDestroyed가 구현되어 있다.)이 생성되어 있는 Child창을 생성하고 Show()한다.
 

 

창이 사라지면서 OnHandleDestroyed가 호출되는 기 현상을 목격 할 수 있다.

 


 

 

 자료를 찾아 봤다. 원래 그렇단다. 아~~ 십수년을 주로 닷넷을 해왔는데 .... 이건 또 뭔가??? 무식에 대한 자책, 허망한 감정은 뒤로 한채.. 

 

그렇다면 우리 컨트롤의 HandleDestroyed 이벤트 함수에서 해왔던 일을 어떻게 하지??? 인터넷을 뒤지다 이런 아이디어를 봤다. 아버지 창이 닫히는 이벤트(FormClosed)에 자원 해재 하는 코드를 쓰라는 것이다. 좋은 의견이다. 그런데 아버지 창이라 .... 우린 아버지 창을 주입 받을 수 없는 상황이다. 게다가 우리 컨트롤의 사용 위치는 너무다 다양하다. 즉, 아버지 윈도우 객체가 창이 아닐 수 도 있다는 것이다. 그래서 아버지를 찾는 방법을 고안 했다. Form Class를 만날때 까지 계속 아버지 윈도우 객체를 찾아 올라가는 함수다. 

 

protected override void OnLoad(EventArgs e) 
{ 
    base.OnLoad(e); 
    Form form = GetParentForm(this); 
    if (form != null) 
    { 
    	form.FormClosed += TestCtrl_FormClosed; 
    } 
} 
private Form GetParentForm(Control control) 
{ 
	if (control.Parent == null) return null; 
    if (control.Parent is Form) 
    { 
    	return control.Parent as Form; 
    } 
    else 
    { 
    	return GetParentForm(control.Parent); 
    } 
} 

private void TestCtrl_FormClosed(object sender, FormClosedEventArgs e) 
{ 
	// 이런저런 자원 해제 코드를 넣는다. 
}

 

 

이 함수가 찾은 Parent의 FormClosed Event를 바인딩 하여 기존 OnHandleDestroyed를 대체 하였다.

블로그의 정보

지금 당장 해!!!

지금당장해

활동하기