본문 바로가기

C#/이것이 C#이다

[ 이것이 C#이다 ] Chapter 20. WinForm으로 만드는 사용자 인터페이스

  1. C# 코드로 WinForm 윈도우 만들기
  2. Application 클래스
  3. 윈도우를 표현하는 Form 클래스
  4. 폼 디자이너를 이용한 WinForm UI 구성
  5. 사용자 인터페이스와 비동기 작업

[ C#코드로 WinForm 윈도우 만들기 ]

[ 개요 ]

- WinForm에서 제공하는 폼 디자이너 툴은 프로그래머가 그림을 그리듯 사용자 인터페이스를 만들게 한다 .

- 위즈윅 ( What You See Is What You Get )방식의 개발을 지원한다 .

-  비주얼 스튜디오의 도움 없이 C# 코드만으로 GUI를 구성할 수도 있다 .


[ WinForm 클래스를 이용한 윈도우 생성 절차 ]

  1. System.Windows.Forms.Form 클래스에서 파생된 윈도우 폼 클래스를 선언한다 .
  2. 클래스의 인스턴스를 System.Windows.Forms.Application.Run()메소드에 인수로 넘겨 호출한다 .

- 아래과정을 통해 윈도우를 생성해보자 .

- 콘솔앱 템플릿으로 새 프로젝트 생성후 최상위문 사용 안함을 체크한다 

- 솔루션 탐색기의 SimpleWindow 더블클릭 .csproj를 코드 편집기로 불러들인다 .

- <TargetFramework>net8.0-windows</TargetFramework>으로 변경

- <UseWindowsForms>true</UseWindowsForms> 추가

- <ImplicitUsings>enable</ImplicitUsings> 제거로 전역 using의 암시적 전체사용을 해제 .

- MainApp으로 명칭 변경후 아래의 코드로 컴파일해서 실행

namespace SimpleWindow
{
	//MainApp이 System.Windows.Forms.From 클래스로부터 상속받도록 선언한다
    class MainApp:System.Windows.Forms.Form
    {
        static void Main(string[] args)
        {
			//Application.Run() 메소드에 MainApp의 인스턴스를 인수로 넘겨 호출
            System.Windows.Forms.Application.Run(new MainApp());
        }
    }
}

- 다음과 같이 쉽게 윈도우를 생성한다 .

- System.Window.Forms.Form  클래스 / System.Windows.Forms.Appliciation 두 클래스에 대해 알아보자 .


[ Application 클래스 ]

using System;
using System.Windows.Forms;

namespace SimpleWindow
{
    class MainApp:Form
    {
        static void Main(string[] args)
        {
            MainApp form = new MainApp();
            //from 클래스는 여러 이벤트 정의 , click 이벤트는 윈도우 클릭시 발생
            form.Click += new EventHandler(
                (sendler, eventArgs) =>
                {
                    Console.WriteLine("Closing Windows");
                    Application.Exit();
                });

            Console.WriteLine("Starting Window Application");
            Application.Run(form);

            Console.WriteLine("Exiting Window Application");
        }
    }
}

- Application 클래스는 1 . 윈도우 응용 프로그램의 시작과 종료 메서드 제공 / 2 . 윈도우 메시지를 처리하는 것 을 한다

- Exit() 메소드가 호출된다해서 응용 프로그램이 바로 종료되는게 아니다 .

- 메소드는 응용 프로그램이 갖고 있는 모든 윈도우 닫은뒤 Run() 메소드가 반환되게 하는 것이다 .

- Run 메소드 뒤에 자원을 정리하는 코드가 있다면 우아하게 응용프로그램을 정리 할 수 있다 .


[ 메시지 필터링 ]

[ 메시지 ]

- 윈도우 기반의 응용 프로그램들은 갑자기 일어나는 이벤트에 반응,코드가 실행되는 이벤트 기반 방식으로 만들어진다 .

- 윈도우 프로그램이 받아들이는 이벤트는 마우스 클릭,키보드 입력등이 있다 .

- 이벤트들은 사용자가 직접 응용 프로그램에 대해 일으킨것 같지만 , 윈도우 OS 가 일으킨 것이다 .

- 마우스등의 하드웨어 제어 - 인터럽트 발생 - 윈도우 OS 가 받음 - 인터럽트 바탕,윈도우 메시지 만듬 - 응용프로그램받음


[ 메시지 필터링 ]

- 윈도우 응용 프로그램은 마우스 이동등 시스템에 미리 정의된 메시지를 받는다 .

- 또한 다른 응용 프로그램이 정의한 많은 메시지도 받을 수 있다 .

- Application 클래스는 응용프로그램이 받는 메시지 중 관심있는 메시지만 거르는 메시지 필터링 기능이 있다 .


[ 메시지 필터링 과정 ]

- 윈도우 OS 에서 정의하는 메시지는 식별번호 (ID)가 붙어있다 . ex)WM_LBUTTONDOWN - 0x201

- Application 클래스는 특정 ID를 가진 메시지를 걸러내는 필터를 함께 등록해뒀다가 응용 프로그램에 메시지가 전달되면 해당 필터를 동작시킨다 .

- 메시지의 ID 가 필터가 관심있는 값이라면 필터는 메시지를 요리 / 아니라면 메시지를 거르지 않고 메시지를 받아야 하는 폼/ 컨트롤로 보내서 이벤트를 발생시킨다 .


[ Application.AddMessageFilter ]

public class MessageFilter : IMessageFilter
{
	public bool PreFilterMessage(ref Message m)
    {
    	if(m.MSG >= 0x200 && m.Msg <= 0x20E)
        {
        	Console.WriteLine("발생한 메시지 : " + m.Msg);
            return true;
        }
        return false;
    }
}

- Application.AddMessageFileter()메소드는 응용 프로그램에 메시지 필터를 설치한다

- 해당 메소드는 IMessageFilter 인터페이스를 구현하는 파생 클래스의 인스턴스를 인수로 받는다 .

- IMessageFilter는 PreFilterMessage() 메소드를 구현할 것을 요구한다 .

- 위 코드는  입력받은 메시지를 처리했으니 응용 프로그램은 관심 가질 필요가 없다는 의미로 True를 반환

- 메시지를 건들지 않았으니 응용프로그램에 처리하라하는 false를 반환한다 .

- 매개변수로 받아지는 Message 구조체는 다음과 같은 프로퍼티를 가지고 있다 .

프로퍼티 설명
HWnd 메시지를 받는 윈도우의 핸들 . 윈도우의 인스턴스를 식별 , 관리를 위해 운영체제가 붙인 번호가 핸들 .
Msg 메시지 ID
LParam 메시지를 처리하는데 필요한 정보가 담김
WParam 메시지를 처리하는데 필요한 부가정보가 담김
Result 메시지 처리에 대한 응답으로 윈도우 OS에 반환되는 값을 지정한다 .

 

using System;
using System.Windows.Forms;

namespace SimpleWindow
{
    class MessageFilger : IMessageFilter
    {
        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == 0x0F || m.Msg == 0xA0 ||
                m.Msg == 0x200 || m.Msg == 0x113)
                return false;

            Console.WriteLine($"{m.ToString()} : {m.Msg}");

            if(m.Msg==0x201)
                Application.Exit();
            return true;    
        }
    }
    class MainApp:Form
    {
        static void Main(string[] args)
        {
            Application.AddMessageFilter(new MessageFilger());
            Application.Run(new MainApp());
        }
    }
}

