이 글은 제가 프로그래밍 초심자일때 작성하여 가독성이 떨어질 수 있으며(작성하는 현재 최대한 보완할 예정입니다.) 좋지 못한 습관을 나타낼 수 있으므로 참고만 하도록 합시다!


최종 프로그램은 현재시간, 타이머, 스톱워치를 포함하고 있습니다.




현재시각 - 사전설정


먼저 윈폼의 도구 상자에서 타이머를 꺼내옵니다.


그럼 위처럼 하단에 타이머가 배치됩니다.


타이머는 속성에서 시작한 상태로 동작할 것인지, 시간 간격은 어떻게 설정할 것인지 설정할 수 있습니다. 물론 코드에서도 같은 설정이 가능합니다.


필자는 레이블을 하나만 추가하여 레이아웃을 위와같이 구성하였습니다. 이 상태로 프로그래밍을 진행하도록 하겠습니다.




현재시각 - 구현

먼저 시계를 만들기 위해서는 DateTime 클래스를 활용해야 합니다. DateTime은 시스템의 시각을 가져옵니다. 이것과 타이머를 적절이 이용하면 정말 간단하게 현재시각을 출력할 수 있습니다.


먼저 시간을 관리할 클래스를 하나 생성하도록 하겠습니다. 윈폼을 이용하는 만큼 클래스를 생성할때는 public partial class Form1 : Form 아래에 만드는 것이 좋습니다. 이것은 디자인 탭에서 오류가 발생하는 것을 막을 수 있습니다.


class myTime
{
    private static DateTime now = DateTime.Now;
    private static string All;
    private static int Hour;
    private static int Minute;
    private static int Second;

    public static void updateNow()
    {
        now = DateTime.Now;
        int Hour = now.Hour;
        int Minute = now.Minute;
        int Second = now.Second;
        All = this.Hour + "시 " + this.Min + "분 " + this.Sec + "초";
    }

    public static stirng getNow()
    {
        return this.All;
    }
}

필자는 위와같이 생성하였습니다. 위에서 언급했듯이 참고만 하도록 합시다!


이 후 타이머를 작동시키도록 하겠습니다. Form1_Load에서 타이머를 작동시키도록 할 것이며, 시간 간격 주기는 1초로 설정하겠습니다. Form1_Load는 디자인 탭에서 Form1을 더블클릭 하는 것으로 손쉽게 생성할 수 있습니다.


private void Form1_Load(object sender, EventArgs e)
{
    timer1.Start();
    timer1.Interval = 1000;
}


다음으로는 timer1을 더블클릭하여 Tick 함수를 생성합시다. 그리고 위에서 생성했던 클래스와 클래스 함수를 사용할 것입니다.


private void timer1_Tick(object sender, EventArgs e)
{
    myTime.updateNow();
    label1.Text = myTime.getNow();
}

현재 사용한 클래스와 함수는 static으로 작성되었기 때문에 별도로 생성하지 않고 사용할 수 있는 것입니다.


DateTime은 시스템의 시간을 호출합니다. 호출한 당시의 시간만 가져오기 때문에 지속적으로 updateNow()를 호출하여 시간을 변경하여 레이블에 띄워주어야 합니다.




현재시각 - 결과





타이머 - 사전설정

타이머에는 어떤게 들어가야 할까요? 타이머인 만큼 남은 시간을 보여주는 레이블과 사용자가 시간을 설정한 텍스트 필드가 필요할 것입니다. 저는 시, 분, 초를 각각 나눠 3개를 생성하였습니다. 그리고 타이머를 시작할 버튼이 필요하고 남은 시간을 직관적으로 표현할 프로그래스바가 필요합니다.


글쓴이가 구상한 레이아웃은 위와 같습니다. 이 동작은 timer2를 통해 진행됩니다.




타이머 - 구현

class myTimer
{
    public static int Hour;
    public static int Minute;
    public static int Second;
    private static string All;

    public static void updateTime()
    {
        All = this.Hour + " : " + this.Min + " : " + this.Sec;
    }

