Study - Programming/C#2008. 11. 16. 14:46

※이 글은 본인의 지극히 얄팍한 지식과 경험을 통해서 생각한 주관적인 글입니다. 이 포스팅의 기술적인 내용은 본인의 지식에서 나온 것이라 실제와 다를 가능성도 있습니다. 이에 대한 수정과 비판은 환영이나 비난은 자제해 주시면 감사하겠습니다.


  내가 처음 프로그래밍이란 것을 접한건 초등학교 3학년때 Visual Basic 5.0을 통해서였다. 일반적으로 프로그래밍을 배울 때는 C같은 언어를 통해서 변수,루프,함수 같은 기본 문법을 배우고 거기서 세세한 사항 및 기술 등을 배우고 다른 언어로 넘어가던지 윈도우 프로그래밍 같은걸 배우던지 할텐데, 난 바로 윈도우 프로그래밍부터 시작하다 보니 기본적인 문법 같은것도 대충 대충배우고 그랬었다.(C를 조금 하긴 했었지만 그때도 포인터나 구조체같은건 신경도 안썼다. 결정적으로 그런 개념들에 대한 중요성을 인식하지 못했었다. 독학이다 보니.) 실제로 함수 등의 문법 사항을 이해하기 시작한건 본격적으로 C를 공부하기 시작한 대학 1학년때 미친듯이 프로그래밍 공부 할 때였다.
  아무튼 이런식으로 약간 역순(말은 이렇지만 역순이라고는 생각하지 않는다. 대세가 위의 방식이지 내 방식이 정석이 아니다 뭐다 그런건 없다는 것이 본인의 생각이다.)으로 배우다 보니 편리한 RAD방식에서 딱딱한 콘솔 기반의 프로그래밍 그리고 거기서 윈도우 프로그래밍으로 넘어갈 때는 정말 힘들었었다. 그리고 좀더 편리한 방식이 없나 종종 생각하기도 했다. VB의 편리함은 충분히 좋은 장점이지만 그에 비해 언어적 약점이 많이 아쉬웠다. 그리고 C나 C++, JAVA같은 언어는 강력한 언어적 지원과 갈수록 좋은 개발환경을 제공해 주었지만 역시 VB의 편리함과 비교해서는 아쉬운 점이 많았다.
  그러다 접한 것이 C#이다. C#은 위 두가지의 장단점을 잘 조합한 좋은 언어라고 생각한다. 엄청나게 강력한 언어적 기능 그리고 윈폼을 활용한 강력한 개발 환경. 분명 기존 언어들과 비교해서 한발자국 나가긴 나간 언어이다. 예전에 RAD툴 관련 글들을 보면서 현재는 VB 등의 RAD툴이 강력한 생산성에도 불구하고 부실한 언어적 기능 때문에 빛을 못받고 있지만 갈수록 RAD툴은 대세가 될 것이라는 글을 많이 봤다. C#은 이 말이 실현되고 있음을 보여 주는 한가지 예라고 할 수 있다. 
  그러나 이 C#도 치명적인 약점이 있다. 바로 .NET Framework라는 것. 조그마한 프로그램을 만들었는데도 20MB 가까이 되는 .NET Framework 을 설치해야 한다는 것과 이것이 현재 닷넷의 큰 약점 중의 하나라는 것은 잘 알려진 사실이다.  .NET Framework위에서 돌아가는 방식은 분명 장점도 있겠지만 현재는 이 배보다 배꼽이 큰 문제가 심각할 수밖에 없다. Windows XP가 보급되기 전에 이 .NET Framework가 좀더 기반을 다지고 Windows XP에 .NET Framework가 함께 나왔다면 어떻게 되었을지 모르겠지만 현재로서는 아무리 Windows Vista에 이것을 넣고 Windows Update에 추가하고 하더라도 충분히 보급되기까지는 상당한 시간이 걸릴 것이다.
  그리고 Microsoft에서 만들다 보니 강력한 기능에도 불구하고 .NET Framework가 Windows기반에서만 돌아갈 수 있는 불상사(?)가 발생하여 JAVA와 비교해 아직은 부족하다.(Mono가 있지만 이것이 완전한 .NET Framwork라고 할 수 있을까?)
  이런 C#의 약점을 가지고 있지 않은 다른 언어는 없을까? 최근에 2009버전이 나온 Delphi가 있겠다. 어떠한 프레임워크 위에서 돌아가지 않으면서도 엄청나게 편리한 개발 환경과 최근 2009버전에서도 나타나는 강력한 언어적 기능 그리고 C,C++같은 어느 특정한 곳에서 독점하지 않는 것이 아니라 한 기업에서 독자적으로 밀고 있는 방식이라 그 발전 가능성과 발전 속도도 상당히 높다고 할 수 있다. 그러나 이 Delphi도 왠지 모를 비인기(상대적인 이야기이다.)와 처음 접하는 개발자는 좀 거북할 수 있는 문법 그리고 Windows개발쪽에서만 나타나는 강력함에서 완전하다고는 이야기 할 수 없다.
  아직까지는 RAD툴이 개발 방식의 대세라고는 이야기 할 수 없겠다. 그러나 언젠가는 이런 방식이 주류가 될 것이라고 난 믿는다. 완전하지는 않지만 분명 변화는 일고 있다. 강력한 언어 위의 강력한 개발 환경. 그런 변화가 언제쯤이면 일어날까 기대하며 이 글을 마친다.