- WM_PAINT(0x0f) / WM_MOUSEMOVE(0x200) / WM_TIMER(0x113)을 제외하고  모든 메시지를 출력한다 .

- WM_LBUTTONDOWN(0x201)메시지가 도착하면 종료한다 .

- 이 메시지들은 WinForm의 각 윈도우와 컨트롤에 전달 , 윈도우와 컨트롤들은 이 메시지를 받으면 미리 정의된 이벤트 발생 , 각 이벤트는 프로그래머가 등록한 이벤트 처리기를 호출한다 .


[ 윈도우를 표현하는 From 클래스 ]

[ 작업 개요 ]

- Form(과 컨트롤)에 정의된 이벤트와 이벤트 처리기 연결하기

- Form의 프로퍼티를 조절하여 윈도우 모양 바꾸기

- Form 위에 컨트롤 올리기


[ Form에 정의된 이벤트와 이벤트 처리기 연결하기 ]

- Form 클래스는 운영체제가 보내는 메시지 중 일부에 대해 이벤트를 구현하고 있다 .

- ex ) Form의 인스턴스 , 즉 윈도우 위에서 마우스 좌클릭시 WM_BUTTONDOWN 메시지가 Form  객체로 전달,객체는 MouseDown 이벤트를 발생시킨다 .

