NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# 29-2. C#의 event. (UserControl, 사용자 정의 컨트롤)

    페이지 정보

    본문

    안녕하세요. 소심비형입니다. 이전 글에 이어서 이벤트에 대한 내용을 좀 더 알아볼건데요. 좀 더 닷넷스러운(?) 예제를 이용하도록 하겠습니다. 개발에 있어서 네이밍 룰이나 코딩 컨벤션을 지키는 것도 상당히 중요한 요소중에 하나이기 때문입니다. 검색해서 나오는 대부분의 예제들이 비슷한 규칙을 따르고 있다는 걸 느끼셨을겁니다. 이는 마이크로소프트에서 정의한 네이밍 룰과 코딩 컨벤션을 그대로 따르고 있기 때문입니다.

    0eJVlry.jpg

     

     

    새로운 이벤트를 만들기 위해 아래 그림처럼 사용자 정의 컨트롤(UserControl)을 하나 추가합니다.

    CB9AcM6.png

     

     

    사용자 정의 컨트롤이 추가되었으면 아래와 같이 디자인하세요. 어차피 이전 예제와 동일한 코드지만, 좀 더 보기좋게 만들기 위해 어느정도 디자인도 필요합니다-_-;

    VcEj4Al.png

     

     

    이 컨트롤을 배포하고, 개발자가 사용한다면 ComboBox에 데이타를 바인딩하고, 상태 보기 버튼을 클릭할 때 ComboBox에서 선택한 값과 바인딩 된 전체 항목중에 몇번째에 위치하고 있는지 알려줘야 합니다. 그 전에 데이타를 바인당 할 수 있는 방법을 제공해야 하는데요. 직접 구현해도 되지만, 귀찮기 때문에 ComboBox를 외부로 노출하여 개발자가 컨트롤 할 수 있도록 하겠습니다.

    UserControl1.cs

    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
     
    namespace WindowsFormsApplication1
    {
        public partial class UserControl1 : UserControl
        {
            public ComboBox ComboBox { get { return this.comboBox; } }
            public Button Button { get { return this.button; } }
     
            public UserControl1()
            {
                InitializeComponent();
            }
        }
    }

     

     

    13~14라인에 사용자 정의 컨트롤 내부에 있는 컨트롤들을 속성을 통해 외부에서 접근할 수 있도록 했습니다. 콤보 박스의 경우에는 데이타를 바인딩해야 하고, 버튼의 경우에는 기본 텍스트가 마음에 들지 않을수도 있기 때문에 외부로 노출했죠. 하지만, 이런 Text의 경우에는 굳이 컨트롤 자체를 외부에 노출하는 것 보다는 아래처럼 간단하게 처리하는게 좋습니다.

    UserControl1.cs

    public ComboBox ComboBox { get { return this.comboBox; } }
    public Button Button { get { return this.button; } }
    public string ButtonText
    {
        get { return this.button.Text; }
        set { this.button.Text = value; }
    }
     
    public UserControl1()
    {
        InitializeComponent();
    }

     

     

    아무튼, 이벤트와 상관 없는 내용으로 글이 길어지고 있네요-_-; 이제 상태 값을 가져올 수 있는 이벤트를 추가해야겠죠? 이전 글과 다르게 이번에는 내부에 랩핑되어 있는 시스템 델리게이트를 사용할겁니다. 제네릭을 이용하므로 먼저 EventArgs를 만들어야 합니다.

    UserControl1.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
     
    namespace WindowsFormsApplication1
    {
        public class UserControlEventArgs : EventArgs
        {
            public string SelectedText { get; private set; }
            public int SelectedIndex { get; private set; }
     
            public UserControlEventArgs(string text, int index)
            {
                SelectedText = text;
                SelectedIndex = index;
            }
        }
    }

     

     

    이벤트를 추가한 전체 코드입니다.

    UserControl1.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
     
    namespace WindowsFormsApplication1
    {
        public partial class UserControl1 : UserControl
        {
            public event EventHandler<UserControlEventArgs> ChangeStatus;
     
            public ComboBox ComboBox { get { return this.comboBox; } }
            public Button Button { get { return this.button; } }
            public string ButtonText
            {
                get { return this.button.Text; }
                set { this.button.Text = value; }
            }
     
            public UserControl1()
            {
                InitializeComponent();
                this.button.Click += Button_Click;
            }
     
            private void Button_Click(object sender, EventArgs e)
            {
                if (ChangeStatus != null)
                {
                    ChangeStatus(this, new UserControlEventArgs(this.comboBox.Text, this.comboBox.SelectedIndex));
                }
            }
        }
     
        public class UserControlEventArgs : EventArgs
        {
            public string SelectedText { get; private set; }
            public int SelectedIndex { get; private set; }
     
            public UserControlEventArgs(string text, int index)
            {
                SelectedText = text;
                SelectedIndex = index;
            }
        }
    }

     

     

    이 코드에서는 델리게이트가 사용되지 않았습니다. 하지만, 아래 그림처럼 EvnetHandler가 델리게이트라는걸 알 수 있습니다. 이벤트의 경우에는 직접 델리게이트를 선언하지는 않습니다. 닷넷에서 제공하고 있는 EventHandler를 사용해야 합니다.

     

    참고로 사용자 정의 컨트롤은 응용 프로그램에서 컨디션 또는 어떤 컨트롤들의 집합을 동시에 핸들링하고자 할 때 주로 사용됩니다. 그렇더라도 지속적으로 변화하는 환경에서 공통된 틀을 운영하는 건 쉽지 않은 일입니다. 그래서, 어느정도 커스터마이징 할 수 있도록 수많은 속성을 외부로 노출하고 있죠. 예로, DevExpress나 Telerik, FarPoint, TeeChart등등... 클라이언트 서드 파티 제품군들이 그렇습니다. 너무 많은 속성들을 외부로 노출하다보니 스크롤 압박이 생기기도 합니다.

     

    Form1로 돌아와서 아래 그림처럼 UserControl1을 폼 위에 던져 놓습니다.

    NWiJfD1.png

     

     

    추가한 사용자 정의 컨트롤의 이벤트를 보면 ChangeStatus라는 새로운 이벤트가 등록되어 있는게 보일겁니다. 더블 클릭하여 이벤트 처리기를 추가하고, 코드 보기로 이동하세요.

    HVws6q0.png

     

     

    아래 코드는 이 콘트롤을 사용하는 방법을 나타내고 있습니다. 우선 데이타를 추가한 후 이벤트 처리기에서 적절하게 사용하면 되겠습니다.

    Form1.cs

    using System;
    using System.Windows.Forms;
     
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
     
                userControl11.ComboBox.Items.Add("박근혜");
                userControl11.ComboBox.Items.Add("최순실");
                userControl11.ComboBox.Items.Add("김기춘");
                userControl11.ComboBox.Items.Add("우병우");
                userControl11.ComboBox.Items.Add("정유라");
                userControl11.ComboBox.Items.Add("장시호");
            }
     
            private void userControl11_ChangeStatus(object sender, UserControlEventArgs e)
            {
                MessageBox.Show(
                    this,
                    $"{e.SelectedText}(은)는 게이트의 {e.SelectedIndex + 1}번째 주인공입니다.",
                    "Warning",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Warning
                    );
            }
        }
    }

     

     

    실행하면 아래처럼 동작하는 것을 확인할 수 있습니다. 예외처리도 없고, 복잡하지도 않기 때문에 정상 동작하는 듯 보일겁니다. 하지만, 복잡(?)한 컨트롤의 경우 상속 받은 또는 내부 자식 컨트롤의 이벤트를 중복해서 사용하거나, 외부로 노출할 경우 문제가 발생될수도 있습니다.

     

    예를 들어, 이 사용자 정의 컨트롤에서는 버튼을 외부로 노출하고 있는데요. 클라이언트 개발자가 이 버튼의 클릭 이벤트를 사용하려고 한다면, 이미 사용중이기 때문에 이벤트 버블링이 발생합니다. 그렇기 때문에 내부 컨트롤은 캡슐화하여 숨기고 필요한 기능만 속성을 통해 외부로 노출해야 합니다. 그렇지 않다면, 사용자 지정 컨트롤을 이용하여 컴포넌트 단계부터 만들어야 합니다.

    moc67KP.jpg

     

     

    이벤트를 통해 델리게이트에 대해 알아봤습니다. 언제 이벤트가 발생할지 알 수 없기 때문에 컨트롤은 대리자를 만들어두고, 사용자는 필요할 때 대리자를 통해 이벤트 처리기에 위임하여 처리하게 됩니다. 이외에도 델리게이트 체인이라는 기능이 있습니다. 이는 연속되는 연산을 처리할 때 상당히 유용합니다. 그래서 보통 계산기를 예로 들곤 하죠^^;

     

    다음 시간에...

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.