Posted by 머리
Study- MSC/Computer2008. 10. 24. 19:48
  이번 포스팅에서는 동적할당을 객체의 생성쪽으로 확장해 보고자 한다. 먼저 말해두지만 이 포스팅은 포인터나 객체, 클래스를 설명하기 위한 포스팅이 아니다. 어디까지나 이런 사항에 대한 기본적인 지식은 알고 있다고 가정하고 진행하겠다. 우선 C++에서 Example이라는 클래스가 있고, 그것에 대한 객체를 생성해 보자.

Example ex;

  자. ex라는 객체가 생성되었다. 아마 특별한 문제 없이 제대로 돌아갈 것이다. 그리고 파괴도 잘 될 것이다. 하지만 이런 방식의 객체 생성은 권하고 싶지 않다. 대신 이런 객체의 생성을 권한다.

Example* pEx = new Example();

  위 방식은  Example라는 클래스 형식으로 메모리 공간에 할당을 한 후 그 주소를 pEx 포인터 변수에 넣어서 사용하게 하는 방식이다. 앞선 포스팅에서, 메모리의 어딘가에(HEAP) 공간을 할당한다고 했는데 위 방식도 바로 이런 방식이다. 이렇게 해 놓으면 이어서 이렇게도 가능하다

Example *pEx = new Example();
pEx = new Example();

  잘 이해가 가지 않을것이다. 무슨 이야기냐면 한 객체를 메모리에 생성하여 한 포인터에 주소를 기억시켰는데, 그 포인터를 재사용하여 또다른 객체를 또 생성하여 그 주소를 넣을 수 있다는 이야기이다. 물론 위 코드는 첫 객체의 해제에서 문제가 발생하지만 이런식으로 좀 더 코드에 유연성을 줄 수 있다. 그렇지 않으면 Example ex1,ex2; 등의 방식으로 객체를 생성해야 하는 문제가 발생한다.
  앞서 말한 유연성 외에도 이런 방식의 동적 할당을 통한 객체 생성은 맨 처음 말한 동적할당의 장점을 최대한 살릴 수가 있게 된다. 객체는 일반 변수보다 여기 저기서 사용될 가능성이 많다. 무슨 말이냐면 지역 변수 이상의 용도로 사용될 가능성이 많다는 이야기이다. 만약에 게임을 만들때 적을 표현하는 클래스가 있고 그 객체를 생성한다고 하면 상황에 따라 계속해서 적을 만들어 내야 하므로 동적 할당이 필요 할 수 밖에 없다.(int형으로 적을 표현할수는 없지 않은가?)
  이제까지는 객체를 동적할당 할 때 포인터를 이용하였다. 그 이유는 C++은 기본적으로 Call by Value 방식이기 때문이기 때문인데 이는 변수 및 객체의 사용을 한정적인 위치에서만 사용할 수 밖에 없게 만든다. 이 틀을 극복하기 위해서 메모리상의 주소로 읽기 위한 포인터가 사용되는데, 여기서 한단계 더 나가서 레퍼런스라는 개념이 나온다. 적어도 여기서는 Call by Reference와 Call by Address의 개념을 다르게 두자. Call by Address는 결론적으로는 포인터의 값을 이용한 Call by Value로 볼 수 있다. 엄밀하게 말하면 Call by Reference와는 다르다.(물론 내부적으로 이놈도 포인터를 쓰긴 쓴다.)
  말이 샜는데, C++ 이후의 객체 지향 언어는 기본 참조 방식을 Call by Value가 아니라 Call by Reference를 채택한 경우가 많다. JAVA가 그렇고, C#이 그러며, 심지어 Visual Basic도 Call by Reference가 기본이다.
  무슨 말이냐면 만약에