- WinForm 으로 응용 프로그램을 만드는 동안 윈도우 메시지를 직접 요리할 경우는 거의 없다.

- Form을 비롯한 WinForm의 윈도우와 컨트롤 클래스들이 윈도우 메시지를 포장하여 이벤트로 구현했기 때문이다 .

- 미리 정의된 이벤트에 이벤트 처리기 메소드를 선언 , 등록하기만 하면 된다 .

class MyForm : Form
{
	//이벤트 처리기 선언
    private void Form_MouseDown(object sender,System.Windows.Forms.MouseEventArgs e)
    {
    	MessageBox.Show("안녕하세요!");
    }
    public MyForm
    {
    	this.MouseDown+=new System.Windows.Forms.MouseEventHandler(this.Form_MouseDown)
    }
}

- 이벤트 처리기를 등록 , 호출하는 메커니즘은 모든 폼과 컨트롤이 동일하다 .

//이벤트는 대리자 기반으로 선언 
public event MouseEventHandler MouseDown;
//대리자
public delegate void MouseEventHandler(objetc sender,MouseEventArgs e);

- 이 선언코드로 이벤트 처리기가 어떤 매개변수를 가지고 어떤 형식을 반환하는지 알 수 있다 .

- Sender 는 발생한 객체 . Form 클래스의 이벤트 처리기에 대해 알아보니 sender는 Form 객체 자신이다 .

- MouseEventArgs는 다음의 프로퍼티를 제공하여 마우스 이벤트의 상세 정보를 제공한다 .

프로퍼티 설명
Button 마우스의 어떤 버튼에서 이벤트가 발생했는지 나타낸다 .
Clicks 마우스의 버튼을 클릭한 횟수 . 
Delta 마우스 휠의 회전 방향과 회전한 거리
X 마우스 이벤트가 발생한 폼 또는 컨트롤 상의 x 좌표
Y 마우스 이벤트가 발생한 폼 또는 컨트롤 상의 y 좌표

 

using System;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

namespace SimpleWindow
{
    
    class MainApp:Form
    {
        public void MyMouseHandler(object sender, MouseEventArgs e)
        {
            Console.WriteLine($"Sender : {((Form)sender).Text}");
            Console.WriteLine($"X : {e.X} Y : {e.Y}");
            Console.WriteLine($"Button { e.Button} , Clicks { e.Clicks} ");
            Console.WriteLine();
        }
        public MainApp(string title)
        {
            this.Text = title;
            this.MouseDown += new MouseEventHandler(MyMouseHandler);
        }
        static void Main(string[] args)
        {
            
            Application.Run(new MainApp("Mouse Event Test"));
        }
    }
}

 


[ Form프로퍼티를 조절 , 윈도우 모양 바꾸기 ]

- Form 클래스는 윈도우 모양을 결정짓는 크기 , 배경색 , 전경색 , 투명도 ,제목 ,폰트 등 여러 프로퍼티를 가지고 있다 .

종류 프로퍼티 설명
크기 Width 창의 너비
Height 창의 높이
색깔 BackColor 창의 배경 색깔
BackgroundImage 창의 배경 이미지
Opacity 창의 투명도
스타일 MaximizeBox 최대화 버튼 설치 여부
MinimizeBox 최소화 버튼 설치 여부
Text 창의 제

 

using System;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

namespace SimpleWindow
{
    
    class MainApp:Form
    {

