연습: C#에서 복합 컨트롤 작성

복합 컨트롤은 사용자 지정 그래픽 인터페이스를 만들고 재사용할 수 있는 방법을 제공합니다. 복합 컨트롤은 기본적으로 시각적 표현이 있는 구성 요소입니다. 따라서 사용자 입력의 유효성을 검사하고 표시 속성을 수정하거나 작성자가 요구하는 다른 작업을 수행하여 기능을 확장할 수 있는 하나 이상의 Windows Forms 컨트롤, 구성 요소 또는 코드 블록으로 구성할 수 있습니다. 복합 컨트롤은 다른 컨트롤과 동일한 방식으로 Windows Forms에 배치할 수 있습니다. 이 연습의 첫 번째 부분에서는 ctlClock이라는 간단한 복합 컨트롤을 만듭니다. 두 번째 부분에서는 상속을 통해 ctlClock의 기능을 확장합니다.

프로젝트 만들기

새 프로젝트를 만들 때는 이에 대한 이름을 지정하여 루트 네임스페이스, 어셈블리 이름 및 프로젝트 이름을 설정하고 기본 구성 요소가 올바른 네임스페이스에 있는지 확인합니다.

ctlClockLib 컨트롤 라이브러리 및 ctlClock 컨트롤을 만들려면

  1. Visual Studio에서 새 Windows Forms 컨트롤 라이브러리 프로젝트를 만들고 이름을 ctlClockLib로 지정합니다.

    프로젝트 이름, ctlClockLib는 기본적으로 루트 네임스페이스에도 할당됩니다. 루트 네임스페이스는 어셈블리에서 구성 요소의 이름을 정규화하는 데 사용됩니다. 예를 들어 두 어셈블리에서 ctlClock이라는 구성 요소를 제공하면 ctlClockLib.ctlClock.을 사용하여 ctlClock 구성 요소를 지정할 수 있습니다.

  2. 솔루션 탐색기에서 UserControl1.cs를 마우스 오른쪽 단추로 클릭한 다음, 이름 바꾸기를 클릭합니다. 파일 이름을 ctlClock.cs로 변경합니다. 코드 요소 “UserControl1”에 대한 모든 참조 이름을 변경할지 묻는 메시지가 표시되면 단추를 클릭합니다.

    참고

    기본적으로 복합 컨트롤은 시스템에서 제공하는 UserControl 클래스에서 상속됩니다. UserControl 클래스는 모든 복합 컨트롤에 필요한 기능을 제공하며 표준 메서드 및 속성을 구현합니다.

  3. 파일 메뉴에서 모두 저장을 클릭하여 프로젝트를 저장합니다.

복합 컨트롤에 Windows 컨트롤 및 구성 요소 추가

시각적 인터페이스는 복합 컨트롤의 필수적인 부분입니다. 이 시각적 인터페이스는 하나 이상의 Windows 컨트롤을 디자이너 화면에 추가하여 구현합니다. 다음 데모에서는 Windows 컨트롤을 복합 컨트롤에 통합하고 기능을 구현하는 코드를 작성합니다.