Example ex;

라는 코드가 있으면 C++에서는 ex가 객체 그 자체이지만 JAVA나 C#등에서는 저것은 그냥 참조를 위한 레퍼런스에 불과하지 객체 자체는 아니다. 즉 아무것도 못한다. 레퍼런스는 포인터처럼 메모리 상의 어떤 것을 가리켜야 그 의미가 있다. 즉

Example ex = new Example();

  와 같이 객체를 생성하고 그것을 참조하여야 한다.
  말이 많았다. 정작 하고 싶은 말은 '동적할당은 메모리 어딘가에 공간을 할당하고 포인터를 이용하여 그것을 참조한다, 그리고 이는 객체에서도 마찬가지이며 좀더 나아가 레퍼런스는 참조 없이는 아무런 의미가 없다'였는데 이것을 표현하기 위해 쓸데없는 말이 너무 많았던 것 같다.
  아무쪼록 이 글을 읽고 뭔가 '아!'하고 깨달음(?)을 얻는 프로그래머 지망생이 있으면 좋겠다. 그렇게 기원하면서 이상 3회에 걸친 포스팅을 마치고자 한다.
Posted by 머리
Project/Chatting2008. 1. 25. 23:05
Chatting 프로그램

개발자 : 양희철
개발 기간 : 2008.1.01~2008.1.19(미완)
개발 환경
- 개발 운영체제 : Windows XP Pro
- 개발 IDE : Visual Studio 2005
- 사용 언어 : C#(.NET Framework 3.0)

  아.. 군대 때문에 어쩔수없이 미완으로 남겨두는 프로젝트이다. 다중 사용자 채팅 프로그램으로, 서버 한 대에 여러 사람이 접속해서 서로 채팅을 한다.

  처음으로 만들어 보는 다자간 채팅 프로그램이라 많이 미흡하다. 일단 채팅 자체는 되는데, 방장의 권한 및 유저 리스트를 구현하지 못하였다.

invalid-file

채팅 프로그램 소스

Posted by 머리
Project/FileBackup2008. 1. 17. 00:29

File Backup 프로그램

개발자 : 양희철
개발 기간 : 2007.12.17~2007.12.28
개발 환경
- 개발 운영체제 : Windows XP Pro
- 개발 IDE : Visual Studio 2005
- 사용 언어 : C#(.NET Framework 3.0)

  예전에 작은아버지께서 한번 있었으면 좋겠다고 말씀하셨었는데, 처음엔 안하려다가 시험 기간 끝나고 방학 시작하면서 뭘 할까 하다가 급하게 공부하면서 만들어 보았다.

  기능은 간단하다. 한 개 또는 복수 개의 파일을 다른 드라이브로 그 경로까지 똑같이 복사해주는 프로그램이다.
  예를 들면 C:\My Doc\abc.txt 파일을 D:\에다가 복사하고 싶으면 D:\My Doc\에 abc.txt라는 파일이 똑같이 복사된다.

 

사용자 삽입 이미지

메인 화면