        static void Main(string[] args)
        {
            MainApp form = new MainApp();
            form.Width = 300;
            form.Height = 200;

            form.MouseDown += new MouseEventHandler(form_MouseDown);

            Application.Run(form);
        }
        static void form_MouseDown(object sender, MouseEventArgs e)
        {
            Form form = (Form)sender;
            int oldWidth = form.Width;  
            int oldHeight = form.Height;

            if(e.Button == MouseButtons.Left)
            {
                if(oldWidth<oldHeight)
                {
                    form.Height = oldWidth;
                    form.Width = oldHeight;
                }
            }
            else if(e.Button == MouseButtons.Right)
            {
                if(oldHeight<oldWidth)
                {
                    form.Height = oldWidth;
                    form.Width = oldHeight;
                }
            }
            Console.WriteLine("윈도우의 크기가 변경");
            Console.WriteLine($"Width : {form.Width}/Heigjt : {form.Height}");
        }
    }
}

 

//창의 배경색은 BackColor 프로퍼티를 통해 바꿀 수 있다 .
//System.Drawing.Color 형식이기에 Color 클래스의 정적 메소드 / 미리 정의된 상숫값 이용

Form form = new Form();
//Color 구조체에는 다양한 색상이 미리 정의
form.BackColor = Color.Red;
//정확한 값의 지정
form.BackColor = Color.FromArgb(255,255,0,0);

- 창의 배경색은 BackColor 프로퍼티를 통해 바꿀 수 있다 .

Form form = new Form();
form.Opacity = 1.00;
form.Opacity = 0.87

- 창의 투명도는 Opacity 프로퍼티를 통해 바꿀 수 있다 .

Form form = new Form()'
form.BackgroundImage = Image.FromFile("MyImage.JPG");

- 창의 배경 이미지는 BackgroundImage 프로퍼티에 Image 형식의 인스턴스를 할당하여 바꾼다 .

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

namespace SimpleWindow
{
    
    class MainApp:Form
    {
        Random rand;
        public MainApp()
        {
            rand = new Random();
            this.MouseWheel += new MouseEventHandler(MainApp_MouseWheel);
            this.MouseDown += new MouseEventHandler(MainApp_MouseDown);
        }
        void MainApp_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
            {
                Color oldColor = this.BackColor;
                this.BackColor=Color.FromArgb(rand.Next(0,255),
                    rand.Next(0,255),
                    rand.Next(0,255));
            }
            else if(e.Button == MouseButtons.Right)
            {
                if(this.BackgroundImage!=null)
                {
                    this.BackgroundImage = null;
                    return;
                }
                string file = "Sample.png";
                if (System.IO.File.Exists(file) == false)
                    MessageBox.Show("이미지 파일이 없습니다");
                else
                    this.BackgroundImage=Image.FromFile(file);

            }
        }
        void MainApp_MouseWheel(object sender,MouseEventArgs e)
        {
            this.Opacity = this.Opacity + (e.Delta > 0 ? 0.1 : -0.1);
            Console.WriteLine($"Opacity : {this.Opacity}");
        }
        static void Main(string[] args)
        {
            Application.Run(new MainApp());
        }
       
    }
}

- 위 예제는 마우스 왼쪽 버튼을 누르면 렌덤하게 창의 배경색을 바꾸고 , 오른쪽 버튼을 누르면 배경 이미지 표시 , 휠을 굴리면 투명도를 바꿔준다 .

- 이때 , 실행파일 위치에 이미지가 있어야 한다 .


[ Form프로퍼티를 조절 , 최대 / 최소화 버튼 활성과 제목 바꾸기 ]

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

namespace SimpleWindow
{
    
    class MainApp:Form
    {

        static void Main(string[] args)
        {
            MainApp form = new MainApp();

            form.Width = 400;
            form.MouseDown += new MouseEventHandler(form_MouseDown);
            Application.Run(form);
        }
       static void form_MouseDown(object sender, MouseEventArgs e)
        {
            Form form = (Form)sender;
            if(e.Button==MouseButtons.Left)
            {
                form.MaximizeBox = true;
                form.MinimizeBox = true;
                form.Text = "최소화/최대화 버튼이 활성화";
            }
            else if(e.Button==MouseButtons.Right)
            {
                form.MaximizeBox = false;
                form.MinimizeBox = false;
                form.Text = "최소화/최대화 버튼이 비활성화";
            }
        }
    }
}