복합 컨트롤에 레이블 및 타이머를 추가하려면

  1. 솔루션 탐색기에서 ctlClock.cs를 마우스 오른쪽 단추로 클릭한 다음, 뷰 디자이너를 클릭합니다.

  2. 도구 상자에서 공용 컨트롤 노드를 확장한 후 레이블을 두 번 클릭합니다.

    label1이라는 Label 컨트롤이 디자이너 화면의 컨트롤에 추가됩니다.

  3. 디자이너에서 label1을 클릭합니다. 속성 창에서 다음 속성을 설정합니다.

    속성 다음으로 변경
    이름 lblDisplay
    Text (blank space)
    TextAlign MiddleCenter
    Font.Size 14
  4. 도구 상자에서 구성 요소 노드를 확장한 후 타이머를 두 번 클릭합니다.

    Timer는 구성 요소이므로 런타임에는 시각적 표현이 없습니다. 따라서 디자이너 화면에 컨트롤과 함께 나타나지 않으며 구성 요소 디자이너(디자이너 화면 맨 아래 트레이)에 나타납니다.

  5. 구성 요소 디자이너에서 timer1을 클릭한 다음, Interval 속성을 1000으로 설정하고 Enabled 속성을 true로 설정합니다.

    Interval 속성은 Timer 구성 요소가 선택되는 빈도를 제어합니다. timer1이 틱할 때마다 timer1_Tick 이벤트에 있는 코드를 실행합니다. 간격은 틱 사이에 시간(밀리초)을 나타냅니다.

  6. 구성 요소 디자이너에서 timer1을 두 번 클릭하여 ctlClock에 대한 timer1_Tick 이벤트로 이동합니다.

  7. 다음 코드 샘플과 비슷하도록 코드를 수정합니다. private에서 protected로 액세스 한정자를 변경해야 합니다.

    protected void timer1_Tick(object sender, System.EventArgs e)
    {
        // Causes the label to display the current time.
        lblDisplay.Text = DateTime.Now.ToLongTimeString();
    }
    

    이 코드를 통해 lblDisplay에 현재 시간이 표시됩니다. timer1의 간격은 1000으로 설정되었으므로 이 이벤트는 천 밀리초마다 발생하므로 현재 시간이 1초마다 업데이트됩니다.

  8. 메서드를 virtual 키워드로 재정의 가능하도록 수정합니다. 자세한 내용은 아래의 “사용자 컨트롤에서 상속” 섹션을 참조하세요.

    protected virtual void timer1_Tick(object sender, System.EventArgs e)
    
  9. 파일 메뉴에서 모두 저장을 클릭하여 프로젝트를 저장합니다.

복합 컨트롤에 속성 추가

이제 시계 컨트롤은 각각 고유한 내재된 속성 세트가 있는 Label 컨트롤과 Timer 구성 요소를 캡슐화합니다. 이러한 컨트롤의 개별 속성은 이후 컨트롤 사용자가 액세스할 수 없지만 적절한 코드 블록을 작성하여 사용자 지정 속성을 만들고 노출할 수 있습니다. 다음 프로시저에서 사용자가 배경 및 텍스트 색을 변경할 수 있도록 컨트롤에 속성을 추가합니다.