메인 화면이다. 기본적으로 탐색기와 비슷한 형태를 가지고 있다. 위의 창에서 폴더 혹은 파일을 선택한 후 오른쪽 버튼을 눌러 나온 팝업 메뉴에서 백업을 누르면 백업 화면이 나온다.

사용자 삽입 이미지

  메인 화면에서 폴더 몇 개를 선택하였고, 백업할 드라이브로 F드라이브를 선택하였다. 참고로 반드시 드라이브일 필요는 없다. 폴더도 루트로 가능하다.

사용자 삽입 이미지

  특정한 파일만을 검색해서 백업하는 것도 가능하다.
 
  폼의 UI는 책을 보고 따라한 것이 대부분이다. 가장 중요한 핵심은 FileSystem 클래스인데, 이 클래스가 파일의 검색 및 백업을 가능하게 해 준다.
사용자 삽입 이미지
CopyAll() : 한 폴더의 내용을 카피한다.
FileCopy() : 파일을 카피한다.
FindFIle() : 지정한 경로에서의 파일을 검색한다.
MakeDir() : 디렉토리를 하위 디렉토리까지 만든다.

  다음은 FileSystem 클래스의 소스이다.


invalid-file

프로젝트의 소스

Posted by 머리
Study - Programming/C#2007. 12. 25. 22:31

검색할 파일과 경로를 입력받아서 하위디렉토리까지 포함해서 경로를 검색하는 프로그램이다.

책에 있던 파일 찾기 예제를 조금 수정했다. 원래 예제는 하위 디렉토리는 검색하지 못해서 재귀를 이용해서 만들어 보았다.

사용자 삽입 이미지

다음은 가장 핵심인 FindFile() 메서드이다. 파라미터의 str은 찾을 파일(또는 형식)이고 dir은 경로이다.

        void FindFile(string str,string dir)
        {
            string tdir = dir.Trim();

            if (dir == "")
            {
                MessageBox.Show("검색할 디렉토리를 입력하세요");
                return;
            }

            string[] files_list;
            try
            {
                DirectoryInfo dinfo = new DirectoryInfo(tdir);
                DirectoryInfo[] subdir = dinfo.GetDirectories();


                files_list = Directory.GetFiles(tdir, str);

                for (int i = 0; i < files_list.Length; i++)
                {
                    ListViewItem item1 = new ListViewItem(files_list[i], 0);

                    FileInfo finfo = new FileInfo(files_list[i]);

                    item1.SubItems.Add(finfo.Length.ToString() + "Byte");
                    item1.SubItems.Add(finfo.CreationTime.ToString());

                    lst_View.Items.Add(item1);
                }

                foreach (DirectoryInfo d in subdir)
                {
                    tdir = d.FullName;
                    if((d.Attributes & FileAttributes.System) <= 0)
                    {
                        FindFile(str,tdir);

                        files_list = Directory.GetFiles(tdir, str);

                        for (int i = 0; i < files_list.Length; i++)
                        {
                            ListViewItem item1 = new ListViewItem(files_list[i], 0);

                            FileInfo finfo = new FileInfo(files_list[i]);

                            item1.SubItems.Add(finfo.Length.ToString() + "Byte");
                            item1.SubItems.Add(finfo.CreationTime.ToString());

                            lst_View.Items.Add(item1);
                        }
                    }

                }


            }
            catch(Exception e)
            {
               
                Console.WriteLine("파일 검색 중 예외 발생");
                Console.WriteLine(e.Message);
                Console.WriteLine(e.ToString());
                Console.WriteLine(tdir);
            }
        }

처음에 자꾸 예외가 발생해서 애를 많이 먹었다. 보니 System Volume Information부분에 권한도 없이 자꾸 접근해서 프로그램이 꺼지는 것이다. 그래서 데브피아에 알아봐서

if((d.Attributes & FileAttributes.System) <= 0)

이렇게 해서 System 관련 디렉토리는 접근하지 않게 해 놓았다.

FileSystem 클래스에 이놈도 넣어 놔야지.

Posted by 머리
Study - Programming/C#2007. 12. 25. 13:06

