중첩된 사용자 정의 컨트롤의 DesignMode 속성은 동작하지 않음
RTFM/Winform 2009. 6. 21. 09:14윈폼의 디자인 타임을 지원하는 Component.DesignMode 라는 속성이 있습니다.
이름이 의미하는 대로 이 컴퍼넌트가 현재 디자인 타임에 있는지 여부를 알 수 있어, 특히 컨트롤이나 폼의 Load 이벤트 핸들러에서 많이 사용됩니다.
예를 들어 폼이 로드되고 나서 어떤 작업을 해야 한다면 아래와 같은 코드를 작성할 수 있습니다.
(주 : Load 이벤트의 핸들러를 작성하지 않고 OnLoad 메서드를 오버라이드 하는 이유에 대해서는 졸고를 참고하십시오.)
(주의 주 : 폼의 경우라면 Load 이벤트 보다 Shown 이벤트를 사용할 것을 추천합니다. Shown 이벤트는 Load 이벤트가 발생한 후 폼이 화면에 표시된 시점에 발생합니다. 참고로 컨트롤에는 Shown 이벤트가 없습니다.)
코드에서 보듯이 Load 이벤트 핸들러에서는 디자인 타임인지 여부를 검사할 필요가 있습니다.
왜냐면 Load 이벤트가 발생하는 시점이 폼이 로드될 때인데, 비주얼 스튜디오에서 폼을 열 때도 폼이 로드된 것이기 때문에 이 이벤트가 발생하기 때문입니다.
여기서 ‘어떤 작업’이 실제로 어떤 작업이냐에 따라서 문제가 발생할 수 있습니다.
만약에 ‘어떤 작업’이 DB에서 데이터를 로드하거나 하드 디스크의 특정 위치에 있는 파일을 읽는 것이라면, 이는 디자인 타임에서는 수행될 수 없거나 수행되지 않아야 할 작업입니다.
위 코드에서 조건문을 뺀 상태에서 디자인 타임에서 할 수 없는 작업을 하면 예외가 발생되고, 비주얼 스튜디오에서 폼을 열 수 없게 됩니다.
비주얼 스튜디오의 버그라고 포기하기 전에 혹 위와 같은 문제가 아닌지 살펴보면 어쩌면 쉽게 문제가 해결될 수도 있습니다.
(물론 비주얼 스튜디오에서 폼을 열지 못하는 원인에는 수 십, 어쩌면 수 백 가지가 있겠는데, 이러한 상황에 대한 각각의 해결법을 하나씩 익혀 가는게 바로 경험이라는 것이겠지요.)
그런데 문제는 이 DesignMode 속성이 중첩된 사용자 정의 컨트롤에서는 동작을 하지 않는다는 것입니다.
코드를 보면서 이야기해 보지요.
사용자 정의 컨트롤을 하나 만들고 그 이름을 InnerTextViewer라고 합시다.
내부에 텍스트 박스를 하나 올리고, 배경색은 빨간 색으로 지정하였습니다.
이름이 의미하듯이 이 컨트롤은 텍스트 파일을 읽어서 표시하는 일을 합니다.
컨트롤이 로드(정확하게는 이 컨트롤이 속한 폼이 로드)되면 실행 디렉토리에 있는 a.txt 파일을 읽는 코드는 다음과 같겠네요.
이제 폼을 하나 만들고 이 사용자 정의 컨트롤을 올려 봅니다.
(아, 사용자 정의 컨트롤을 작성하거나 수정하면 빌드를 하여야 반영되는 건 아시죠?)
문제 없이 올라가고 실행하면 동작도 잘 합니다.
문제는 지금 부터입니다.
새로운 사용자 정의 컨트롤을 만들고 이름을 OuterTextViewer 라고 합시다.
그리고 앞에서 만든 InnerTextViewer 컨트롤을 도구상자에서 끌어 옵니다.
이번에는 배경색은 파란색으로 지정하였습니다.
(역시 빌드를 한 후에) 이제 위 폼에서 InnerTextViewer를 지우고 OuterTextViewer를 올려봅시다.
친숙한 화면이 나타났습니다. 절대 그냥 닫지 마시고 어떤 예외인지를 찬찬히 보시기 바랍니다.
FileNotFoundException 이고 1.txt 파일이 없다는 말 같습니다.
무슨 말인지 감이 오실 겁니다.
런 타임이 아닌 디자인 타임 임에도 불구하고 위 InnerTextViewer의 코드가 실행이 된 것입니다.
그렇다면 그 말은 위 코드의 ‘if (DesignMode)’ 에서 DesignMode 값이 false 라는 말이 되겠습니다.
위 코드에다 아래와 같이 메시지 박스를 출력하는 코드를 추가하여 직접 값을 볼 수도 있습니다.
정리하자면, 사용자 정의 컨트롤에 포함된 사용자 정의 컨트롤의 DesignMode 속성은 기대한 것처럼 동작하지 않는다는 것입니다.
‘중첩된 사용자 정의 컨트롤이 사용되는 경우가 있을까’ 하고 대수롭지 않게 생각할 수도 있지만, 규모가 제법 있고 코드가 구조화된 프로젝트에서라면 필요한 경우가 제법 있습니다.
그렇다면 이 문제를 어떻게 해결하면 좋을까요?
먼저 Load 이벤트에서 디자인 타임에서 할 수 없는 작업을 빼는 방법이 있겠네요.
위 예에서라면 파일을 읽는 부분을 별도의 메서드로 만들고, 폼이 로드되었을 때 이를 수동으로 호출하도록 하는 것입니다.
하지만 사용자 정의 컨트롤이 사용된 모든 폼의 로드 시점을 추적하여야 하기 때문에, 전체적인 프로그램의 복잡도를 증가시키는 요인이 될 수 있으므로 별로 좋은 방법은 아닌 것 같습니다.
또 다른 방법은 전역 변수 개념으로 플래그를 하나 만들어서 디자인 타임에서 이를 체크하는 것입니다.
코드를 보면서 이야기 해봅시다.
아래와 같이 Program 클래스에 정적 속성을 추가하고, 이 값을 응용프로그램이 실행되기 전에 true로 설정합니다.
그리고 InnerTextViewer의 Load 이벤트 핸들러에서는 DesignMode 속성 대신 이 플래그를 검사합니다.
빌드하고 나면 이제 폼에 OuterTextViewer를 올릴 수 있습니다.