#13. 지능적인 대화 상자 프로그래밍하기

지능적인 어플리케이션의 개발에 있어서 꼭 필요한 요소중에는 대화 상자를 얼마나 똑똑하게 만들어주느냐에 따른 요소도 많다. 사실, 거의 대부분의 어플리케이션에서 대화 상자가 이용되고 있으며 대화 상자의 역할이 크든 작든 사용된다.


그렇다면, 어떠한 형태로 지능적인 대화 상자를 만들 것인가를 생각해 볼 필요가 있을 것이다. 의외로 간단한 곳에서 찾을 수 있는데, 계산기를 생각해 보자.


계산기에서 받아들일 수 있는 수치의 크기는 물론 유한이다. 또한, 수치만을 받아들이므로 문자열이나 기타 특수 문자들은 사용할 수가 없을 것이다. 그런데 사용자가 이것을 점검해보기 위하여 일부러 입력하였다면 특별한 대응책이 없는 어플리케이션 내에서는 그것을 곧이 곧대로 계산하려 들 것이며, 당연히 이상한 수치를 내보내거나 전혀 알 수 없는 데이터를 내보내거나, 심각하면 프로세스를 중단시켜야할 수도 있다.


이러한 사태를 미연에 방지하기 위하여 지능적 대화 상자 프로그래밍이 요구된다. 즉, 문자열을 입력하면 수치만을 입력해 달라고 안내한 후 값을 지우고 하는 식으로 말이다.


그렇다면, 한번 해보자.


책의 286쪽부터 참조하여 이름과 나이를 입력받는 대화 상자를 만들어서 출력될 수 있도록 메뉴까지 만들어 놓고 시작하겠다. 물론, 이 이후의 내용도 책에서 소개되어있지만 이것을 먼저 보기 바란다.


일단, 대화 상자에서 보이는 것들 중에 나이를 입력받게 될 텍스트 박스 컨트롤을 우선 클릭하고, Ctrl키를 누른 상태에서 더블 클릭하면, 빠르게 MFC 클래스 마법사 내에 있는 멤버 변수 탭으로 접근할 수 있다.


* 팁 : 해당 컨트롤을 해당 클래스에 연결시키기 위하여 멤버 변수를 등록할 때에는 Ctrl + 더블 클릭을 이용하여 빠르게 열 수 있다.


그리고, 나이를 입력 받게 될 텍스트 박스의 리소스 ID를 찾아서 클릭한 다음 좌측에 Add Variable을 선택하여 등록 대화 상자를 수행한다. 그렇게 하면, 3가지 항목을 제시하여 줄 것을 요구받게 된다.



  • 멤버 변수의 이름 :
  • 범주 :
  • 자료 형식 :

멤버 변수의 이름은 헝가리안 표기법을 사용하여 적절하게 써주면 나중에 확인하기가 유용하므로 꼼꼼히 해두자. 그리고, 범주에 관해서 신경쓸 필요가 있다.


크게 두 가지로 구분된다. Value형과 Control형이 있다. 이 둘의 차이점을 설명하고자 한다.



  • Value형 : 텍스트 박스 안에 있는 수치값에 대해서 작업을 하기 위해 사용되어야 한다. 즉, 나이를 0세 이상 200세 미만에서 입력받기 위해 등록시켜야 할 필요가 있을 때 Value형으로 범주를 설정하면 된다.
  • Control형 : 텍스트 박스 안에 있는 값이 어떻든 무관하다. 다만, 컨트롤의 외관이나 입력 받을 자료의 형식을 설정한다거나 할 때에 사용될 수 있는 범주이다.

참고로, 위의 두 범주는 갯수나 중복 여부와 관계 없이 사용될 수 있으므로 참고해 둔다. 다만, 멤버 변수의 이름만 다르게 지정해 주면 상관이 없다.