복합 컨트롤에 속성을 추가하려면

  1. 솔루션 탐색기에서 ctlClock.cs를 마우스 오른쪽 단추로 클릭한 다음, 코드 보기를 클릭합니다.

    컨트롤에 대한 코드 편집기가 열립니다.

  2. public partial class ctlClock 문을 찾습니다. 여는 중괄호 ({) 아래에 다음 코드를 입력합니다.

    private Color colFColor;
    private Color colBColor;
    

    이러한 문은 작성하려는 속성에 대한 값을 저장하는 데 사용할 private 변수를 만듭니다.

  3. 2단계의 변수 선언 바로 아래에 다음 코드를 입력하거나 붙여넣습니다.

    // Declares the name and type of the property.
    public Color ClockBackColor
    {
        // Retrieves the value of the private variable colBColor.
        get
        {
            return colBColor;
        }
        // Stores the selected value in the private variable colBColor, and
        // updates the background color of the label control lblDisplay.
        set
        {
            colBColor = value;
            lblDisplay.BackColor = colBColor;
        }
    }
    // Provides a similar set of instructions for the foreground color.
    public Color ClockForeColor
    {
        get
        {
            return colFColor;
        }
        set
        {
            colFColor = value;
            lblDisplay.ForeColor = colFColor;
        }
    }
    

    위의 코드는 ClockForeColorClockBackColor라는 두 사용자 지정 속성을 만들며 이 속성은 이 컨트롤의 후속 사용자에게 제공됩니다. getset 문은 속성 값의 스토리지 및 검색과 속성에 맞는 기능을 구현하는 코드를 위해 제공됩니다.

  4. 파일 메뉴에서 모두 저장을 클릭하여 프로젝트를 저장합니다.

컨트롤 테스트

컨트롤은 독립 실행형 애플리케이션이 아니며 컨테이너에서 호스팅해야 합니다. 컨트롤의 런타임 동작을 테스트하고 UserControl 테스트 컨테이너로 해당 속성을 실행합니다. 자세한 내용은 방법: UserControl의 런타임 동작 테스트를 참조하세요.

컨트롤을 테스트하려면

  1. F5 키를 눌러 프로젝트를 빌드하고 UserControl 테스트 컨테이너에서 컨트롤을 실행합니다.

  2. 테스트 컨테이너의 속성 눈금에서 ClockBackColor 속성을 찾은 후 해당 속성을 선택하여 색상표를 표시합니다.

  3. 클릭하여 색을 선택합니다.

    컨트롤의 배경색이 선택한 색으로 바뀝니다.

  4. 유사한 이벤트 시퀀스를 사용하여 ClockForeColor 속성이 예상대로 작동하는지 확인합니다.

    이 섹션과 이전 섹션에서는 구성 요소 및 Windows 컨트롤을 코드와 결합하고 패키징하여 복합 컨트롤 형태로 사용자 지정 기능을 제공하는 방법을 살펴보았습니다. 복합 컨트롤에 속성을 노출하고 완료한 후에는 컨트롤을 테스트하는 방법도 알아보았습니다. 다음 섹션에서는 ctlClock을 기본으로 사용하여 상속된 복합 컨트롤을 생성하는 방법에 대해 알아봅니다.

복합 컨트롤에서 상속

이전 섹션에서는 Windows 컨트롤, 구성 요소 및 코드를 재사용 가능한 복합 컨트롤에 결합하는 방법을 알아보았습니다. 이제 복합 컨트롤을 다른 컨트롤을 작성하기 위한 기본으로 활용할 수 있습니다. 기본 클래스에서 클래스를 파생시키는 과정을 상속이라고 합니다. 이 섹션에서는 ctlAlarmClock이라는 복합 컨트롤을 만듭니다. 이 컨트롤은 부모 컨트롤인 ctlClock에서 파생됩니다. 부모 메서드를 재정의하고 새 메서드 및 속성을 추가하여 ctlClock의 기능을 확장하는 방법을 알아보겠습니다.

상속된 컨트롤을 만드는 첫 번째 단계는 부모에서 파생시키는 것입니다. 이 작업은 속성, 메서드 및 부모 컨트롤의 그래픽 특성을 모두 포함하는 새 컨트롤을 만들지만 신규 또는 수정된 기능 추가를 위한 기본으로도 활용할 수 있습니다.

상속된 컨트롤을 만들려면

  1. 솔루션 탐색기에서 ctlClockLib를 마우스 오른쪽 단추로 클릭하고, 추가를 가리킨 다음, 사용자 컨트롤을 클릭합니다.

    새 항목 추가 대화 상자가 열립니다.

  2. 상속된 사용자 정의 컨트롤 템플릿을 선택합니다.

  3. 이름 상자에 ctlAlarmClock.cs를 입력한 다음 추가를 클릭합니다.

    상속 선택 대화 상자가 나타납니다.

  4. 구성 요소 이름 아래에서 ctlClock을 두 번 클릭합니다.

  5. 솔루션 탐색기에서 현재 프로젝트를 찾아봅니다.

    참고

    ctlAlarmClock.cs라는 파일이 현재 프로젝트에 추가되었습니다.

경보 속성 추가

속성은 복합 컨트롤에 추가된 것과 같은 방식으로 상속된 컨트롤에 추가됩니다. 이제 속성 선언 구문을 사용하여 컨트롤에 두 가지 속성, 즉 경보가 꺼지는 날짜와 시간 값을 저장하는 AlarmTime과 경보가 설정되는지 여부를 나타내는 AlarmSet을 추가합니다.

복합 컨트롤에 속성을 추가하려면

  1. 솔루션 탐색기에서 ctlAlarmClock을 마우스 오른쪽 단추로 클릭한 다음, 코드 보기를 클릭합니다.

  2. public class 문을 찾습니다. 컨트롤은 ctlClockLib.ctlClock에서 상속됩니다. 여는 중괄호 ({) 문 아래에 다음 코드를 입력합니다.

    private DateTime dteAlarmTime;
    private bool blnAlarmSet;
    // These properties will be declared as public to allow future
    // developers to access them.
    public DateTime AlarmTime
    {
        get
        {
            return dteAlarmTime;
        }
        set
        {
            dteAlarmTime = value;
        }
    }
    public bool AlarmSet
    {
        get
        {
            return blnAlarmSet;
        }
        set
        {
            blnAlarmSet = value;
        }
    }
    

컨트롤의 그래픽 인터페이스에 추가

상속된 컨트롤에는 상속 원본 컨트롤과 동일한 시각적 인터페이스가 있습니다. 부모 컨트롤과 동일한 구성 요소 컨트롤을 소유하지만, 구체적으로 노출되지 않는 한, 구성 요소 컨트롤의 속성을 사용할 수 없습니다. 복합 컨트롤에 추가하는 것과 같은 방법으로 상속된 복합 컨트롤의 그래픽 인터페이스에 추가할 수 있습니다. 알람 시계의 시각적 인터페이스에 계속 추가하려면 경보가 울릴 때 깜박거리는 레이블 컨트롤을 추가합니다.

레이블 컨트롤을 추가하려면

  1. 솔루션 탐색기에서 ctlAlarmClock을 마우스 오른쪽 단추로 클릭한 다음, 뷰 디자이너를 클릭합니다.

    ctlAlarmClock에 대한 디자이너가 주 창에 열립니다.

  2. 컨트롤의 표시 부분을 클릭하고 속성 창을 확인합니다.

    참고

    모든 속성이 표시되는 동안 흐리게 표시됩니다. 이것은 이러한 속성이 lblDisplay의 기본 속성이며 속성 창에서 수정하거나 액세스할 수 없음을 나타냅니다. 기본적으로 복합 컨트롤에 포함된 컨트롤은 private이며 해당 속성은 어떤 방법으로도 액세스할 수 없습니다.

    참고

    복합 컨트롤의 후속 사용자가 내부 컨트롤에 액세스할 수 있도록 하려면 이를 public 또는 protected로 선언합니다. 이렇게 하면 적절한 코드를 사용하여 복합 컨트롤 내에 포함된 컨트롤의 속성을 설정 및 수정할 수 있습니다.

  3. Label 컨트롤을 복합 컨트롤에 추가합니다.

  4. 마우스를 사용하여 Label 컨트롤을 표시 상자 바로 아래로 끌어옵니다. 속성 창에서 다음 속성을 설정합니다.

    속성 설정
    이름 lblAlarm
    Text Alarm!
    TextAlign MiddleCenter
    Visible false

경보 기능 추가

이전 프로시저에서는 복합 컨트롤에서 경보 기능을 설정하는 속성 및 컨트롤을 추가했습니다. 이 프로시저에서는 현재 시간을 경보 시간과 비교하여 같으면 깜박이는 코드를 추가합니다. ctlClocktimer1_Tick 메소드를 재정의하고 코드를 더 추가하여 ctlClock의 모든 고유 기능을 유지하면서 ctlAlarmClock의 기능을 확장합니다.

ctlClock의 timer1_Tick 메서드를 재정의하려면

  1. 코드 편집기에서 private bool blnAlarmSet; 문을 찾습니다. 바로 아래에서 다음 문을 추가합니다.

    private bool blnColorTicker;
    
  2. 코드 편집기에서 클래스 끝에 있는 닫는 중괄호 (})를 찾습니다. 중괄호 바로 앞에 다음 코드를 추가합니다.

    protected override void timer1_Tick(object sender, System.EventArgs e)
    {
        // Calls the Timer1_Tick method of ctlClock.
        base.timer1_Tick(sender, e);
        // Checks to see if the alarm is set.
        if (AlarmSet == false)
            return;
        else
            // If the date, hour, and minute of the alarm time are the same as
            // the current time, flash an alarm.
        {
            if (AlarmTime.Date == DateTime.Now.Date && AlarmTime.Hour ==
                DateTime.Now.Hour && AlarmTime.Minute == DateTime.Now.Minute)
            {
                // Sets lblAlarmVisible to true, and changes the background color based on
                // the value of blnColorTicker. The background color of the label
                // will flash once per tick of the clock.
                lblAlarm.Visible = true;
                if (blnColorTicker == false)
                {
                    lblAlarm.BackColor = Color.Red;
                    blnColorTicker = true;
                }
                else
                {
                    lblAlarm.BackColor = Color.Blue;
                    blnColorTicker = false;
                }
            }
            else
            {
                // Once the alarm has sounded for a minute, the label is made
                // invisible again.
                lblAlarm.Visible = false;
            }
        }
    }
    

    이 코드를 추가하려면 여러 작업을 수행할 수 있습니다. override 문은 기본 컨트롤에서 상속된 메서드 대신, 이 메서드를 사용하도록 컨트롤에 지시합니다. 이 메서드가 호출되면 base.timer1_Tick 문을 호출하여 이를 재정의하는 메서드를 호출하여 원래 컨트롤에 통합된 모든 기능이 이 컨트롤에서 재현되도록 합니다. 그런 다음 경보 기능을 통합하는 추가 코드를 실행합니다. 경보가 발생하면 깜박이는 레이블 컨트롤이 나타납니다.

    알람 시계 컨트롤이 거의 완료되었습니다. 이제 해제하는 방법을 구현하는 것만 남았습니다. 이를 위해서는 lblAlarm_Click 메서드에 코드를 추가합니다.

shutoff 메서드를 구현하려면

  1. 솔루션 탐색기에서 ctlAlarmClock.cs를 마우스 오른쪽 단추로 클릭한 다음, 뷰 디자이너를 클릭합니다.

    디자이너가 열립니다.

  2. 단추를 폼에 추가합니다. 단추의 속성을 다음과 같이 설정합니다.

    속성
    이름 btnAlarmOff
    Text 경보 사용 안 함
  3. 디자이너에서 btnAlarmOff를 두 번 클릭합니다.

    코드 편집기private void btnAlarmOff_Click 행이 열립니다.

  4. 이 메서드를 다음 코드와 비슷하도록 수정합니다.

    private void btnAlarmOff_Click(object sender, System.EventArgs e)
    {
        // Turns off the alarm.
        AlarmSet = false;
        // Hides the flashing label.
        lblAlarm.Visible = false;
    }
    
  5. 파일 메뉴에서 모두 저장을 클릭하여 프로젝트를 저장합니다.

양식에서 상속된 컨트롤 사용

기본 클래스 컨트롤 ctlClock을 테스트했던 것과 같은 방법으로 상속된 컨트롤을 테스트할 수 있습니다. F5 키를 눌러 프로젝트를 빌드하고 UserControl 테스트 컨테이너에서 컨트롤을 실행합니다. 자세한 내용은 방법: UserControl의 런타임 동작 테스트를 참조하세요.

사용할 컨트롤을 배치하려면 폼에서 컨트롤을 호스트해야 합니다. 표준 복합 컨트롤과 마찬가지로 상속된 복합 컨트롤은 독립 실행형으로 사용할 수 없으며 폼 또는 다른 컨테이너에서 호스트해야 합니다. ctlAlarmClock은 보다 심도 있는 기능을 포함하므로 테스트하려면 추가 코드가 필요합니다. 이 프로시저에서는 ctlAlarmClock의 기능을 테스트하는 간단한 프로그램을 작성합니다. ctlAlarmClockAlarmTime 속성을 설정하고 표시하는 코드를 작성하고 고유한 기능을 테스트합니다.

컨트롤을 빌드하고 테스트 폼에 추가하려면

  1. 솔루션 탐색기에서 ctlClockLib를 마우스 오른쪽 단추로 클릭한 다음, 빌드를 클릭합니다.

  2. Windows 애플리케이션 프로젝트를 솔루션에 추가하고 이름을 Test로 지정합니다.

  3. 솔루션 탐색기에서 테스트 프로젝트에 대한 참조 노드를 마우스 오른쪽 단추로 클릭합니다. 참조 추가 대화 상자를 표시하려면 참조 추가를 클릭합니다. 프로젝트로 레이블이 지정된 탭을 클릭합니다. ctlClockLib 프로젝트가 프로젝트 이름 아래에 나열됩니다. 프로젝트를 두 번 클릭하여 테스트 프로젝트에 참조를 추가합니다.

  4. 솔루션 탐색기에서 Test를 마우스 오른쪽 단추로 클릭한 다음, 빌드를 클릭합니다.

  5. 도구 상자에서 ctlClockLib 구성 요소 노드를 확장합니다.

  6. ctlAlarmClock을 두 번 클릭하여 ctlAlarmClock의 복사본을 폼에 추가합니다.

  7. 도구 상자에서 DateTimePicker를 찾아서 두 번 클릭하고, DateTimePicker 컨트롤을 양식에 추가한 다음, 레이블을 두 번 클릭하여 Label 컨트롤을 추가합니다.

  8. 마우스를 사용하여 폼의 편리한 위치에 컨트롤을 배치합니다.

  9. 다음 방식으로 이러한 컨트롤의 속성을 설정합니다.

    제어 속성 Value
    label1 Text (blank space)
    이름 lblTest
    dateTimePicker1 이름 dtpTest
    형식 Time
  10. 디자이너에서 dtpTest를 두 번 클릭합니다.

    코드 편집기private void dtpTest_ValueChanged가 열립니다.

  11. 다음과 비슷하도록 코드를 수정합니다.

    private void dtpTest_ValueChanged(object sender, System.EventArgs e)
    {
        ctlAlarmClock1.AlarmTime = dtpTest.Value;
        ctlAlarmClock1.AlarmSet = true;
        lblTest.Text = "Alarm Time is " +
            ctlAlarmClock1.AlarmTime.ToShortTimeString();
    }
    
  12. 솔루션 탐색기에서 Test를 마우스 오른쪽 단추로 클릭한 다음, 시작 프로젝트로 설정을 클릭합니다.

  13. 디버그 메뉴에서 디버깅 시작을 클릭합니다.

    테스트 프로그램이 시작됩니다. 현재 시간은 ctlAlarmClock 컨트롤에서 업데이트되고 시작 시간은 DateTimePicker 컨트롤에 표시됩니다.

  14. 시간의 분이 표시되는 DateTimePicker를 클릭합니다.

  15. 키보드를 사용하여 ctlAlarmClock으로 표시된 현재 시간보다 1분 큰 값(분)을 설정합니다.

    경보 설정 시간이 lblTest에 표시됩니다. 표시된 시간이 경보 설정 시간에 도달할 때까지 기다립니다. 표시된 시간이 경보 설정 시간에 도달하면 lblAlarm이 깜박입니다.

  16. btnAlarmOff을 클릭하여 경보를 해제합니다. 이제 경보를 다시 설정할 수 있습니다.

이 문서에서는 여러 가지 주요 개념을 살펴보았습니다. 컨트롤 및 구성 요소를 복합 컨트롤 컨테이너에 결합하여 복합 컨트롤을 만드는 방법을 배웠습니다. 컨트롤에 속성을 추가하고 사용자 지정 기능을 작성하는 코드를 작성하는 방법도 알아봤습니다. 마지막 섹션에서는 상속성을 통해 지정된 복합 컨트롤의 기능을 확장하고 해당 메서드를 재정의하여 호스트 메서드의 기능을 수정해보았습니다.

참고 항목