    public static string getTime()
    {
        return this.All;
    }

    // 프로그래스바를 계산할 값
    public static int allTime;
    public static int nowTime;

    public static void initTimeValue()
    {
        allTime = this.Hour * 3600 + this.Minute * 60 + this.Second;
        nowTime = this.Hour * 3600 + this.Minute * 60 + this.Second;
    }
    public static void updateTimeValue()
    {
        nowTime = this.Hour * 3600 + this.Minute * 60 + this.Second;
    }
}

아까와 같이 사용할 클래스를 하나 생성하였습니다. 이후 버튼을 클릭하면 동작할 이벤트를 작성합시다. 이벤트 생성 방법은 다 아시죠? 아까와 같습니다.


private void button1_Click(object sender, EventArgs e)
{
    if(textBox1.Text.Trim().Length == 0 && textBox2.Text.Trim().Length == 0 && textBox3.Text.Trim().Length == 0)
    {
        // 사용자가 모든 칸에 아무것도 입력하지 않았으면 동작하지 않습니다.
        MessageBox.Show("값을 입력하여 주십시오.", "오류");
    }
    else
    {
        if (textBox1.Text.Trim().Length == 0) 
        // 시단위 텍스트 박스가 비어있을 때
        {
            myTimer.Hour = 0;
        }
        else
        {
            myTimer.Hour = int.Parse(textBox1.Text));
            // 텍스트박스는 String 형태로 저장되므로 int형으로 전환
        }

        if (textBox2.Text.Trim().Length == 0) 
        // 시단위 텍스트 박스가 비어있을 때
        {
            myTimer.Minute = 0;
        }
        else
        {
            myTimer.Minute = int.Parse(textBox2.Text);
            // 텍스트박스는 String 형태로 저장되므로 int형으로 전환
        }

        if (textBox3.Text.Trim().Length == 0) 
        // 시단위 텍스트 박스가 비어있을 때
        {
            myTimer.Second = 0;
        }
        else
        {
            myTimer.Second = int.Parse(textBox3.Text);
            // 텍스트박스는 String 형태로 저장되므로 int형으로 전환
        }

        label2.Text = myTimer.getTime();
        myTimer.initTimeValue();

        timer2.start();
        timer2.Interval = 1000;
    }
}

코드는 길었지만 그다지 어려운 부분은 없습니다. 찬찬히 따라해 보세요. 이제 버튼 이벤트를 만들었다면 타이머 이벤트를 만들어야 합니다.


private void timer2_Tick(object sender, EventArgs e)
{
    if(myTimer.Second == 0)
    {
        if(myTimer.Minute == 0)
        {
            if(myTimer.Hour == 0)
            {
                timer2.Stop(); // 초,분,시가 0이되면 끝난다.
                MessageBox.Show("종료 되었습니다.", "완료");
                return;
            }
            else
            {
                myTimer.Hour--;
                myTimer.Minute = 60;
            }
        }
        else
        {
            myTimer.Minute--;
            myTimer.Second = 59;
        }
    }
    else
    {
        myTimer.Second--;
    }

    // 게이지 바를 출력할 부분입니다.

    // 남은 시각을 시간이 변할때 마다 업데이트를 해줘야 합니다.
    myTimer.updateTimeValue();
    
    // float 형식으로 만들지 않으면 게이지 값이 정확하게 출력되지 않습니다.
    float x = (myTimer.allTimeValue - myTimer.nowTimeValue) / (float)myTimer.allTimeValue * 100;
    progressBar1.Value = (int)x;

    label2.Text = myTimer.getTime();
}

타이머 생성은 현재시간을 출력하는 것 보다는 구현이 어려웠습니다. 현재시간은 현재시간을 출력해 주기만 하면 되지만 타이머는 입력을 처리하고 타이머 기능을 구현하는 등 여러가지 고려사항이 있기 때문입니다.




스톱워치 - 사전설정