다음주에 작은아버지께 드릴 프로그램을 위해서 만들어 본 클래스이다.

백업 프로그램인데, 모든 파일을 경로와 파일명 그대로 다른 드라이브로 옮겨 주기 위해서 만들어 클래스들

그런데 이거 참 클래스 이름 짓기가 참 난감했다. 이걸 뭐라고 해줘야 하나..
그래서 그냥 어울리지는 않지만 FileSystem이라고 이름지엇다.

class FileSystem
{
    /*CopyAll : 특정 디렉토리의 모든 폴더와 파일을 다른 드라이브로 옮기는 메서드
     * args : 원본의 경로
     * drive : 옮길 드라이브
     */
    public void CopyAll(string args,string drive)
    {
        DirectoryInfo dinfo = new DirectoryInfo(args);
        string dest;
       
        if (dinfo.Exists)
        {
            DirectoryInfo[] dir = dinfo.GetDirectories();
           
            foreach (DirectoryInfo d in dir)
            {
                CopyAll(d.FullName,drive);
            }

            FileInfo []fs = dinfo.GetFiles();

            foreach (FileInfo f in fs)
            {
                dest = f.Directory.FullName;
                dest = dest.Remove(0, 1);
                dest = dest.Insert(0, drive);
                Console.WriteLine("Copy " + f.Name + " to " + dest);
                FileCopy(f.FullName, dest, 0);
            }
        }
    }
    /* MakeDir : 디렉토리를 만드는 메서드
     * dir : 만들 디렉토리의 경로
     */
    public void MakeDir(string dir)
    {
        DirectoryInfo dinfo = new DirectoryInfo(dir);

        if (dinfo.Exists == false)
        {
            dinfo.Create();
        }
    }


    /* FileCopy : 파일을 복사하는 함수
     * src : 복사할 파일의 경로 및 파일 이름
     * dest : 복사할 목적지
     * mode : 파일을 복사할것인지, 이동할것인지 설정
     */

    public void FileCopy(string src,string dest,int mode)
    {
        FileInfo finfo = new FileInfo(src);
        FileInfo fsrc;
        if (finfo.Exists == true)
        {
            MakeDir(dest);
            dest = dest + "\\" + finfo.Name;
            switch (mode)
            {
                case 0:
                      fsrc = finfo.CopyTo(dest, true);
                    break;
                case 1:
                    finfo.MoveTo(dest);
                    break;
            }
        }
    }  
}

Posted by 머리
Study - Programming/C#2007. 12. 22. 03:04

사용자 삽입 이미지

요즘 생각해보고 있는 프로그램중에 문자열 토큰 관련 기능이 필요해서 한번 책 예제를 따라해 봤다. 간단하다 텍스트 박스의 내용 중 #이랑 &을 가지고 토큰을 나눠주는 기능이다.

간단하니 버튼의 이벤트 핸들러만 소스를 올린다.

            string msg = txt_MSG.Text;
            string[] token = msg.Split('#');

            for (int i = 0; i < token.Length; i++)
            {
                if (token[i].IndexOf("&") > 0)
                {
                    txt_Info.AppendText("\r\n" + token[i]);
                    string[] subtoken = token[i].Split('&');
                    for (int j = 0; j < subtoken.Length; j++)
                    {
                        txt_Info.AppendText("\r\n=>" + subtoken[j]);
                    }
                }
                else
                {
                    txt_Info.AppendText("\r\n" + token[i]);
                }
            }

그리고 이것은 텍스트 박스 안의 내용

"S_S_FILE#검색서버IP#파일개수#파일이름&파일사이즈&파일생성일"

토큰으로 나누면 스크린샷과 같은 모습으로 출력된다.

기가막힌건.. C같았으면 토큰 저런거 저장할때 별 짓을 다해야 할텐데 여기서는
 string[] token = msg.Split('#');
이거 하나로 그냥 해결해 버렸다. 보고 기가 막혀서 웃음을 터뜨렸었다. 이렇게 간단하게 해결이 될 줄이야.. 역시 제공되는게 많다.

Posted by 머리
Study - Programming/C#2007. 12. 21. 13:52