그리고, 변수형은 위의 범주에서 선택한 것에 따라 결정되는데, Control형인 경우 Control형으로 결정되므로 상관없다. Variable Type인 경우는 아래와 같다.



  • CString : 문자열 처리 클래스이다.
  • int : 말 그대로 정수형이다.
  • UINT : UnSigned Int, 부호가 없는 양수 전용 정수이다.
  • long : long형
  • DWORD : Double Word형.
  • float : float형
  • double : double형
  • BYTE : BYTE형
  • short : short형
  • BOOL : Boolean형 (0/1, TRUE/FALSE)
  • COleDateTime : 컨트롤 내에 입력된 시간 정보 자료형이다.
  • COleCurrency : 컨트롤 내에 입력된 돈의 단위 자료형이다.

여기서, 우리는 나이를 검사할 필요가 있으므로 UINT 형을 선택한다. 나이는 음수값이 없기 때문이다. 뭐, int형으로 하더라도 상관 없지만, 구분을 편리하게 하기 위해서 UINT를 쓰는 것이 좋을 듯 하다.


최종적으로 내용을 점검한 뒤 OK 버튼을 눌러 등록한다.


이상과 같은 방법으로 이름을 입력받는 컨트롤에도 적용시킨다. 이 때, 범주는 Value형으로 하고 자료형식은 CString으로 하는 것을 잊지 않도록. 이제, 클래스 뷰로 가서 어떤 내용이 추가되었는지 확인해 보자.


우선, 전에없던 클래스가 눈에 띄는데, C(대화 상자 ID)라는 클래스가 보인다. 그리고 그것을 확장시키면 CAboutDlg 또는 CDialog와 같은 형태의 멤버 함수와 방금 추가했던 두 개의 멤버 변수가 눈에 띈다.


자세히 확인해 보기 위하여 파일 뷰에서 헤더 파일의 (대화 상자 ID).h 파일을 열어서 클래스 정의를 보면 방금 지정한 대로 CString과 UINT형 멤버 변수가 등록이 되어있다. 그리고, 생성자 함수를 찾아가 보면 적당한 초기화 구문이 포함되어있을 것이다.


이제, 가장 중요한 내용을 살펴볼 차례이다. 바로, DoDataExchange(CDataExchange* pDX) 함수인데, 이것이 없으면 대화 상자 클래스는 그야말로 벙어리가 되어버린다. 다른 클래스와 객체들간의 의사소통, 즉 입구멍인 셈이다.


자세히 살펴보면 아래와 같은 것이 있다.



    //{{AFX_DATA_MAP(CNameDlg)


    DDX_Text(pDX, (이름을 입력받는 텍스트 박스 ID), m_strName);


    DDX_Text(pDX, (나이를 입력받는 텍스트 박스 ID), m_nAge);


    //}}AFX_DATA_MAP


이것이 맵이라고 하는 선언이다. 이것은 아직 무엇인지 알 필요는 없다. 하지만, 눈에 익혀 두고 있어야 하는 필수 구문 중에 하나이다. 이렇게 선언되는 형태의 맵들은 많이 있다. 메시지 맵, 데이터 맵 등이 여기에 속한다. 참고로, 이런 것들은 굳이 건드릴 필요도 없으며 전문가가 아닌 이상은 괜히 잘못 건드리면 프로젝트 전체가 붕괴될 수도 있음을 주의하여야 한다.


그리고, 실제로 데이터를 변경하였을 때에 화면에 표시되는 것과 메모리상에 저장되는 것간의 교류가 필요한데, 이 때 어떻게 할까? 바로 아래와 같은 함수가 있다.



    UpdateData();


이것은 양방향으로 쓰일수 있다. 화면에 표시되는 값에서 메모리 상에 저장되는 값으로 전송시키려면 UpdateData(FALSE);를 쓰면 되고 그 반대의 경우일 때에는 UpdateData(TRUE);를 입력하면 된다. 여기서 TRUE/FALSE의 의미는 무관하다.


그러면, 기본적으로 언제 호출될지를 알아 두어야 한다. 다이얼로그 박스가 처음에 시작하면 UpdateData(FALSE);로 보내지는데 컨트롤을 사용할 수 있도록 컨트롤 내부 값을 메모리에서 초기화된 값으로 초기화 시키는 것이다. 그리고, 끝날때 컨트롤에 저장되었던 값을 메모리로 가져오기 위해서 UpdateData(TRUE);를 사용하는 것이다. 그리고, 처리할 때 유의할 것은 311쪽의 상단에 있는 안내문을 참조해 두어야 한다.