스톱워치의 가장 기본적인 동작을 살펴볼까요? 스톱워치는 기본적으로 밀리초 단위를 측정하는 경우가 많습니다. 시간을 측정한다는 것은 정밀한 작업입니다.


들어가야할 레이아웃을 고려해 볼까요? 흘러가는 밀리초 단위의 시간을 표현할 레이블시작하고 중단한 버튼과 모든 것을 리셋할 버튼 또한 현재 밀리초의 시간을 기록하는 버튼이 필요하고 기록된 시간이 나열될 리스트박스가 필요합니다.


글쓴이가 구상한 레이아웃은 다음과 같습니다. 이 동작은 timer3를 통해서 구현하겠습니다.




스톱워치 - 구현

class myStopwatch
{
    static public int MilliSecond = 0;
    static public int Second = 0;
    static public ont Minute = 0;

    static public bool start = false;
}

실제로는 클래스내의 변수를 직접 건드리는게 아니라 getSecond()setSecond()와 같은 함수를 만들어 캡슐화를 유지해야 합니다. 하지만 저는 코드를 좀 간결하게 보실 수 있도록 간단하게 작업하겠습니다.


다음은 시작/정지 버튼의 이벤트를 구현한 코드입니다.


private void StartOrStop_Click(object sender, EventArgs e)
{
    if(myStopwatch.start == false)
    {
        timer3.Start();
        timer3.Interval = 10;
        myStopwatch.start = true;
    }
    else if(myStopwatch.start == true)
    {
        timer3.Stop();
        myStopwatch.start = false;
    }
}

기본적으로 이 버튼은 처음 누르면 시작으로 셋팅을 해야하므로 start변수는 기본적으로 false가 되어 있습니다. 이와같이 버튼 하나로 2가지의 기능을 할 수 있도록 구현할 수 있습니다.


다음은 리셋 버튼의 이벤트를 구현한 코드입니다.


private void Reset_Click(object sender, EventArgs e)
{
    myStopwatch.MilliSecond = 0;
    myStopwatch.Second = 0;
    myStopwatch.Minute = 0;
    myStopwatch.start = false;
    label3.Text = myStopwatch.Minute + " : " + myStopwatch.Second + ":" + myStopwatch.MilliSecond;
    timer3.Stop();
}

리셋 버튼을 누르면 프로그램을 다시 실행한 것처럼 동작시킵니다.


다음은 기록 버튼의 이벤트를 구현한 코드입니다.


private void Record_Click(object sender, EventArgs e)
{
    listBox1.Items.Add(label3.Text);
}

기록 버튼은 생각보다 정말 간단하게 만들 수 있습니다. 단지 Label3에 떠있는 텍스트를 리스트박스에 추가해 주면 됩니다.


이제 타이머 동작만 만들면 끝입니다.


private void timer3_Tick(object sender, EventArgs e)
{
    myStopwatch.MilliSecond++;
    if (myStopwatch.MilliSecond > 59)
    {
        myStopwatch.MilliSecond = 0;
        myStopwatch.Second++;
        if (myStopwatch.Second > 59)
        {
            myStopwatch.Second = 0;
            myStopwatch.Minute++;
        }
    }
    label3.Text = myStopwatch.Minute + ":" + myStopwatch.Second + ":" + myStopwatch.MilliSecond;
}

스톱워치는 타이머와 정 반대로 동작을 구현하면 됩니다. 스톱워치는 타이머와 달리 따로 종료 조건이 없습니다. 사용자가 멈출때까지 무기한으로 진행됩니다.


안드로이드 AOSP의 기본 시계를 살펴보면 밀리초가 99까지 증가하지만 위 밀리초를 99로 설정하면 시간이 점점 지연되는 오류가 있었습니다. 따라서 59까지 증가하는 것으로 지정하였습니다.


이렇게 간단하게 C#을 통해서 시계를 구현해 보았습니다. 소스코드의 오류나 보완할 점은 댓글로 달아주시면 감사하겠습니다.


WRITTEN BY

배진오

하고싶은 건 다 하면서 사는게 목표
im@baejino.com

comments powered by Disqus