- 좌클릭시 최소/최대 버튼이 활성화되고 우클릭시 최소/최대 버튼이 비활성화 된다 .


[ Form 위에 컨트롤 올리기 ]

[ 컨트롤 ]

- 컨트롤은 윈도우 OS 가 제공하는 UI 요소 . (응용 프로그램을 제어하는데 사용하는 도구) ex ) 버튼/텍스트 박스

- 윈도우 OS 는 UI를 위해 메뉴 , 콤보박스 , 리스트뷰 , 버튼 , 텍스트 박스등의 표준 컨트롤을 제공한다 .

  1. 컨트롤의 인스턴스 생성
  2. 컨트롤의 프로퍼티에 값 지정
  3. 컨트롤의 이벤트에 이벤트 처리기 등록
  4. 폼에 컨트롤 추가

위의 과정을 거쳐 Form 위에 컨트롤을 올린다 .


[ 1 . 컨트롤의 인스턴스 생성 ]

- WinForm의 모든 컨트롤은 System.Windows.Forms.Control 을 상속한다 .

- 해당 형식이 모든 윈도우 컨트롤이 지원해야 하는 그래픽,동작,이벤트를 제공하기에 이 컨트롤로부터 상속받는 어떤 클래스라도 Form 위에 올려서 윈도우 UI 요소로 사용할 수 있다 .

- 버튼은 다음과 같이 인스턴스를 생성한다 .

Button button = new Button();

[ 2 . 컨트롤의 프로퍼티에 값 지정 ]

- 인스턴스를 만들었으면 각 프로퍼티에 값을 지정 , 컨트롤의 모양을 결정한다 .

button.Text = "Click ME";
button.Left = 100;
button.Top = 50;

[ 3 . 컨트롤의 이벤트에 이벤트 처리기 등록 ]

- 컨트롤은 애플리케이션의 정보를 표시하기도 하지만 , 사용자로부터 입력을 받는 창구이기도 하다 .

- 사용자가 버튼 클릭시 메시지 박스를 띄우도록 이벤트 처리기 선언후 이벤트에 등록한다 .

button.Click+=
(object sender,EventArgs e)=>
{
	Message.ShowBox("딸깍!");
}

[ 4 . 폼에 컨트롤 추가 ]

- Form의 인스턴스를 생성 , Contorls 프로퍼티의 Add 메서드로 button 객체를 Form에 올린다 .

MainApp form = new MainApp();
form.Controls.Add(button);

[ 실습 ]

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

namespace SimpleWindow
{
    
    class MainApp:Form
    {

        static void Main(string[] args)
        {
            Button button = new Button();
            button.Text = "Click Me!";
            button.Left = 100;
            button.Top = 50;
            button.Click +=
                (object sender, EventArgs e) =>
                {
                    MessageBox.Show("딸깍");
                };

            MainApp form = new MainApp();
            form.Text = "Form & Control";
            form.Height = 150;
            form.Controls.Add(button);
            Application.Run(form);
        }

    }
}


[ 폼 디자이너를 이용한 WinForm UI 구성 ]

[ 폼 디자이너 ]

- 비주얼 스튜디오 IDE의 일부로 앞서 코드를 통해 해왔던 작업이 마우스 클릭만으로 가능하다 .

- 폼 디자이너는 도구상자 (컨트롤 팔레트)와 함께 사용해야 한다 .


[ 새 프로젝트 만들기 ]

- Windows Forms 앱을 생성해준다 .

- Form1.cs를 MainForm.cs으로 명칭을 변경해준다 .


[ Form ]

- 속성창에서 Text를 변경해준다 .


[ Group Box / Label / Combo Box / Check Box / Text Box ]

 

- 위와 같이 컨트롤을 배치한다 .

