PROGRAMMING WORKSHOP

.NetFrameWork | 마우스로 화면에 그림그리기

엑셀을 다루는 정보처리프로그래밍에 있어서
이런 것은 별로 흥미가 없을지 몰라도
(실은 엑셀 VBA를 할때도 도형등을 통하여 프로그래밍을 배우는것이 재미있다)
윈도우의 세상을 이것 저것 살펴보는 것이 많은 영감을 주고
다양한 개체를 접할 수 있는 기회가 된다
아래의 그림과 같이 화면에 마우스롤 움직이는 대로 선이 그려지는
도구를 하나 만들어 보자..



윈도우 폼을 하나 만들면 자동으로 코딩이 되는 부분이
아래와 같다
우선 윈도우의 New 이벤트프로시져에 아래와 같이 코드 한줄이 있다
InitializeComponent() 라는 프로시져가 호출되게 작성되어 있다
그리고 주석으로 이 프로시져는 Window Form Designer에서
필요한 프로시져라고 씌여있다
그러니 이것을 지웠다가는 윈도우가 뜨지못한다
여러분이 윈도우에 이것, 저것 마우스로 컨트롤을 끌어다가
놓고 위치를 잡으면..
여러분이 배치해 놓은 모든 컨트롤이 VB로 작성이 되는 것이다

이 말은 즉 프로그래머가 콘트롤을 끌어다 놓을 필요없이
그냥 VB로 작성해도 된다는 이야기다..

Public Sub New()

' This call is required by the Windows Form Designer.
    InitializeComponent()

' Add any initialization after the InitializeComponent() call.

End Sub


그리고 InitializeComponent()프로시져가 실행후
여려분이 또 초기화시킬 작업을 해도 된다는 주석이 자동작성되어 있다
그러면 최초의 InitializeComponent()프로시져를 열어 보면
(프로시져명으로 전체 소루션에서 찾기명령을 하면, Form Designer.VB라는 모듈)

