이제 Keyboard와 Mouse를 처리해 볼 것입니다.
기본 프로그램 제작 1에서 설명한 방식으로 빈프로젝트(EventTest)를 생성하고 참조를 한 후, 코드 파일을 생성하여 다음과 같이 입력해봅시다.
using System;
using System.Windows.Forms;
class EventTest
{
public static void Main()
{
Form form = new Form();
form.Text = "FormTest";
form.MouseDown += new MouseEventHandler(ExeMouseDown);
form.KeyDown += new KeyEventHandler(ExeKeyDown);
Application.Run(form);
}
static void ExeMouseDown(object sender, MouseEventArgs e)
{
MessageBox.Show("마우스가 눌러졌습니다");
}
static void ExeKeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("키보드가 눌러졌습니다");
}
}
C++에서는 마우스 및 키보드 처리를 위해 윈도우즈 메시지를 이용하여 작성하였다. C#에서는 이벤트를 이용하여 작성합니다.
마우스나 키보드는 Form 클래스로 생성한 form 객체에서 발생합니다. 그때 수행해야하는 기능은 전혀 관계없는 클래스인 EventTest에서 처리를 해야만 합니다. 이 경우 사용하는 기법이 이벤트입니다. 여기서는 이벤트를 사용하는 것을 설명할 것입니다. 향후 대화 상자에서 이벤트 발생방법도 설명할 예정입니다.
이벤트를 사용하기 위해 사용되는 것은 Type의 한 종류인 Event와 Member의 한 종류인 Delegate(대리자)입니다.
이제 아래 문장을 해석해 봅시다.
form.MouseDown += new MouseEventHandler(ExeMouseDown);
form객체의 MouseDown 이벤트가 발생하면 대리자인 MouseEventHandler가 ExeMouseDown 메서드를 호출하라는 의미입니다.
여기서 += 이라고 입력하는 이유는 무었일까? 이것은 하나의 이벤트가 발생되었을때 호출되는 메서드를 여러개 연결할 수 있다는 의미입니다. 연결을 할때 += 을 입력하고 연결해제 할 때 -=을 입력합니다. 실제 프로그램에서는 -=을 쓸일이 별로 없으므로 무조건 +=으로 입력한다라고 기억해 두기 바랍니다.
이제 도움말을 한번봅시다.
MouseDown에 커스를 두고 F1을 누르면 화면에 다음과 같이 나타납니다.
public event MouseEventHandler MouseDown;에서 보듯이 MouseDown 이벤트는 대리자인 MouseEventHandler형으로 event선언되어 있습니다. Form 클래스 내부에서 마우스가 눌러지면 MouseDown이벤트를 발생시키도록 구성되어 있을것입니다.
이제 MouseEventHandler에 커서를 두고 F1을 누르면 화면에 다음과 같이 나타납니다.
public delegate void MouseEventHandler( object sender, MouseEventArgs e );에서 보듯이 MouseEventHandler는 delegate로 선언되어 있고 인자는 object형과 MouseEventArgs 형 두 인자를 넘겨 받습니다.
실제 Form 클래스에는 위와같이 형선언만 되어 있습니다. 즉 향후에 Method 이름은 상관없이 object형과 MouseEventArgs 형 두 인자를 넘겨 받는 Method를 만들어 이벤트와 연결만 하면 대리자인 MouseEventHandler가 그 Method를 호출할 수 있도록 하는 것입니다.
이제 편집 방법을 설명해 보겠습니다. 필자는 event와 delegate가 완전히 이해가 되기 전에는 수동으로 입력하기를 권합니다. 그렇다고 위의 내용을 모두 암기하여 입력하는 것은 아닙니다. Visual Studio는 쉽게 입력할 수 있도록 동적 도움말을 제공합니다. 즉 form.이라고 입력하면 Form클래스에서 제공하는 모든 Type과 Member를 표시하여 선택하도록 합니다. 여기에서 MouseDown이라는 이벤트를 선택하고 += 이라고 입력하면 입력해야 하는 형태가 풍선 도움말로 표시되는데 그것을 보고 입력하면 됩니다.
delegate에 의해 호출되는 함수인 ExeMouseDown과 ExeKeyDown을 보면 static으로 선언되어 있습니다. 이것은 static으로 선언된 Method에서는 static으로 선언된 Member만 호출할 수 있다는 규칙을 준수하기 위한 것입니다. 이 규칙은 static으로 선언된 Member가 메모리에 할당되는 시점과 일반 Member가 메모리에 할당되는 시점을 생각해 보면 당연한 일입니다. static Member는 프로그램이 실행될 때 오직 한번만 할당되어 유일하게 동작하며, 일반 Member는 new를 이용하여 클래스 객체를 생성할 때 마다 다른 메모리 영역으로 할당됩니다. 만약 static Member에서 일반 Member를 호출가능하다면 여러개 할당되어 있는 Member중 어떤 Member를 홀출해야 할지 결정 할 수 없을 것입니다.
같은 개념으로 만약 ExeMouseDown과 ExeKeyDown을 static으로 선언하면 일반 Member를 전혀 호출할 수 없어 프로그램을 구성하기 힘들 것입니다. 예를 들어 Mouse 위치를 Member 변수로 할당하여 저장하고자 한다면 그 변수 또한 static으로 선언해야 하고, static으로 선언된 Member는 오직 하나로 할당되어 하나의 객체에서 변경하면 모든 객체에서 변경되는 오류를 범하게 될 것입니다. 이 부분을 해결하기 위해 EventTest 클래스를 Form 클래스로부터 상속받아 아래와 같이 변경하면 됩니다.
using System; using System.Windows.Forms; class EventTest : Form { public static void Main() { Application.Run(new EventTest()); } EventTest() { base.Text = "FormTest"; base.MouseDown += new MouseEventHandler(ExeMouseDown); base.KeyDown += new KeyEventHandler(ExeKeyDown); } void ExeMouseDown(object sender, MouseEventArgs e) { MessageBox.Show("마우스가 눌러졌습니다"); } void ExeKeyDown(object sender, KeyEventArgs e) { MessageBox.Show("키보드가 눌러졌습니다"); } } |
1. EventTest클래스를 Form으로부터 상속 받습니다.
class EventTest : Form
2. 어플리케이션 가동시 EventTest 클래스를 생성하여 인자로 넘겨 줍니다.
Application.Run(new EventTest());
3. static Member가 아닌 EventTest 생성자에서 Event를 연결합니다.
EventTest() { base.Text = "FormTest"; base.MouseDown += new MouseEventHandler(ExeMouseDown); base.KeyDown += new KeyEventHandler(ExeKeyDown); } |
여기에서 base는 EventTest 클래스의 부모 Member라는 뜻입니다. 참고로 this는 EventTest클래스 본인을 나타냅니다. 물론 둘다 생략해도 컴파일은 잘됩니다. 컴파일러가 자동으로 붙이기 때문입니다. 그러나 편집의 편의성 및 프로그램 가독성을 높이기 위해 base.을 붙이는 것이 좋다.
- 편집의 편의성
앞에서도 이야기 했지만 Visual Studio는 동적 도움말을 제공합니다. base.이라고 입력하면 동적 도움말이 나타나 Member를 고르면 되므로 실제 계속 입력하다 보면 편함을 느끼게 될 것입니다.
- 프로그램 가독성 상승
만약 base.을 생략하게 되면 함수가 길때 프로그래머는 함수 내에 선언된 Member인지, 또는 자기 클래스의 Member인지, 부모 클래스의 Member인지 찾아 봐야 됩니다. 그러나 함수 내에 선언된 Member는 그냥 입력하고, 자기 클래스의 Member는 this.을 추가하고, 부모클래스의 Member는 base.을 추가 하여 구분해 주면 찾아 볼 것도 없이 보고 바로 알 수 있습니다.
이 개념에서 C++은 함수 내부 Member와 클래스 Member를 구분하기 위해 변수 이름에 m_를 붙였다. 당연히 위의 내용에 의해 변수 이름 앞에 m_를 붙이는 것을 C#에서는 권장하지 않습니다.
4. delegate에의해 호출되는 함수에 선언된 static을 모두 제거합니다.
void ExeMouseDown(object sender, MouseEventArgs e)
상속을 받은 상황에서 보면 부모 클래스에서 Method를 호출하면 자식 클래스에서 선언한 Method가 호출되게 하는 기능이 있습니다. 이기능은 C++에도 있던 기능으로 override 기능입니다. 부모 클래스에서 Method를 virtual로 선언하고 상속받은 클래스에서는 Method를 override 로 선언하면 이 기능이 수행됩니다. 다음은 override기능을 이용하여 수정한 프로그램입니다.
using System; using System.Windows.Forms; class EventTest : Form { public static void Main() { Application.Run(new EventTest()); } EventTest() { base.Text = "FormTest"; } protected override void OnMouseDown(MouseEventArgs e) { MessageBox.Show("마우스가 눌러졌습니다"); } protected override void OnKeyDown(KeyEventArgs e) { MessageBox.Show("키보드가 눌러졌습니다"); } } |
생성자를 보면 Event 연결 내용이 없어 졌습니다. 그리고 OnMouseDown과 OnKeyDown을 override 함수로 작성하여 그 기능을 수행합니다. 이것은 Form 클래스(실제로는 Form 부모 클래스들 중 하나인 Control 클래스)에 OnMouseDown과 OnKeyDown이라는 함수를 virtual로 선언하여 마우스가 눌러지거나 키보드가 눌러지면 호출되도록 구성해 두었기 때문에 가능합니다.
이 형태는 MFC와 비슷한 느낌이 들것입니다.
이제 편집 방법을 설명하면 그냥 override 라고 입력하면 동적 도움말이 뜹니다. override함수는 거의 On으로 시작하므로 선택하면 바로 기본입력이 모두 이루어 집니다. 그 기본 입력된 내용을 수정하면 됩니다.
첫 번째 예제는 상속을 받지 않고 구성했고 두번째는 상속을 받아서, 세 번째는 override기능을 이용하여 구성했다. 세 번째 같은 경우 수동으로 입력할 때는 편리합니다. 그러나 가독성 면에서 내부 구조를 전혀 알수 없으므로 약간 떨어진다고 볼수 있습니다. 따라서 자동으로 생성되는 코드에서는 두 번째 방식으로 코드가 생성됩니다.
예제 프로그램 다운로드
'C#' 카테고리의 다른 글
C#06. GDI+ 및 깜박임 문제 해결 (0) | 2022.03.08 |
---|---|
C#05. 메뉴 및 툴바 지원 (0) | 2022.03.08 |
C#04. 실제 프로그램 제작 (0) | 2022.03.08 |
C#02. 기본 프로그램 제작1 (0) | 2022.03.08 |
C#01. C++과 문법적으로 다른점 (0) | 2022.03.07 |