- 각 컨트롤에 이벤트에 대한 처리기를 등록한다 .MainForm/cboFont / chkBold/chkItalic 에 아래의 이벤트 처리기를 등록 .

namespace UsingControls
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
        void MainForm_Load(object sender, EventArgs e)
        {
            var Fonts = FontFamily.Families;//운영 체제에 설치되어 있는 폰트 목록 검색
            foreach (FontFamily font in Fonts)
                cboFont.Items.Add(font.Name);

        }
        void ChangeFont()
        {
            //cboFont에서 선택한 항목이 없으면 메소드 종료
            if (cboFont.SelectedIndex < 0)
                return;
            FontStyle style = FontStyle.Regular;//FontStyle 객체를 초기화
            if(chkBold.Checked )
                style|= FontStyle.Bold;
            if( chkItalic.Checked )
                style|= FontStyle.Italic;

            txtSampleText.Font = new Font((string)cboFont.SelectedItem, 10, style);
        }
        void cboFont_SelectedIndexChanged(object sender, EventArgs e) { ChangeFont(); }
        void chkBold_CheckedChanged(object sender, EventArgs e) { ChangeFont(); }
        void chkItalic_CheckedChanged(object sender, EventArgs e) { ChangeFont(); }




    }
}

- f5를 눌러 테스트를 해본다 .


[ Track Bar / ProgrssBar ]

- 위와 같이 컨트롤을 배치한다 .

void tbDummy_Scroll(object sender, EventArgs e)
{
    pgDummy.Value=tbDummy.Value;
}

- tbDummy에 다음의 이벤트 처리기를 등록한다 .


[ Button / Form / Dialog ]

- 위와 같이 컨트롤을 배치한다 .