처음으로 만들어본 C# 프로그램이다.

뭐 별건 없고 그냥 간단한 사칙 연산이 되는 계산기를 만들어 봤다.

일단 소스


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;


namespace Calculator
{
    class Program : Form
    {
        const int BTNSIZE = 40;
        const int CLRBTN = 10;
        const int ANSBTN = 11;

        const int BLK = 0;
        const int PLUS = 1;
        const int MIN = 2;
        const int MUL = 3;
        const int DIV = 4;

        int result = 0;
        int op = 0;

        bool answered = false;

        Button[] btn = new Button[16];
        Label lbl_display;

        int iButtonSize = 50;

        public Program()
        {
            int i = 0;
            int zero_x = 0;
            int zero_y = 0;
            char []name  = new char[4]{'+','-','*','/'};

            this.Text = "간단한 계산기";

            this.SetBounds(0, 0, BTNSIZE * 4 + 40, BTNSIZE * 5 + 50);
            lbl_display = new Label();
            lbl_display.Text = "0";
            lbl_display.BackColor = Color.White;
            lbl_display.SetBounds(15,10,160,20);


            this.Controls.Add(lbl_display);
            zero_y = 0;

            //'1~9'버튼

            for (i = 1; i <= 9; i++)
            {
                if (i % 3 == 1)
                {
                    zero_x = 15;
                    zero_y += BTNSIZE;
                }
                else
                {
                    zero_x += BTNSIZE;
                }
                    btn[i] = new Button();
                    btn[i].Text = i.ToString();
                    btn[i].SetBounds(zero_x, zero_y, BTNSIZE, BTNSIZE);
                    btn[i].Click += new EventHandler(BtnNumber_OnClick);
                this.Controls.Add(btn[i]);
            }

            //0버튼
            zero_x = 15;
            zero_y += BTNSIZE;
            btn[0] = new Button();
            btn[0].Text = "0";
            btn[0].SetBounds(zero_x, zero_y, BTNSIZE, BTNSIZE);
            btn[0].Click += new EventHandler(BtnNumber_OnClick);
            this.Controls.Add(btn[0]);

            //'Clear'버튼
            zero_x += BTNSIZE;
            btn[CLRBTN] = new Button();
            btn[CLRBTN].Text = "CLR";
            btn[CLRBTN].SetBounds(zero_x, zero_y, BTNSIZE, BTNSIZE);
            btn[CLRBTN].Click += new EventHandler(BtnCLR_OnClick);
            this.Controls.Add(btn[CLRBTN]);

            //'Answer'버튼
            zero_x += BTNSIZE;
            btn[ANSBTN] = new Button();
            btn[ANSBTN].Text = "=";
            btn[ANSBTN].SetBounds(zero_x, zero_y, BTNSIZE, BTNSIZE);
            btn[ANSBTN].Click += new EventHandler(BtnANS_OnClick);
            this.Controls.Add(btn[ANSBTN]);


            //연산자 버튼
            zero_x += BTNSIZE;
            zero_y = 0;
            for (i = 12; i < 16; i++)
            {
                zero_y += BTNSIZE;
                btn[i] = new Button();
                btn[i].Text = name[i - 12].ToString();
                btn[i].SetBounds(zero_x, zero_y, BTNSIZE, BTNSIZE);
                btn[i].Click += new EventHandler(BtnOp_OnClick);
                this.Controls.Add(btn[i]);
            }
           

        }
        private void BtnNumber_OnClick(object sender, EventArgs arg)
        {
            Button obj = (Button)sender;
            if (answered == true)
            {
                lbl_display.Text = "0";
                answered = false;
            }

            if (lbl_display.Text != "0")
            {
                lbl_display.Text += obj.Text;
            }
            else
            {
                lbl_display.Text = obj.Text;
            }

        }

        private void BtnCLR_OnClick(object sender, EventArgs arg)
        {
            result = 0;
            op = BLK;
            lbl_display.Text = "0";
        }