그리고, 이번 Chapter의 주제인 입력 수치의 유효성 검사 부분이다. 이 부분을 if문과 switch 문으로 처리하려고 생각하였다면 잘못 생각한 것이다. MFC는 이런것 까지도 자동화시켜 놓았다.


서두에서 말하였던 클래스 마법사 내의 멤버 변수 탭으로 가서 멤버 변수가 지정된 항목을 선택해 보자. 그러면 하단의 Description 밑에 무언가 입력할 텍스트 박스가 보인다.


이름을 입력받게되는 텍스트 박스에서는 Maximum Characters : 가 나타난다. 10으로 지정되어있는데 이것은 원하는 만큼 문자열을 입력받을 때에 지정하면 된다.


그리고, 나이를 입력받게 되는 텍스트 박스에서는 Minimum Value : 와 Maximum Value : 가 있다. 21세기 초기의 인간의 평균 수명이 약 150세 미만인 것을 감안하여 Minimum Value에는 0을, Maximum Value에는 150을 입력하고 OK를 누르자.


그렇다면 이렇게 입력된 것은 어떻게 소스 코드에 반영될까? 한번 찾아보자. 방금전에 이야기했던 DoDataExchange()로 찾아가보자. 그러면, 없었던 부분이 추가되어있다.



    //{{AFX_DATA_MAP(C(대화상자ID))


    DDX_Text(pDX, (이름을 입력받는 대화 상자의 ID), m_strName);


    DDX_MaxChars(pDX, m_strName, 10);


    DDX_Text(pDX, (나이를 입력받는 대화 상자의 ID), m_nAge);


    DDX_MinMaxInt(pDX, m_nAge, 0, 150);


    //}}AFX_DATA_MAP


DDX_MaxChars는 문자열의 최대 길이를 검사하는 함수이다. 또, DDX_MinMaxInt는 수치의 최소, 최대 값을 검사하는 함수이다. 그리고. 함수의 파라미터들을 잘 보면 클래스 마법사에서 입력했던 그대로가 적용된 것을 확인할 수 있다.


이제 대화 상자로부터 입력받은 내용을 경고 메시지 박스로 표현해 보자. 처음에 OnDialog() 멤버 함수에서 대화 상자를 띄우는 DoModal()을 입력했었다. 이 부분을 조금 편집해 보면 이렇다.



    C(대화상자ID) dlg;


    dlg.m_strName = “아무개”;


    dlg.m_nAge = 15;


    if(dlg.DoModal() == IDOK)


    {




      CString strOutText;


      strOutText.Format(“입력된 이름 : %s, 나이 : %d”,


      dlg.m_strName, dlg.m_nAge);


    }


dlg.m_strName = “아무개”; 구문은 대화 상자가 처음 띄워질 때 이름 대화 상자에 들어있을 이름을 의미한다. 처음 띄워보면 이미 “아무개”라는 이름이 지정되어 있는 것이다. dlg.m_nAge = 15;의 경우도 마찬가지이다. 처음 띄워보면 이미 나이가 15살로 지정되어있다.


그리고 if 구문을 주목할 필요가 있다. 대화 상자에서 OK 버튼을 눌렀는지의 여부를 확인하는 구문이다. DoModal() == IDOK 라고 하였는데 여기서 IDOK는 확인 버튼의 리소스 ID이므로 IDOK가 아닌 다른 것이라면 당연히 그것으로 지정되어야 하며, 다른 버튼에 대해서 적용시킬 내용이라면 그 버튼의 리소스 ID가 대신 설정되면 된다.


CString으로 버퍼 변수가 하나 지정되었을 것이다. 그리고, 형식을 설정하는 함수가 있는데, 이전에 16비트 콘솔 어플리케이션을 프로그래밍할 때의 printf();를 연상하면 되겠다. 인 프레임 윈도우에서 다이얼로그 박스를 처리하도록 코딩하였다면 메시지 박스를 볼 수 있을 것이다.


* 파라미터 : 함수나 어플리케이션의 명령줄 보조 옵션을 의미하는 전문 용어로 부가적인 옵션을 입력받아서 어플리케이션을 동작시킬 필요가 있을 때 사용된다.  

댓글 남기기