Download presentation
Presentation is loading. Please wait.
Published byŌΘωμᾶς Στεφανόπουλος Modified 6년 전
1
이벤트(Event) 처리하기 윈도우 프로그램에서는 이벤트를 통하여 실행 중인 프로그램에게 사용자의 요구사항을 알린다.
즉, 사용자가 실행 중인 프로그램 위에서 입력장치(마우스, 키보드 등)를 조작하면 운영체제는 실행 중인 프로그램에게 사용자의 움직임(요구)을 알려주게 된다. 사용자에 의하여 입력된 내용은 실행 중인 (사실은 무슨 일이 일어나기를 기다리고 있는) 프로그램에게는 하나의 이벤트로 전달된다. 이벤트에는 마우스의 움직임, 마우스 버튼이 특정 위치에서 눌림, 혹은 놓임, 키보드의 키 중의 하나가 눌림 혹은 놓임 등이 모두 이벤트로서 프로그램에게 전달된다. 어떤 이벤트가 발생 했을 때 그 이벤트에 대하여 특별히 할 일이 없는 경우에는 프로그램은 그 이벤트를 무시하고 아무런 반응도 하지 않게 된다. 실제로 많은 이벤트들은 프로그램에 의하여 무시된다. 그러나 몇 몇 특정 이벤트에 대해서 그와 관련된 일련의 기능을 수행해야 되는 경우 가 있을 수 있다. 이런 경우에는 해당 이벤트가 발생 했을 때 자동으로 실행되는 함수를 지정해 주어야 한다. 이렇게 특정 이벤트에 대하여 자동으로 호출되어 실행되는 함수를 이벤트처리함수(혹은 이벤트 핸들러(Event Handler))라고 한다. 다음 예제 프로그램에서는 앞에서 만든 프로그램의 윈도우 창의 위에서 마우스의 왼쪽 혹은 오른쪽 버튼으로 눌렀을 경우에 눌린 버튼과 그 위치의 좌표 값을 콘솔 창에 출력한다.
2
이벤트(Event) 처리하기 새로운 프로젝트를 만들어서 새로 코딩을 하든지 아니면 지난 시간에 마지막으로
새로운 프로젝트를 만들어서 새로 코딩을 하든지 아니면 지난 시간에 마지막으로 코딩했던 프로그램을 수정하여 아래와 같은 내용을 입력한다. (프로젝트명: EventHandler 코드파일명: EventHandler.cs) using System; using System.Windows; using System.Windows.Input; namespace pjkim.EventHandler { class MyWindow : Window { [STAThread] //-- Main 함수 static void Main() { Application app = new Application(); app.Run(new MyWindow()); }//Main() //-- 생성자 함수 public MyWindow() Title = "Event 처리하는 방법"; Width = 400; Height = 200; MouseDown += MouseDownHandler; }//MyWindow2() //-- MouseDown 이벤트 처리함수 void MouseDownHandler(object sender, MouseButtonEventArgs args) Window win = sender as Window; string btn; if (args.ChangedButton == MouseButton.Left) btn = "왼쪽"; else if (args.ChangedButton == MouseButton.Right) btn = "오른쪽"; else btn = "알 수 없는"; Console.WriteLine("[{0}] 버튼을 위치({1})에서 눌렀습니다. ", btn, args.GetPosition(win)); } }//class MyWindow2 }//namespace
3
MouseDownHandler함수를 지정
아래는 앞의 코드의 일부가 Visual Studio 2010에 입력된 모습이다. 줄번호 18~24는 생성자 함수로서 기존의 예제 프로그램처럼 Title, Width, Height 등의 속성 값을 지정하고 있다. 그런데 맨 마지막에 명령어 인 23번 줄의 MouseDown += MouseDownHandler; 는 이번 예제에서 처음 보는 명령어이다. MouseDown이벤트는 객체의 위에서 마우스의 버튼이 눌렸을 때 발생하는 이벤트이다. 아래의 명령어에서는 MouseDown이벤트가 발생하면 MouseDownHandler라는 함수가 호출되도록 이벤트 처리함수를 지정하고 있다. MouseDownHandler함수는 바로 밑에 코딩되어 있다. MouseDown 이벤트에 MouseDownHandler함수를 지정 MouseDownHandler함수 다음 페이지에서 MouseDown이벤트 처리함수인 MouseDownHandler의 내용에 대하여 자세히 설명한다.
4
일단 Ctl+F5를 눌러서 프로그램을 실행한 후 윈도우 창 위를 마우스 왼쪽, 혹은
오른쪽 버튼으로 클릭해 보자 마우스 버튼을 누르는 순간 콘솔 창에 아래와 같은 문자열이 출력되는 것을 확인할 수 있을 것이다. 이 위에서 마우스를 클릭하면 콘솔 창에 누른 버튼과 위치가 출력된다. 마우스 이벤트 처리함수의 첫 번째 파라메터는 object타입이고, 두 번째 파라메터는 MouseButtonEventArgs 타입이다. 위의 함수에서 object 타입의 첫 번째 파라메터 sender는 이벤트가 발생한 객체(여기서는 윈도우창)를 나타내고, 두 번째 파라메터인 args는 이벤트와 함께 전달되는 추가적인 정보가 들어 있는 객체이다. 만약 args 객체에 있는 추가적인 정보를 사용할 필요가 없다면 단순히 EventArgs 타입으로 정의해도 된다. 그러나 위의 경우처럼 args안에 있는 ChangedButton, GetPosition(win)등을 사용해야 한다면 정확한 타입명 (MouseButtonEventArgs)를 사용해야 한다. 이벤트 처리함수의 이름은 개발자가 맘대로 정할 수 있다. 대부분 이벤트 명 뒤에 Handler를 붙이는 등의 각자 나름대로의 규칙을 정해서 사용하는 것이 좋다. 이벤트 처리함수는 두 개의 파라메터를 전달 받는다.
5
MouseButtonHandler로 수정
이벤트(Event) 처리하기 2 앞의 프로그램을 다음과 같이 수정해 보자 이벤트 처리 함수이름을 MouseDownHandler에서 MouseButtonHandler로 수정 MouseUp 이벤트에 대해서도 이벤트처리함수를 지정하였다 버튼의 상태를 나타내기 위하여 변수 및 명령어 추가 위에서는 마우스 버튼이 눌렸을 때 뿐 아니라, 눌렀던 버튼을 놓는 이벤트가 발생할 때도 똑 같은 이벤트 처리함수가 실행되도록 MouseUp 이벤트 처리를 추가하였다. 마우스가 눌렸을 때나 놓였을 때나 모두 같은 함수가 실행되므로 이벤트 처리함수의 이름도 일반적인 이름인 MouseButtonHandler로 바꾸었다. 이것은 불필요한 일 같지만 함수의 이름을 정확하게 지정하는 것은 소프트웨어 개발에 많은 도움이 된다. 위의 경우 마우스 버튼이 눌렸을 때나 놓였을 때 모두 같은 함수가 실행되므로 이 함수가 실행될 때 마우스 버튼의 상태가 눌린 것인지 놓인 상태인지를 구별할 수 있어야 할 것이다. 그것을 알아내기 위하여 위의 함수에서는 MouseButtonEventArgs타입의 파라메터 args안에있는 ButtonState의 값을 체크한다. 이런 모든 추가적인 정보가 파라메터로 전달되는 것이다.
6
“[오른쪽] 버튼이 위치(270, 92)에서 올라가 있습니다.”로 편집된다.
이벤트(Event) 처리하기 2 아래의 그림은 앞 페이지의 수정된 프로그램을 실행환 결과이다. 마우스 버튼을 누르면 반드시 놓아야 하기 때문에 버튼을 클릭할 때마다 두 개의 메시지가 콘솔 창에 나타난다. 첫 번째 것은 버튼을 누를 때 출력되고 두 번째 것은 눌렀던 버튼을 놓을 때 출력될 것이다. 천천히 시간차를 두고 눌러보고, 또 누른 후 마우스를 끌어서 위치를 옮겨서 놓아보자. 이 위치에서 마우스를 누른 후 마우스를 다른 장소로 끌어서 놓아보자. 여기서 위의 콘솔창에 문자열을 출력하는 Console.WriteLine()함수에 대하여 알아보자. 위의 문자열은 다음의 문장에 의하여 출력된다. Console.WriteLine("[{0}] 버튼이 위치({1})에서 {2} 있습니다.", btn, args.GetPosition(win), state); 위의 출력함수 WriteLine에는 모두 네 개의 파라메터가 전달되고 있다. 첫 번째는 문자열로서 "[{0}] 버튼이 위치({1})에서 {2} 있습니다." 이다. 이 문자열은 편집문자열이다. 이 문자열에 뒤에 주어진 세 개의 파라메터의 값이 편집된 후 그 내용이 콘솔창에 출력된다. 새 개의 파라메터 중 첫 번째는 문자열 (string)타입으로 정상적인 경우에 “왼쪽”, 혹은 “오른쪽” 둘 중의 하나의 값을 가지고 있다. 두 번째 GetPosition함수가 return하는 값은 마우스의 상태가 바뀐 위치의 x,y좌표값(숫자)을 나타내는 문자열이다. 마지막 state는 마찬가지로 정상적인 경우에 “올라가” 혹은 “내려가” 중 하나의 값을 갖고 있을 것이다. 편집 문자열 안에는 뒤에 따라오는 값이 들어가는 위치가 정해져 있는데 각각 {0}, {1}, {2} 로 표시되어 있다. {0}은 편집 문자열 뒤에 나타나는 파라메터 중 첫 번째 항목의 값을, {1}은 두 번째 항목의 값을, {2}는 세 번째 항목의 값의 위치이다. btn: “오른쪽”, GetPosition: 270, 92, state: “올라가” 의 값을 가지고 있다면 편집 문자열은 최종적으로 “[오른쪽] 버튼이 위치(270, 92)에서 올라가 있습니다.”로 편집된다.
7
: 콘트롤: GUI 객체 GUI 객체에서 GUI란 란 Graphical User Interface의 약자이다. 따라서
“컴퓨터 프로그램과 사용자 사이에 데이터를 주고 받기 위해서 사용되는 시각적 요소를 가지고 있는 객체” 정도가 되겠다. 시각적인 요소를 가지고 있다는 것은 그 객체가 화면에 사용자가 볼 수 있는 형태로 그려진다는 것이다. 이러한 시각적인 요소를 가지고 있는 객체를 윈도우 프로그램에서는 콘트롤 이라고 부른다. 아래의 그림에는 세 가지의 콘트롤(GUI객체)들이 나열되어 있다. ‘학번:’ , ‘이름:’ 등의 제목을 나타내고 있는 Label 콘트롤, 문자열 데이터의 입,출력에 사용되는 TextBox 콘트롤 추가, 수정, 삭제를 지시할 때 누르기 위한 Button 콘트롤 등을 볼 수 있다. Label : TextBox Button 앞으로 우리는 위의 그림처럼 시각적으로 보여지는 모든 객체를 콘트롤 이라고 부를 것이다.
8
Content 프로퍼티 앞에서 이야기 한 대로 콘트롤은 시각적 요소를 가지고 있는 객체이다.
콘트롤들은 나름대로 자신의 기본적인 모양을 가지고 있다. 이상하게 들릴지 모르지만 몇몇 콘트롤은 시각적인 요소를 가지고 있음에도 불구하고 실제로 눈에는 보이지 않는 것도 있다. 이러한 앞 뒤가 안 맞는 경우를 설명하기 위하여 보이지 않는 콘트롤을 투명한 콘트롤이라고 부를 수도 있겠다. 이렇게 보이는 부분이 있는 콘트롤이든 아니면 투명한 콘트롤이든 간에 자기 자신의 모습 이외에 다른 콘트롤을 사용하여 자신의 모습을 꾸밀 수 있다. 이를 위하여 몇몇 콘트롤은 Content라는 프로퍼티를 가지로 있다. Content 의 뜻은 ‘내용물’ 정도가 될 것이다. 어떤 콘트롤의 Content 프로퍼티에 다른 콘트롤을 포함시키는 것을 보통 ‘붙인다”고 표현한다. A 콘트롤과 B콘트롤이 있다고 할 때 A.Content = B; 와 같이 B를 A의 Content에 붙이면 A의 모습의 일부 혹은 전부는 B의 모습으로 바뀐다. 다음의 그림은 이것을 설명한다. 왼쪽의 Window객체의 Content에 Button 객체를 붙인 결과 화면이다. 중앙에 있는 사각형이 Button객체이다. Button객체의 Content는 비어있다. Button의 Content에 “코알라”라는 문자열 객체를 붙인 결과화면이다. Window객체도 아래의 그림과 같이 시각적으로 보이는 부분이 있다. 그러나 Content에는 아무것도 붙여져 있지 않다. 따라서 중앙에는 아무것도 보이지 않는다. 문자대신 코알라 이미지를
9
Button 콘트롤 다음 프로그램은 하나의 Button을 생성해서 Window객체의 Content에 붙이는
프로그램의 예이다. 버튼을 클릭하면 메시지박스 대화 상자가 화면에 나타나도록 프로그래밍을 하였다. 솔루션에 프로젝트를 추가하고 다음의 코드를 코딩해보자. 프로젝트명: ButtonTest, 코드파일명: ButtonTest.cs using System; using System.Windows; using System.Windows.Controls; namespace pjkim.ButtonTest { class ButtonTest : Window [STAThread] static void Main() Application app = new Application(); app.Run(new ButtonTest()); }//Main() //-- 생성자 public ButtonTest() Title = "버튼 테스트"; Width = 500; Height = 200; Button btn = new Button(); //Window의 Content에 Button객체를 붙인다. Content = btn; //Button의 Content에 문자열 객체를 붙인다. btn.Content = "이 버튼을 절대 누르지 마시오!"; //버튼의 폰트 크기 지정 btn.FontSize = 24; //버튼 Click이벤트처리함수 지정 btn.Click += btn_Click; }//생성자 함수 ButtonTest() //-- 버튼 이벤트 처리함수 void btn_Click(object sender, RoutedEventArgs args) MessageBox.Show("결국 누르고 말았군요!", "버튼 이벤트"); } }//class ButtonTest }//namespace
10
아래는 앞의 코드를 Visual Studio 2010에 코딩한 모습이다.
버튼 객체를 생성하는 부분, 윈도우의 Content에 버튼 객체를 붙이는(대입하는) 부분, 그리고 버튼을 마우스로 클릭 했을 때 실행될 이벤트처리함수(btn_click)을 지정하는 부분이 주석으로 설명되어 있다. 이벤트 처리함수에서는 메시지 창을 하나 띄워서 버튼의 클릭에 반응하는 것을 보여주고 있다.
11
아래의 그림은 앞의 프로그램을 실행한 결과이다. 버튼이 윈도우 창 전체를 덮고 있는 모습을 볼 수 있다. 즉, 버튼이 윈도우의
버튼이 윈도우 창 전체를 덮고 있는 모습을 볼 수 있다. 즉, 버튼이 윈도우의 내용물이 된 것이다. 또한 버튼의 내부에는 “이 버튼을 절대 누르지 마시오!”라는 문자열이 보이고 있다. 버튼을 마우스로 클릭하면 아래와 같은 메시지 창이 화면에 나타난다. 메시지창 위의 윈도우의 테두리를 마우스로 잡아 끌어서 창의 크기를 변경시켜보면 버튼의 크기가 윈도우 창의 크기에 따라서 같이 늘었다 줄었다 하는 것을 볼 수 있을 것이다. 이는 버튼의 HorizontalAlignment(수평 맞추기), VerticalAlignment(수직 맞추기)의 기본 값(Default값)이 Stretch(늘리기)로 지정되어 있기 때문이다. 다음과 같이 두 줄의 명령어를 적절한 위치에 추가해 보자(위치: 왼쪽의 줄 번호 참조) 위의 두 줄은 버튼의 수평 맞추기와 수직 맞추기를 Default값인 Stretch에서 수평은 Left, 수직은 Top으로 지정하였다. 아래의 그림은 프로그램의 실행 결과이다. 버튼이 수평적으로는 왼쪽(Left), 수직적으로는 위(Top)로 붙어있는 것을 볼 수 있다. 이제 창의 크기가 변해도 버튼의 크기가 따라서 변하지 않는다. 버튼의 크기는 따로 지정하지 않았기 때문에 자신의 Content(문자열)의 크기에 맞추어져 있다.
12
Margin, Padding으로 여백 지정하기
아래의 그림은 앞 페이지의 그림과 같은 그림이다. 버튼이 왼쪽, 위로 바짝 붙어 있는 모습이 좀 답답해 보이지 않는가? 그리고 버튼 내부의 문자열도 버튼에 너무 꽉 차 있는 모습이다. 어느 정도 여백을 줄 필요가 있다. 주위의 다른 물체가 너무 가까이 붙지 못하도록 객체의 외부에 여백을 두는 것을 Margin이라고 한다. 내부의 물체가 너무 가까이 붙지 못하도록 객체의 경계와 내부의 물체 사이에 여백을 두는 것을 Padding이라고 한다. 예를 들면: 버튼을 기준으로 할 때, 버튼과 그의 부모인 윈도우 창 사이의 여백을 Margin이라고 한다. 또, 버튼과 그의 자식인 내부의 문자열 사이의 여백을 Padding이라고 한다. 앞 페이지에서 프로그램에 추가한 두 줄의 명령어 밑에 다음의 두 줄을 추가해 보자. 위의 두 줄의 명령어는 버튼과 버튼의 부모인 윈도우 창 사이에 20의 마진(Margin)을 두었다. 또, 버튼과 버튼의 자식인 문자열 사이에 10크기의 패딩(Padding)을 두었다. 그 결과 왼쪽의 그림에서 보는 바와 같이 버튼과 창 사이에 20크기의 여백이, 또, 버튼의 테두리와 문자열 사이에 10크기의 여백이 생긴 것을 확인할 수 있다.
13
Margin, Padding으로 여백 지정하기
프로그램을 아래와 같이 수정하라. 즉, 앞에서 추가했던 수평, 수직 맞추기 명령어를 아래와 같이 주석 처리하여 임시로 삭제한다. 그러면 다시 이전의 Default값인 Stretch로 돌아 갈 것이다. 아래는 프로그램을 실행환 화면이다. 프로그램을 실행하고 윈도우 창의 크기를 변경시켜 보면 버튼의 크기가 다시 윈도우 창의 크기에 맞추어 늘어났다, 줄어들었다 할 것이다. 그러나, Margin과 Padding은 윈도우 창의 크기에 비례하지 않고 고정되어 있는 것을 볼 수 있을 것이다. Margin과 Padding을 적절히 지정하여야 콘트롤의 배치가 자연스러울 것이다. 이 버튼을 절대 윈도우 버튼 문자열 마진 패딩 마진이나 패딩은 왼쪽, 위, 오른쪽, 아래를 각각 다른 값으로 지정할 수 있다. 예를 들어서 btn.Margin = new Thickness(30, 20, 10, 5); 와 같이 네 개의 숫자를 지정하면 버튼의 왼쪽은 30, 위쪽은 20, 오른쪽은 10, 아래쪽은 5의 외부 여백이 생긴다. 각자 테스트 해 보자.
14
TextBox 콘트롤 아래의 그림은 윈도우에 TextBox 콘트롤을 붙인 프로그램의 실행 결과이다.
“TextBoxTest”라는 새 프로젝트를 만든 후 아래의 코드를 복사하여 테스트 해 보자. using System; using System.Windows; using System.Windows.Controls; namespace pjkim.TextBoxTest { class TextBoxTest : Window [STAThread] static void Main() Application app = new Application(); app.Run(new TextBoxTest()); }//Main() //-- 생íy성ù¨¬자U public TextBoxTest() Title = "TextBox Control Test"; Width = 400; Height = 150; TextBox tbox = new TextBox(); tbox.FontSize = 36; tbox.Margin = new Thickness(20); tbox.VerticalContentAlignment = VerticalAlignment.Center; Content = tbox; } }//class TextBoxTest }//namespace pjkim.TextBoxTest 텍스트박스 안에 입력되는 문자열이 수직방향으로 정 가운데 있도록 하기 위한 속성값 지정(텍스트박스 자체의 위치가 아님)
15
패널 콘트롤 Content 프로퍼티에는 하나의 콘트롤 밖에는 붙일 수 없다. 그러나 우리가 아는 거의 모든
윈도우 프로그램은 여러 개의 콘트롤들로 이루어져 있다. 아래는 앞에서 본 화면인데 비교적 간단한 화면인데도 17개의 콘트롤로 이루어져 있다(Label 7개, TextBox 6개, Button 4개) 콘트롤 중에는 눈에 보이지 않는 투명한 콘트롤이 있는데 이 중 하나가 패널(Panel)이다. 패널은 자기 자신은 보여지지 않지만 그 대신 다른 여러 개의 콘트롤들을 자식으로 포함할 수 있다. 따라서 패널 콘트롤에 위의 그림과 같이 일반 콘트롤을 위치시킨 후 그 패널을 윈도우의 Content에 붙임으로써 위와 같이 여러 개의 콘트롤로 이루어진 폼(Form)을 구성할 수 있다. 많이 사용되는 패널에는 StackPanel , DockPanel, Grid 가 있다.
16
StackPanel 뒤에 코드 더 있습니다. StackPanel을 이용하여 콘트롤들을 한 방향으로 차곡차곡 쌓을 수 있다.
정렬방향은 Default로 위에서 아래로 내려가고 Orientation 속성 값을 Horizontal로 바꿔주면 왼쪽에서 오른쪽으로 향하여 횡으로 위치시킬 수 있다. 아래의 프로그램은 다섯 개의 버튼을 StackPanel에 수직으로 배치하는 프로그램이다. 프로그램이 길어진 관계로 두 페이지에 나누어서 붙여 넣었다. using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace pjkim.StackPanelTest { class StackPanelTest : Window [STAThread] static void Main() Application app = new Application(); app.Run(new StackPanelTest()); } //-- 생성자 public StackPanelTest() Title = "StackPanel Test"; //윈도우의 크기를 Content의 크기에 맞추도록 지정 SizeToContent = SizeToContent.WidthAndHeight; //윈도우의 크기 변경 않되도록 지정 ResizeMode = ResizeMode.NoResize; //StackPanel 객체를 생성 StackPanel stack = new StackPanel(); //stack의 외부에 여백 지정 stack.Margin = new Thickness(2.5); //StackPanel 객체에 다섯 개의 버튼을 위에서 아래로 정렬 for (int i = 1; i <= 5; i++) { //Button객체 생성 Button btn = new Button(); //버튼의 이름을 각각 다르게 지정 버튼1, 버튼2 등 Name은 띄어쓰기 하면 안됨. btn.Name = "Button" + i.ToString(); //외부 여백을 지정하여 버튼이 서로 붙지 않도록.. btn.Margin = new Thickness(2.5); //버튼의 Name을 버튼의 Content로 사용. btn.Content = btn.Name; //버튼 Click이벤트 처리함수 지정 btn.Click += btn_Click; //스택패널에 버튼을 추가 stack.Children.Add(btn); }//for //윈도우의 Content에 StackPanel객체를 붙임 Content = stack; }//생성자 StackPanelTest() 뒤에 코드 더 있습니다.
17
앞 페이지의 코드 뒤에 붙여 넣으세요 다음 페이지에서 위의 프로그램을 함수 별로 자세히 설명한다.
//-- 이벤트 처리함수 void btn_Click(object sender, RoutedEventArgs args) { //sender를 Button객체로 타입변경하는 안전한 방법 Button btn = sender as Button; string msg = btn.Name + "을(를) Click 했습니다."; MessageBox.Show(msg, Title); }// btn_Click() 이벤트 처리함수 }//class StackPanelTest }//namespace pjkim.StackPanelTest 앞 페이지의 코드 뒤에 붙여 넣으세요 왼쪽은 위의 프로그램을 실행한 결과 화면이다. 윈도우의 ResizeMode를 NoResize로 지정 했기 때문에 윈도우 크기를 변경할 수 없고 타이틀 바에 최소, 최대화 시키는 버튼도 없다. 다섯 개의 버튼은 StackPanel에 자식으로 정렬되어 있고 StackPanel이 Window의 Content로 주어져 있다. 위의 결과 창에 나타난 버튼의 크기(폭,높이)는 그 안에 있는 Content에 의하여 정해지는데 위의 그림에서 버튼의 폭이 내부 문자열의 크기보다 큰 이유 최 외곽의 윈도우의 폭이 허용되는 최소 크기이기 때문에 StackPanel의 폭이 그보다 더 작아질 수 없었고 StackPanel 안에 있는 버튼의 폭도 StackPanel의 폭에 맞추어졌기 때문이다. 각 버튼을 누르면 Click이벤트 처리 함수가 눌린 버튼의 이름을 메시지 창에 보여준다. 만약 위의 프로그램에서 StackPanel객체를 생성한 후에 stack.Orientation = Orientation.Horizontal; 을 코딩하여 정렬 방향을 수평으로 바꿔 주었다면 아래의 그림과 같이 버튼이 옆으로 정렬되었을 것이다. Default는 위와 같이 수직 정렬이다. 다음 페이지에서 위의 프로그램을 함수 별로 자세히 설명한다.
18
아래는 앞 프로그램의 생성자 함수이다. 이 생성자 함수 내에서 모든 작업이 이루어진다.
아래는 앞 프로그램의 생성자 함수이다. 이 생성자 함수 내에서 모든 작업이 이루어진다. 윈도우의 크기는 내부 컨텐트의 크기에 딱 맞게 지정됨 윈도우의 크기를 마우스 등으로 변경시킬 수 없도록 지정 StackPanel 객체를 생성 StackPanel에 다섯 개의 버튼을 집어 넣기 위한 for 반복문 StackPanel의 Children 콜렉션에 Add함수를 통하여 버튼을 추가 버튼의 이름에 사용되는 숫자가 매번 증가하여 바뀌는 것을 확인하라. 위의 코드에서 for 반복문은 변수 i가 처음에 1의 값을 갖게 되고 그 후 5까지 1씩 증가하면서 for문장의 { } 내부에서 사용된다. 따라서, for문장의 { } 내부에 있는 명령어는 모두 다섯 번 실행되고 매번 변수 i의 값은 1씩 증가한다. (1,2,3,4,5까지 증가한 후 6 이되면 종료) for 반복문이 매번 실행 될 때마다 Button객체가 하나 생성되고 그 Button객체의 Name, Margin, Content등을 지정 한 후 50번 문장의 stack.Children.Add(btn); 명령어를 통하여 버튼을 StackPanel객체에 추가한다.
19
아래는 다섯 개의 버튼 중 하나가 클릭 되었을 때 실행되는 이벤트 처리함수이다.
63번 문장은 이벤트 처리함수 btn_Click()에게 전달된 첫 번째 파라미터인 sender를 Button 타입으로 변경하는 문장이다. sender는 이벤트를 발생시킨 최초의 객체이다. 여기서는 Button 객체일 것이다. 그러나 부모,자식 객체 간에 이벤트가 전달되는 복잡한 과정 등으로 인하여 경우에 따라서는 sender가 우리가 예상하는 Button이 아닐 수도 있다(설명 생략). 따라서, 위의 프로그램에서는 sender를 가장 기본적인 클래스인 object타입으로 지정한 후 함수 내부에서 우리가 필요한 객체 타입으로 변환하여야 한다. 타입변환에는 아래와 같이 두 가지 방법이 있다. ① Button btn = (Button)sender; ② Button btn = sender as Button; ①번 방법은 C언어에서부터 사용하는 오리지널 Type Casting 방법이고, ②번 방법은 C#이 제공하는 안전한 타입변환 방법이다. ①번 방법을 사용했을 경우에 만약 sender가 Button 타입이 아니라면 Exception(예외)이 발생하여 프로그램이 비 정상적으로 종료 된다. 따라서, 조금 위험한 방법이다. ②번 방법의 경우에는 sender가 Button 타입이 아닌 경우에도 프로그램이 종료되지 않고 null값이 return된다. 따라서 ②번 방법을 사용하고 그 아래의 문장에서 if문을 사용하여 btn 값이 null이 아닌 경우에만 정상적인 처리를 하는 식으로 코딩하면 훨씬 안전한 코딩이 될 것이다. 위의 프로그램의 경우에는 화면의 구조가 간단하여 sender는 Button 타입이 확실하므로 에러 확인 과정은 생략하였다. 65번 문장에서 MessageBox에 출력되는 문자열이 어떻게 만들어 지는지 확인하라. 67번 문장에서 MessageBox.Show(msg, Title); 에서 사용된 Title은 윈도우의 Title이다.
20
DockPanel DockPanel을 이해하기 위하여 왼쪽의 코드를 복사해 실행해보라. 프로젝트명: DockPanelTest
파일명: DockPanelTest.cs using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace pjkim.DockPanelTest { class DockPanelTest : Window [STAThread] static void Main() Application app = new Application(); app.Run(new DockPanelTest()); }//Main() //--생성자 public DockPanelTest() Title = "DockPanel 테스트"; SizeToContent = SizeToContent.WidthAndHeight; ResizeMode = ResizeMode.NoResize; Content = CreateDockPanel(); }//DockPanelTest() // DockPanel CreateDockPanel() DockPanel dock = new DockPanel(); dock.LastChildFill = false; dock.Margin = new Thickness(2.5); //DockPanel객체에 다섯 개의 버튼을 추가 for (int i = 1; i <= 5; i++) { Button btn = new Button(); btn.Margin = new Thickness(2.5); btn.Content = "Button" + i; dock.Children.Add(btn); DockPanel.SetDock(btn, Dock.Top); }//for return dock; }//CreateDockPanel() }//class DockPanelTest }//ns 왼쪽의 프로그램은 StackPanel 대신 DockPanel을 사용하고 있다. DockPanel을 사용하여 콘트롤을 패널의 네 변 중 하나에 붙일 수 있다. DockPanel에 다섯 개의 버튼을 붙여놓았다. 왼쪽의 프로그램처럼 버튼을 모두 위쪽(Top)방향으로 붙이면 아래와 같이 앞의 Stack을 사용한 것과 똑같은 결과를 얻을 수 있다. 또, 버튼을 모두 왼쪽(Left)으로 붙이면 역시 StackPanel의 Orientation을 Horizontal로 지정한 것과 같은 결과를 얻을 수 있다. 그러나 DockPanel은 컨트롤을 오른쪽이나, 아래쪽으로도 붙일 수 있기 때문에 그 점에서 StackPanel보다 유연성이 있다.
21
DockPanel 이 프로그램은 DockPanel의 StackPanel과는 다른 점을 설명하기 위하여 약간 다르게 코딩하였다.
왼쪽의 프로그램을 복사하여 실행해보라. 프로젝트명: DockPanelTest2 코드파일명: DockPanelTest2.cs DockPanel using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace pjkim.DockPanelTest { class DockPanelTest : Window [STAThread] static void Main() Application app = new Application(); app.Run(new DockPanelTest()); }//Main() public DockPanelTest() Title = "DockPanel 테스트"; Width = 250; Height = 250; Content = CreateDockPanel(); }//DockPanelTest() DockPanel CreateDockPanel() DockPanel dock = new DockPanel(); dock.LastChildFill = false; Label lbl = new Label(); lbl.FontSize = 20; lbl.HorizontalAlignment = HorizontalAlignment.Center; lbl.Background = Brushes.Bisque; lbl.Foreground = Brushes.Red; lbl.Content = "DockPanel을 소개헙니다"; TextBox tbox = new TextBox(); tbox.FontSize = 16; tbox.Text = "입력: "; Button rbtn = new Button(); rbtn.Content = "오른쪽 버튼"; Button lbtn = new Button(); lbtn.Content = "왼쪽 버튼"; dock.Children.Add(lbl); DockPanel.SetDock(lbl, Dock.Top); dock.Children.Add(rbtn); DockPanel.SetDock(rbtn, Dock.Right); dock.Children.Add(tbox); DockPanel.SetDock(tbox, Dock.Bottom); dock.Children.Add(lbtn); DockPanel.SetDock(lbtn, Dock.Left); return dock; }//CreateDockPanel() }//class DockPanelTest }//ns 앞 페이지의 마지막 설명과 같이 StackPanel은 Child를 한쪽 방향으로만 정렬하는데 비하여 DockPanel은 상, 하, 좌, 우 어느 방향으로든지 위치지청이 가능하다. 또, 추가된 컨트롤 별로 붙이는 방향을 바꾸어 줄 수 있다. 왼쪽의 프로그램에서는 모두 네개 (제목으로 사용하는 Label 객체, 두 개의 Button 객체, 한 개의 TextBox 객체를 생성하여 DockPanel에 추가하고 그 붙이는 위치를 네 변에 위에서부터 시계 방향으로 각각 다르게 지정하였다. 순서대로: Label을 Top으로, 버튼을 Right로 TextBox를 Bottom으로, 또 다른 버튼을 Left로 추가하였다. 추가하는 순서에 따라서 결과가 달라진다. 아래는 프로그램 실행결과이다.
22
빈 화면에 Label을 첫 번째로 오른쪽에 붙이면 화면의 전체 영역에서 위 부분을 차지한다 두 번째로 버튼을 오른쪽에
남은 영역 중에서 오른쪽을 차지한다. 마지막으로 또 하나의 버튼을 왼쪽에 추가하면 현재 남아있는 영역 중 에서 왼쪽에 고정됨 세 번째로 TextBox를 아래쪽에 붙이면 앞의 두 컨트롤이 차지하고 남은 영역 중 아래 부분을 차지한다. 창을 옆으로 잡아당겨서 크기를 늘리면 아래의 그림처럼 Label콘트롤의 양쪽에 빈 공간이 생기지만 양쪽의 버튼은 그 자리를 침범하지 않는다. 양쪽의 빈 공간도 여전히 Label 컨트롤의 영역인 것을 알 수 있다.
23
앞의 프로그램을 함수별로 설명한다. Main()함수 등은 항상 같으므로 앞으로는 제외
DockPanel 객체를 직접 생성해서 Content에 붙여 넣지 않고 CreateDockpanel()함수가 Return하는 DockPanel객체를 사용한다. 앞의 StackPanel객체의 예에서도 이와 같은 방법을 사용하는 것이 더 좋았을 것이다. 라벨생성 텍스트박스 생성 오른쪽버튼 왼쪽 버튼 라벨을 Top에 붙임 오른쪽 버튼을 Right에 붙임 텍스트박스를Bottom에 붙임 왼쪽 버튼을 왼쪽에 붙임
24
앞에서 이야기 하기를 DockPanel에 컨트롤을 추가하는 순서에 따라서
화면의 모습이 약간 달라진다고 하였었다. 그것을 확인하기 위하여 앞의 프로그램에서 컨트롤을 추가하는 순서를 다음과 같이 바꿔보자. 현재: Label(Top) -> 오른쪽Button(Right) -> TextBox(Bottom) -> 왼쪽Button(Left) 수정: : Label(Top) -> 오른쪽Button(Right) -> 왼쪽Button(Left) -> TextBox(Bottom) 위의 첫 번째 줄과 같은 순서였을 때는 이미 본 바와 같이 아래의 왼쪽과 같이 되고 두 번째 줄과 같은 순서로 수정 하였을 경우에는 아래의 오른쪽과 같이 된다. 위의 두 그림에서 중앙에 아무것도 없는 부분은 아직 컨트롤이 차지하지 않은 비어 있는 영역이다. DockPanel에서는 제일 마지막에 추가한 컨트롤이 비어있는 나머지 영역 전체를 다 차지하게 할 수 있다. 앞의 프로그램에서 CreateDockPanel()함수의 앞 부분에 있는 다음의 문장이 있는 것을 확인하라. dock.LastChildFill = false; 위의 문장은 DockPanel객체의 마지막 차일드가 남은 영역 전체를 차지하지 못하도록 막아 놓은 것이다. 위의 문장의 오른쪽의 false를 true로 바꾸거나, 아니면 문장을 주석 처리하여 막아버리면(true가 Default) 아래의 그림처럼 마지막에 추가된 컨트롤이 전체를 자치한 모습을 확인 할 수 있을 것이다. 왼쪽버튼을 제일 마지막에 추가 하면 왼쪽 버튼이나머지 전체 역역을 차지 텍스트박스를 제일 마지막에 추가하면 텍스트박스가 나머지 전체영역을 차지
25
Grid 다음 페이지에계속 됩니다. 마지막으로 소개할 Panel은 Grid이다. Grid는 격자구조를 의미한다.
다음의 코드를 복사하여 실행해 본 후 함수 별로 설명하기로 한다. 옆의 이미지는 이 프로그램의 실행결과이다. using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Input; namespace pjkim.GridTest { class GridTest : Window TextBox idText; PasswordBox passText; [STAThread] static void Main() Application app = new Application(); app.Run(new GridTest()); } //-- 생성자 public GridTest() Title = "Grid Test"; SizeToContent = SizeToContent.WidthAndHeight; ResizeMode = ResizeMode.NoResize; Grid grid = CreateGrid(); SetGrid(grid); Content = grid; 다음 페이지에계속 됩니다.
26
앞 페이지 코드 뒤에 붙여 넣으세요 다음 페이지에계속됩니다.
//-- Grid 생성 후 행과 열을 지정 Grid CreateGrid() { Grid grid = new Grid(); grid.Margin = new Thickness(5); // 3행 추가 grid.RowDefinitions.Add(new RowDefinition()); // 2열 추가 grid.ColumnDefinitions.Add(new ColumnDefinition()); return grid; } //-- 버튼 클릭 이벤트 처리함수 void btn_Click(object sender, RoutedEventArgs args) { string msg = "아직 로그인 처리기능이 완성되지 않았습니다.\n\n" + "아이디: " + idText.Text + "\n" + "비밀번호: " + passText.Password; MessageBox.Show(msg, Title, MessageBoxButton.OK, MessageBoxImage.Exclamation); 다음 페이지에계속됩니다.
27
끝 앞 페이지 코드 뒤에 붙여 넣으세요 //-- 그리드의 내부에 컨트롤을 배치 ----------------
void SetGrid(Grid grid) { Label lbl; //아이디 Label lbl = new Label(); lbl.Content = "아이디: "; lbl.HorizontalAlignment = HorizontalAlignment.Right; lbl.VerticalContentAlignment = VerticalAlignment.Center; lbl.Margin = new Thickness(5); grid.Children.Add(lbl); Grid.SetRow(lbl, 0); Grid.SetColumn(lbl, 0); //비밀번호 Label lbl.Content = "비밀번호: "; Grid.SetRow(lbl, 1); //아이디 텍스트박스 idText = new TextBox(); idText.Width = 200; idText.Margin = new Thickness(5); idText.FontSize = 18; idText.FontWeight = FontWeights.Bold; idText.VerticalContentAlignment = VerticalAlignment.Center; grid.Children.Add(idText); Grid.SetRow(idText, 0); Grid.SetColumn(idText, 1); //비밀번호 텍스트박스(PasswordBox) passText = new PasswordBox(); passText.Width = 200; passText.Margin = new Thickness(5); passText.FontSize = 18; passText.FontWeight = FontWeights.Bold; passText.VerticalContentAlignment = VerticalAlignment.Center; grid.Children.Add(passText); Grid.SetRow(passText, 1); Grid.SetColumn(passText, 1); //로그인버튼 Button btn = new Button(); btn.Margin = new Thickness(5); btn.FontSize = 16; btn.Content = "로그인"; btn.Click += btn_Click; grid.Children.Add(btn); Grid.SetRow(btn, 2); Grid.SetColumn(btn, 1); }//SetGrid() }//class GridTest }//namespace pjkim.GridTest 끝
28
Window의 Content에 Grid객체를 붙임
아래 그림은 Visual Studio 2010에 앞의 프로그램을 입력한 화면의 일부이다. 생성자 함수와 생성자 함수에서 호출하는 CreateGrid()함수의 내용이다. CreateGrid()함수는 3행 2열의 Grid 클래스의 객체를 생성하여 return하는 함수이다. 또, SetGrid()함수는 CreateGrid()함수가 생성한 2행 3열의 그리드에 컨트롤들을 배치하는 함수이다. 앞으로 작성하는 예제 프로그램들은 점점 길고 복잡해 지므로 이와 같이 명령어들을 기능별로 여러 개의 함수에 나누어 코딩 하는 것을 많이 보게 될 것이다. CreateGrid()함수는 3행2열 Grid 객체를 return SetGrid(grid)함수는 전달된 그리드객체에 컨트롤을 배치한다. Window의 Content에 Grid객체를 붙임 Grid 객체 생성 Grid 객체에 2개의 열을 추가 3개의 행을 추가 그리드에 행과 열을 필요한 만큼 추가하는 부분을 눈 여겨 볼 것. Grid객체에는 RowDefinitions라는 Collection객체에 RowDefinition객체를 추가하면 추가한 개수 만큼의 행이 생긴다. 위의 예에서는 RowDefinition객체를 추가하는 문장이 세 개가 있다. 따라서 세 개의 행을 추가한 것이다. 열을 추가하는 방법도 거의 유사하다. 따라서 위의 CreateGrid()함수는 3행 2열의 Grid객체를 Return하게 된다.
29
아래의 그림은 앞의 CreateGrid()함수가 생성한 3행 2열의 그리드의 모습을
그린 것이다. 행과 열의 인덱스 번호는 0부터 시작(1이 아님) 하는 것에 주의하라. 0행 1행 2행 0열 1열 (0, 0) (0, 1) (1, 0) (1, 1) (2, 0) (2, 2) 아래는 3행 2열의 그리드의 셀에 컨트롤을 배치하는 SetGrid()함수의 일부이다. 추가되는 컨트롤이 위치하게 되는 셀을 지정하는 방법을 눈 여겨 보라. SetGrin()함수는 이미 만들어진 Grid 객체를 외부로부터 전달 받는다. 그리드에 Label컨트롤 추가 Label컨트롤의 행을 0번으로 지정 Label컨트롤의 열을 0번으로 지정 그리드에 Label 컨트롤을 추가하고 (0, 0)셀에 배치 또 다른 Label 컨트롤을 추가하고 (1, 0)셀에 배치 TextBox 컨트롤을 추가하고 (0, 1)셀에 배치 그리드에 다른 Label컨트롤 추가 Label컨트롤의 행을 1번으로 지정 그리드에 TextBoxl컨트롤 추가 TextBox컨트롤의 행을 0번으로 지정 TextBox컨트롤의 열을 1번으로 지정
30
하나의 컨트롤을 여러 개의 셀에 걸쳐서 배치하기
아래의 그림은 앞의 SetGtid()함수에 의하여 배치된 컨트롤을 보여주는 화면을 설명하고 있다. (2, 0)셀은 비어있음 (2, 0)셀에는 “로그인”제목의 버튼이 위치 (1, 0)셀에는 “비밀번호” 제목의 Label (0, 0)셀에는 “아이디” (1, 1)셀에는 비밀번호를 입력하는 TextBox가 위치 (0, 1)셀에는 id를 입력하는 하나의 컨트롤을 여러 개의 셀에 걸쳐서 배치하기 Grid객체에 있는 SetRowSpan, SetColumnSpan 함수를 사용하여 하나의 셀을 여러 개의 행 또는 열에 확장시켜서 위치시킬 수 있다. 예를 들어서 앞의 프로그램에서 버튼을 추가할 때 다음과 같이 코딩하면: Grid.SetRow(btn, 2); Grid.SetColumn(btn, 0); Grid.SetColumnSpan(btn, 2); 버튼을 (2,0)셀에 위치 시키고 SetColumnSpan(btn, 2); 로 Column span을 2로 지정하면 아래 그림과 같이 로그인 버튼이 두 개의 열을 차지하게 된다.
31
grid.ShowGridLines = true; grid.ShowGridLines = false;
아래의 그림은 또 다른 프로그램의 실행 결과이다. 6행 4열의 그리드에 컨트롤을 배치 했는데 TextBox입력 콘트롤들은 모두 3개의 열에 걸쳐서 배치되었다. 이렇게 해야 했던 이유는 이 그리드를 작성한 사람이 맨 마지막 행의 두 개의 버튼(확인, 취소)을 오른쪽에 작게 배치하고 싶었던 모양이다. 아래의 왼쪽 그림의 화면의 점선은 프로그램에서 출력한 것으로서 프로그램을 테스트 할 때 전체적인 그리드의 모습과 그 위에 컨트롤이 배치된 것을 확인하기 위 하여 일부러 점선이 보이도록 출력한 것이다. 점선이 보이도록 하기 위하여서는 다음과 같이 Grid객체의 ShowGridLines 프로퍼티에 true값을 대입한다. 물론 최종적으로 컴파일 할 때는 이 값을 false로 지정하거나 문장 자체를 주석 처리하여 점선이 안 보이도록 하여야 할 것이다. false가 default 값이다. grid.ShowGridLines = true; grid.ShowGridLines = false; 여기서는 컨트롤을 여러 개의 열에 확장하는 예만 보여주었지만, SetRowSpan을 사용하여 컨트롤을 여러 개의 행에 걸쳐서 배치하는 것도 물론 가능하다(예: 생략)
32
Panel안에 Panel 배치하기 : 어느 종류의 Panel객체이든지 다른 Panel객체의 컨트롤로 추가될 수 있다.
있는 형태가 반복 될 수 있다. 실제로 하나의 panel에 여러 개의 panel이 위치하고 그 안에 다른 panel이 위치하는 식으로 하여 복잡한 형태의 폼을 구성하는 일은 매우 흔한 일이다. using System; using System.Windows; using System.Windows.Media; using System.Windows.Controls; using System.Windows.Input; namespace pjkim.ThreeStacks { class ThreeStacks : Window [STAThread] static void Main() Application app = new Application(); app.Run(new ThreeStacks()); } //-- 생성자 public ThreeStacks() Title = "ThreeStacks"; SizeToContent = SizeToContent.WidthAndHeight; ResizeMode = ResizeMode.NoResize; Content = CreateThreeStacks(); // StackPanel CreateThreeStacks() StackPanel mainStack = new StackPanel(); mainStack.Margin = new Thickness(2.5); mainStack.Orientation = Orientation.Horizontal; for(int i=0; i<3; i++) { StackPanel colStack = new StackPanel(); mainStack.Children.Add(colStack); for(int j=1; j<=10; j++) { Button btn = new Button(); btn.Margin = new Thickness(2.5); btn.Content = "버튼 " + (i*10+j); colStack.Children.Add(btn); }//for j }//for i return mainStack; }//CreateThreeStacks() }//class ThreeStacks }//namespace 왼쪽의 프로그램은 StackPanel안에 세 개의 다른 StackPanel을 횡으로 배치하고 각각의 StackPanel에 각각 10개씩의 버튼을 수직으로 배치한 프로그램이다. 아래의 이미지는 옆의 프로그램을 실행한 화면이다. 옆의 코드를 복사하여 실제로 프로그램을 실행해 보라. 스택 : 스택 스택 스택
33
외곽의 StackPanel을 생성하고 Orientation을 Horizontal로 지정
아래는 앞의 프로그램을 Visual Studio 2010에 코딩한 화면이다. 처음에 가장 외곽의 StackPanel을 만든다. 그 후 이 스택패널에 세 개의 다른 StackPanel을 생성해서 수평으로 배치하고 각각의 스택패널에 각가 10개의 버튼을 수직으로 배치하는 과정이 중간에 2중 for 문장으로 구현되어 있다. 외곽의 StackPanel을 생성하고 Orientation을 Horizontal로 지정 이 for 문장의 내부는 세 번 반복된다. 내부에서 i의 값은 차례로 0, 1, 2의 값을 같게 된다. 이 내부의 for 문장은 열 번 반복된다. 내부에서 j의 값은 차례로 의 값을 같게 된다. 내부의 StackPanel을 생성하여 외곽의 스택에 추가한다. 모두 세 개의 스택이 추가된다(수평정렬) 한 번 반복할 때마다 버튼이 하나씩 생성되어 내부의 스택에 추가된다. 버튼은 수직으로 정렬된다. 버튼의 번호가 1부터 30까지 부여되는 계산과정을 확인하라 위의 이 중 반복문에서 외부의 반복문이 3번 반복되고 각각에 대하여 내부의 반복문이 10번 반복되므로 결국 Button 객체는 30개 생성된다. 그리고 버튼에 주어지는 번호는 1부터 30까지 계산된다. 아래와 같이 출력하려면 어디를 어떻게 수정해야 하는지 생각해 보고 테스트 해보라 끝
Similar presentations