        private void BtnANS_OnClick(object sender, EventArgs arg)
        {
            switch (op)
            {
                case BLK:
                    return;
                    break;
                case PLUS:
                    result += Int32.Parse(lbl_display.Text);
                    break;
                case MIN:
                    result -= Int32.Parse(lbl_display.Text);
                    break;
                case MUL:
                    result *= Int32.Parse(lbl_display.Text);
                    break;
                case DIV:
                    if (result != 0)
                        result /= Int32.Parse(lbl_display.Text);
                    else
                    {
                        lbl_display.Text = "Error!";
                    }
                    break;

            }
            answered = true;
            lbl_display.Text = result.ToString();
        }
        private void BtnOp_OnClick(object sender, EventArgs arg)
        {
            Button obj = (Button)sender;
            switch (obj.Text)
            {
                case "+":
                    op = PLUS;
                    break;
                case "-":
                    op = MIN;
                    break;
                case "*":
                    op = MUL;
                    break;
                case "/":
                    op = DIV;
                    break;
            }
            result = Int32.Parse(lbl_display.Text);
            Console.WriteLine(op);
            lbl_display.Text = "0";
        }
        static void Main(string[] args)
        {
            Application.Run(new Program());
        }
    }
}

사용자 삽입 이미지


생각해 보니까 이벤트 관련 함수 안에 모든 내용을 다 넣어 버렸다. 그리 좋지 않은 방식인데.. 윈도우 프로그래밍쪽만 신경쓰다 보니 계산기의 클래스화도 제대로 신경 못쓰고 그러다보니 어떤 오류가 날지도 제대로 모르겠다. 다음에는 신경써서 만들어 보아야 겠다.

Posted by 머리
Study - Programming/C#2007. 12. 13. 11:26

using System;
using System.Windows.Forms;
using System.Drawing;


class Program : Form
{
    Button btn = null;
    ListBox lstbox = null;
    Image image = null;
    public Program()
    {
        this.Text = "Graphics 개체 얻기";
        btn = new Button();
        btn.Text = "버튼 위에 GDI+ 출력";
        btn.SetBounds(10, 10, 200, 100);
        btn.Click += new EventHandler(btn_Click);
        this.Controls.Add(btn);

        lstbox = new ListBox();
        lstbox.SetBounds(210, 110, 410, 310);
        lstbox.Items.Add("사과");
        lstbox.Items.Add("포도");
        lstbox.Items.Add("수박");
        lstbox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(lstbox_DrawItem);
        lstbox.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(lstbox_MeasureItem);

        this.Load += new EventHandler(On_Load);
        this.Controls.Add(lstbox);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics grfx = e.Graphics;
        if (image != null)
            grfx.DrawImage(image, 0, 0);
    }

    static void Main(string[] args)
    {
        Application.Run(new Program());
    }

    public void btn_Click(object sender, EventArgs e)
    {
        Graphics grfx = btn.CreateGraphics();
        grfx.FillRectangle(new SolidBrush(Color.Blue), btn.ClientRectangle);
        grfx.Dispose();

        Image imageFile = Image.FromFile("C:\\Documents and Settings\\All Users\\Documents\\My Pictures\\그림 샘플\\겨울.jpg");
        grfx =  Graphics.FromImage(imageFile);

        Font font = new Font("돋음",20);
        Brush brush = Brushes.Pink;

        grfx.DrawString("이미지에 글자 쓰기", font, brush, 10, 10);
        grfx.Dispose();

        imageFile.Save("sample.gif");
        this.image = Image.FromFile("sample.gif");
        this.Invalidate(this.ClientRectangle);
  
    }

    private void On_Load(object sender, EventArgs e)
    {
        lstbox.DrawMode = DrawMode.OwnerDrawFixed;
    }

    private void lstbox_MeasureItem(object sender, MeasureItemEventArgs e)
    {
        Graphics g = e.Graphics;
        Console.WriteLine("{0} : MeasureItem 이벤트 실행", e.ToString());
    }