void btnModal_Click(object sender, EventArgs e)
{
    Form frm=new Form();
    frm.Text = "Modal Form";
    frm.Width = 300;
    frm.Height = 100;
    frm.BackColor = Color.Red;
    frm.ShowDialog();//Modal  창을 띄움
}
void btnModaless_Click(object sender, EventArgs e)
{
    Form frm = new Form();
    frm.Text = "Modaless Form";
    frm.Width = 300;
    frm.Height = 300;
    frm.BackColor = Color.Green;
    frm.Show();//Modaless  창을 띄움
}
void btnMsgBox_Click(object sender, EventArgs e)
{
    MessageBox.Show(txtSampleText.Text, "MessageBox Text", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

- 다음의 이벤트 처리기를 등록한다 .


[ TreeView / List View ]

- 위와 같이 컨트롤을 배치한다 .

Random random = new Random(37);

- 난수 생성을 위해 MainForm.cs 에 추가

void TreeToList()
{
    lvDummy.Items.Clear();
    foreach (TreeNode node in tvDummy.Nodes)
        TreeToList(node);
}
void TreeToList(TreeNode Node)
{
    lvDummy.Items.Add(
        new ListViewItem(
            //TreeNode 형식의 FullPath 프로퍼티는 루트 노드부터 현재 노드까지의 경로를 나타냄
            //각 경로는 \로 구분
            new string[] { Node.Text, Node.FullPath.Count(f => f == '\\').ToString() }));
    foreach (TreeNode node in Node.Nodes)
        TreeToList(node);
}
void btnAddRoot_Click(object sender, EventArgs e)
{
    tvDummy.Nodes.Add(random.Next().ToString());
    TreeToList();
}
void btnAddChild_Click(object sender, EventArgs e)
{
    if (tvDummy.SelectedNode == null)
    {
        MessageBox.Show("선택된 노드가 없습니다 .","TreeView List", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    tvDummy.SelectedNode.Nodes.Add(random.Next().ToString());
    tvDummy.SelectedNode.Expand();
    TreeToList();
}

- 다음의 이벤트 처리기를 등록한다


[ 사용자 인터페이스와 비동기 작업 ]

[ 폼 디자이너 ]

- 위와 같이 설정한다 

using System.Security.Cryptography;

namespace AsyncFileIOWinForm
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
        #region CopyMethod
        async Task<long> CopyAsync(string FromPath, string ToPath)
        {
            btnSyncCopy.Enabled = false;
            long totalCopied = 0;

            using(FileStream fromStream=new FileStream(FromPath,FileMode.Open))
            {
                using (FileStream toStream = new FileStream(ToPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int nRead = 0;
                    while ((nRead = await fromStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    {
                        await toStream.WriteAsync(buffer,0,nRead);
                        totalCopied += nRead;

                        pbCopy.Value=(int)(((double)totalCopied/(double)fromStream.Length)*pbCopy.Maximum);
                    }
                }
            }    
            btnSyncCopy.Enabled = true;
            return totalCopied;
        }
       long CopySync(string FromPath, string ToPath)
        {
            btnSyncCopy.Enabled = false;
            long totalCopied = 0;

            using (FileStream fromStream = new FileStream(FromPath, FileMode.Open))
            {
                using (FileStream toStream = new FileStream(ToPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int nRead = 0;
                    while ((nRead = fromStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        toStream.Write(buffer, 0, nRead);
                        totalCopied += nRead;

                        pbCopy.Value = (int)(((double)totalCopied / (double)fromStream.Length) * pbCopy.Maximum);
                    }
                }
            }
            btnSyncCopy.Enabled = true;
            return totalCopied;
       }
        #endregion

        void btnFindSource_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            if(dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtSource.Text = dlg.FileName;
            }
        }
        void btnFindTarget_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtTarget.Text = dlg.FileName;
            }
        }
        async void btnAsyncCopy_Click(object sender, EventArgs e)
        {
            long totalCopied = await CopyAsync(txtSource.Text,txtTarget.Text);
        }
        void btnSyncCopy_Click(Object sender, EventArgs e)
        {
            long totalCopied=CopySync(txtSource.Text,txtTarget.Text);
        }
        void btnCancel_Click(object sender, EventArgs e)
        {
            MessageBox.Show("UI 반응 테스트 성공");
        }


    }
}
using System.Security.Cryptography;

namespace AsyncFileIOWinForm
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
        #region CopyMethod
        async Task<long> CopyAsync(string FromPath, string ToPath)
        {
            btnSyncCopy.Enabled = false;
            long totalCopied = 0;

            using(FileStream fromStream=new FileStream(FromPath,FileMode.Open))
            {
                using (FileStream toStream = new FileStream(ToPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int nRead = 0;
                    while ((nRead = await fromStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    {
                        await toStream.WriteAsync(buffer,0,nRead);
                        totalCopied += nRead;

                        pbCopy.Value=(int)(((double)totalCopied/(double)fromStream.Length)*pbCopy.Maximum);
                    }
                }
            }    
            btnSyncCopy.Enabled = true;
            return totalCopied;
        }
       long CopySync(string FromPath, string ToPath)
        {
            btnSyncCopy.Enabled = false;
            long totalCopied = 0;

            using (FileStream fromStream = new FileStream(FromPath, FileMode.Open))
            {
                using (FileStream toStream = new FileStream(ToPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int nRead = 0;
                    while ((nRead = fromStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        toStream.Write(buffer, 0, nRead);
                        totalCopied += nRead;

                        pbCopy.Value = (int)(((double)totalCopied / (double)fromStream.Length) * pbCopy.Maximum);
                    }
                }
            }
            btnSyncCopy.Enabled = true;
            return totalCopied;
       }
        #endregion

        void btnFindSource_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            if(dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtSource.Text = dlg.FileName;
            }
        }
        void btnFindTarget_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtTarget.Text = dlg.FileName;
            }
        }
        async void btnAsyncCopy_Click(object sender, EventArgs e)
        {
            long totalCopied = await CopyAsync(txtSource.Text,txtTarget.Text);
        }
        void btnSyncCopy_Click(Object sender, EventArgs e)
        {
            long totalCopied=CopySync(txtSource.Text,txtTarget.Text);
        }
        void btnCancel_Click(object sender, EventArgs e)
        {
            MessageBox.Show("UI 반응 테스트 성공");
        }


    }
}