Private components As System.ComponentModel.IContainer
Private Sub InitializeComponent()
    components = New System.ComponentModel.Container()
    Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
    Me.Text = "Form1"
    
    
    
    '''' 이곳에 자동으로 계속 작성된다
End Sub

와 같이 아무 콘트롤도 정의된 것이 없는 빈탕의
Window Form의 캡션과 폰트가 윈도우폼에 맞추어서 자동크기
조절이 되라는 정도가 작성 되어 있다
콘트롤을 폼에 갖다 놓으면 위의 프로시져에 모두 자동작성되고
이것이 앞에서 Load이벤트에 작성한 것과 다른 점은
디자인 타임에 폼상에 콘트롤이 모두 나타난다는 점이다
그러니..컨트롤을 끌어다 놓을 필요없이
그냥 위의 프로시져에 줄줄이 작성하고 폼을 디자인모드에서
열어보면 마치 컨트롤을 끌어다 놓은 것같이 작성이 되는 것이다

아래의 코드를 그대로 복사하여
Form1.Designer.vb 모듈의
InitalizeComponent() 프로시져내용에 붙여 넣으시고

Private Sub InitializeComponent()

' 각 콘트롤개체 생성

Me.picCanvas = New System.Windows.Forms.PictureBox
Me.MenuStrip1 = New System.Windows.Forms.MenuStrip
Me.mnuDraw = New System.Windows.Forms.ToolStripMenuItem
Me.mnuClear = New System.Windows.Forms.ToolStripMenuItem
Me.mnuSave = New System.Windows.Forms.ToolStripMenuItem
CType(Me.picCanvas, System.ComponentModel.ISupportInitialize).BeginInit()
Me.MenuStrip1.SuspendLayout()
Me.SuspendLayout()

'
' 그림판
'
Me.picCanvas.Dock = System.Windows.Forms.DockStyle.Fill
Me.picCanvas.Location = New System.Drawing.Point(0, 24)
Me.picCanvas.Name = "picCanvas"
Me.picCanvas.Size = New System.Drawing.Size(303, 246)
Me.picCanvas.TabIndex = 0
Me.picCanvas.TabStop = False
'
' 메인메뉴바
'
Me.MenuStrip1.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.mnuDraw})
Me.MenuStrip1.Location = New System.Drawing.Point(0, 0)
Me.MenuStrip1.Name = "MenuStrip1"
Me.MenuStrip1.Size = New System.Drawing.Size(303, 24)
Me.MenuStrip1.TabIndex = 1
Me.MenuStrip1.Text = "MenuStrip1"
'
' 드롭다운 메뉴 
'
Me.mnuDraw.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.mnuClear, Me.mnuSave})
Me.mnuDraw.Name = "mnuDraw"
Me.mnuDraw.Text = "Draw"
'
' 지우기 메뉴
'
Me.mnuClear.Name = "mnuClear"
Me.mnuClear.Text = "Clear"
'
' 그림저장하기
'
Me.mnuSave.Name = "mnuSave"
Me.mnuSave.Text = "Save"
'
' 콘트롤 개체 만든후 폼에 대한 서식
'
Me.ClientSize = New System.Drawing.Size(400, 300)
Me.Controls.Add(Me.picCanvas)
Me.Controls.Add(Me.MenuStrip1)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.MainMenuStrip = Me.MenuStrip1
Me.Name = "Form1"
Me.Text = "Scribble"
Me.MenuStrip1.ResumeLayout(False)
Me.ResumeLayout(False)
Me.PerformLayout()

End Sub

' 개체변수 선언하고
' 선언부 위에 작성하던 밑에 작성하던 상관없다

Public WithEvents picCanvas As System.Windows.Forms.PictureBox
Public WithEvents MenuStrip1 As System.Windows.Forms.MenuStrip
Public WithEvents mnuDraw As System.Windows.Forms.ToolStripMenuItem
Public WithEvents mnuClear As System.Windows.Forms.ToolStripMenuItem
Public WithEvents mnuSave As System.Windows.Forms.ToolStripMenuItem

위의 내용은 도구상자에서 마우스로 폼에 끌어다 놓은 것을
코드로 모두 처리한 것이다
Load이벤트에서 처리한 것은 디자인타임에 하나도 어떤 컨트롤을
어떻게 배치하였는지 하나도 알수 없지만 위와 같이 New이벤트에서
호출하여 작성된 내용은 디자인타임에 그대로 반영된다는 점!!

아무튼 위와 같이 만든후 각 Form1.vb 모듈에서(위의 것은 Form1.Designer.vb였다)



엑셀에서 VBA매크로기록기와 같이 콘트롤을 손으로 이곳저곳
배치하고 속성주고 하는 내용이 위의 InitializeComponents프로시져에
자동으로 기록되는 것이니..콘트롤이름이나 속성을 무엇으로 적용하여야
할지 감이 안올때 위와 같이 작성시키면 쉽게 알아볼수가 있는 것이다

이제 엑셀에서 워크시트에 콘트롤을 올린후 이벤트프로시져를
작성해주어야 지능이 붙여지게 되는 것과 같은 원리로
각콘트롤의 이벤트프로시져를 작성해주는 것이 실제적인
그림을 그리는 작업이 되는 것이다

작업내용은 마우스를 움직일때마다 마우스의 X,Y점을
줄줄이 연결하여 효과는 연필로 선을 그리듯한 효과가 되는 것
앞페이지에서 하였던 Graphic개체가 이것을 관리하고
Graphic개체의 내용을 Bitmap개체로 변환하고
이 Bitmap이라는 개체는
PicutreBox개체의 Image속성에 전달 되는 것이다
그래서 Form1.vb 선언부에

'Bitmap개체
Private oBitmap As System.Drawing.Bitmap
'Graphics개체
Private oGraphics As System.Drawing.Graphics
Private bDrawing As Boolean
Private iX As Integer
Private iY As Integer

Bitmap과 Graphics개체가 주인공인 셈이다
이 두개체는 System.Drawing 네임스페이스하에 있는 개체

폼의 Load 이벤트프로시져에서 위의 두개의 개체를 생성 초기화시킨다

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
     Handles MyBase.Load
       MakeNewBitmap()
End Sub

Private Sub MakeNewBitmap()
' PictureBox의 크기에 맞게 
  Dim iW As Integer = picCanvas.ClientSize.Width
  Dim iH As Integer = picCanvas.ClientSize.Height
' BitMap개체를 생성
  oBitmap = New Bitmap(iW, iH)
' 생성된 Bitmap개체로 Graphics개체생성
  oGraphics = Graphics.FromImage(oBitmap)
' Graphics개체 초기화
  oGraphics.Clear(Me.BackColor)
' PictureBox의 Image속성에 Bitmap개체를 준다
  picCanvas.Image = oBitmap
End Sub

이제 Mouse로 그림판을 선택하는 순간의 MouseDown이벤트에서
최초의 시작 점 X, Y점을 전역변수에 전달한다

Private Sub picCanvas_MouseDown(ByVal sender As Object, _
 ByVal e As System.Windows.Forms.MouseEventArgs) Handles picCanvas.MouseDown
 ' 전역변수 그림그리기가 시작된다는 표시를  bDrawing에 주고
    bDrawing = True
 ' X,Y위치를 이벤트프로시져매개변수 e 개체에서 읽어서 전달
    iX = e.X
    iY = e.Y
End Sub

다음은 마우스를 움직임에 따라서 X, Y점이 계속 변한다
이것을 추적하여 Graphics개체가 그림을 그려나간다

Private Sub picCanvas_MouseMove(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) Handles picCanvas.MouseMove
  If Not bDrawing Then Exit Sub
' Graphics개체의 DrawLine메소드에 색상은 검정색
' 색깔을 바꾸고 싶으면 Pens개체의 색상값을 바꿔주면 된다
' 그리고 X, Y값
  oGraphics.DrawLine(Pens.Black, iX, iY, e.X, e.Y)
' 전역변수의 X,Y값을 갱신해주어 다음 선을 그릴때 시작점을 유지한다
  iX = e.X
  iY = e.Y
' 다시 PictureBox콘트롤의 Image속성을 갱신해주고
  picCanvas.Image = oBitmap
End Sub

이제 마우스버튼을 놓아주면 그림은 끝난다 마우스의 MouseUp이벤트프로시져에 bBitmap값을 False로 하여 그림 그리기가 끝났음을 표시한다

Private Sub picCanvas_MouseUp(ByVal sender As Object, _
     ByVal e As System.Windows.Forms.MouseEventArgs) Handles picCanvas.MouseUp
   bDrawing = False
End Sub

위와 같이 별 복잡한 것 없이 그림그리기 표현을 해볼수 있는 것이다
선의 굵기라던가, 도형의 모양이라던가 모두
Graphics개체의 속성이나 메소드를 다루면 되는 것..
응용확장력이 여러분의 관심과 상상력만큼 커지는 것

이제 저장하기 메뉴를 크릭하면 여러분이 그린대로 그림화일이
저장 된다

Private Sub picCanvas_MouseUp(ByVal sender As Object, _
     ByVal e As System.Windows.Forms.MouseEventArgs) Handles picCanvas.MouseUp
   bDrawing = False
End Sub

이제 그린 그림을 그림화일로 저장하기를 한다

Private Sub mnuSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuSave.Click
	Dim file As String = My.Computer.FileSystem.SpecialDirectories.Desktop & "\test.bmp"
  picCanvas.Image.Save(file)
	MessageBox.Show(file & "  에 저장되었습니다")
End Sub

그림이 담긴 개체의 Image개체의 Save메소드는
마치 엑셀에서 Chart개체의 Export 메소드로 그림화일 만드는 것과
마찬가지다

몇개의 개체들의 성질머리를 이해하고 활용하면 되는 것이
현대의 프로그래밍...!!
손작업으로 콘트롤을 끌어다가 배치하고 속성주는 것 보다
New이벤트나 Load이벤트에 코딩으로 처리하는 것이 편하다고 느껴지는
고수들이 될 때까지..!!

***[LOG-IN]***