    private void lstbox_DrawItem(object sender, DrawItemEventArgs e)
    {
        Graphics g = e.Graphics;
        Brush brush = Brushes.Black;

        switch(e.Index)
        {
            case 0:
                brush = Brushes.Red;
                break;
            case 1:
                brush = Brushes.Violet;
                break;
                case 2:
                brush = Brushes.Green;
                break;
        }

        g.DrawString(lstbox.Items[e.Index].ToString(), e.Font, brush, e.Bounds, StringFormat.GenericDefault);

        Console.WriteLine("{0} : DrawItem 이벤트 실행", e.ToString());
    }
}

소스가 좀 많다. 이외에도 몇가지가 있는데, 한 소스에 담기가 좀 번거로워서 한번에 담을 수 있는것만 해 놓았다.

주석이 없어서 소스 분석이 좀 어렵겠다. 주석 습관 들여야 하는데..

사용한 방법은 다음과 같다.

1. Control Class의 CreateGraphics메서드 이용
2. ListBox 등의 컨트롤에서 제공하는 MeasureItem이나 DrawItem 이벤트 등을 이용한 방법
3. Graphics.FromImage() 메서드 이용
4. OnPaint()메서드 override 해서 사용


이외에도 Paint이벤트 상속받기, PrintPage이벤트 핸들러,Win33API 사용하기 등의 방법이 있다.

Posted by 머리
Study - Programming/C#2007. 11. 26. 01:10

using System;
using System.Windows.Forms;

class Program : Form
{
    public Program(string strText)
    {
        this.Text = strText;
        this.Load += new System.EventHandler(this.Form_Load);
        this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form_Closed);
        this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form_Click);
        this.MouseEnter += new System.EventHandler(this.Form_MouseEnter);
        this.Show();
    }

    public static void Main(string[] args)
    {
        Application.Run(new Program("이벤트!"));
    }

    private void Form_Load(object sender, System.EventArgs e)
    {
        Console.WriteLine("윈도우가 Load됩니다.");
    }
    private void Form_Closed(object sender, System.EventArgs e)
    {
        Console.WriteLine("윈도우가 Closed됩니다.");
    }

    private void Form_Click(object sender, System.Windows.Forms.MouseEventArgs e)
    {
        Console.WriteLine(e.Button);
    }

    private void Form_MouseEnter(object sender, System.EventArgs e)
    {
        Console.WriteLine("Mouse Entered!!");
    }

   
}

  실제 실행 결과는 여기서 이벤트가 발생하면 콘솔에 그에 대한 내용이 출력된다. 대표적으로 Load와 Closed이벤트가 있겠다. MouseEnter은 마우스가 폼에 진입했을때 발생하는 이벤트인데, 상당히 흥미로운 이벤트였다. 직접 구현했으면 복잡했을 것을 이렇게 간단히 지원해주니 좋을 따름.
 
  Click이벤트를 보면 두번째 인자가 좀 다른것을 볼 수 있다. System.Windows.Forms.MouseEventArgs인자의 Button속성은 누른 마우스 버튼이 무엇인지를 알려 준다.

  각 이벤트마다 등록해줘야 할 이벤트 등록 클래스가 각각 다르다. 그러므로 중요한것은 따로 암기해 두고, 필요할때는 MSDN 등을 통해 검색해 보고 사용해야 할 것이다. 예를 들면 Load와 FormClosed이벤트의 등록 클래스의 형태는 다르다.

  이벤트의 등록 방법은 다음과 같다.

[이벤트 이름] +- new System.EventHandler(메서드);

  이벤트의 제거는 -=를 이용해 주면 되고, 메서드는 알아서 만들어서 넣어 주면 되지만 그 형태는 다음과 같다.

private void 메서드이름(object sender,System.EventArgs e){}

물론 EventArgs는 MouseClick이벤트를 보듯이 다를 수도 있지만 대개 저런 형태이다.

sender를 다룰때는 어떤 특정한 컨트롤일 경우에는 그 컨트롤로 캐스팅을 시켜주는 경우가 많다. 예를 들어 앞 글에서 버튼을 다룰때 이런식의 코드가 있었다.

(button)sender

이렇게 object를 button으로 캐스팅을 하고 사용해 줘야 한다.

Posted by 머리