달나라 노트

C# : PictureBox (사진 띄우기, 도형 그리기, PictureBox의 Invalidate) 본문

C#/C#

C# : PictureBox (사진 띄우기, 도형 그리기, PictureBox의 Invalidate)

CosmosProject 2022. 4. 5. 20:58
728x90
반응형

 

 

 

PictureBox class는 이미지를 띄울 수 있는 박스입니다.

 

아래 코드를 봅시다.

 

참고로 저는 C:\Users\Public\arraw.png 경로에 아래 이미지를 넣어뒀습니다.

직접 그림판으로 만든 이미지인데 위 이미지를 사용해도 되고 직접 그림판으로 그려도 됩니다.

 

 

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

class MyProgram
{
    public static void Main()
    {
        Form fm = new Form();
        fm.Width = 500;
        fm.Height = 300;

        PictureBox pb = new PictureBox();
        pb.Parent = fm;
        pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");


        Application.Run(fm);
    }
}

 

위 코드를 실행시키면 아래와 같은 Window가 뜹니다.

 

이미지가 잘린거같지만 어쨌든 이미지가 표시되었죠.

PictureBox는 이처럼 이미지를 표시해주는 박스입니다.

 

이제 코드를 부분별로 알아봅시다.

 

 

        Form fm = new Form();
        fm.Width = 500;
        fm.Height = 300;

먼저 PictureBox를 표시해줄 Window를 생성해야합니다.

따라서 위처럼 Form class를 이용해 fm 객체를 생성합니다.

 

 

 

 

        PictureBox pb = new PictureBox();
        pb.Parent = fm;
        pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");

그리고 PictureBox의 생성 부분입니다.

 

- PictureBox pb = new PictureBox();

PictureBox class를 이용해서 pb객체를 생성합니다.

 


- pb.Parent = fm;

PictureBox는 fm 객체에 소속되어 표시되어야하므로 pb.Parent를 fm으로 생성합니다.

 


- pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");

PictureBox class로 객체를 생성하면 PcitureBox는 비어있는 상태입니다.

따라서 PictureBox에 이미지를 넣으려면 위처럼 Image.FromFile() method를 사용해야합니다.

PictureBox의 Image 속성에 Image.FromFile() method를 이용해서 이미지를 불러와서 넣어야합니다.

 

Image.FromFile() method의 인자로는 이미지가 존재하는 경로를 명시해줍니다.

 

근데 경로를 적을 때 뭔가 좀 이상합니다.

보통 경로에서 폴더를 구분할 때에는 \를 이용해서 구분합니다. 아래의 경로처럼요.

C:\Users\Public\arrow.png

 

근데 위 코드에서는 \를 2개씩 써서 구분했습니다.

C:\\Users\\Public\\arrow.png

 

그 이유는 \라는 기호 자체가 어떠한 기능을 가지고 있기 때문에 \를 하나만 쓰면 \의 기능이 발현됩니다.

따라서 \를 \자체로 인식시키려면 \를 이용해 escape를 해야합니다.

그래서 \를 2개씩 적은 것입니다.

 

 

 

근데 위 코드의 결과를 보면 아래와 같았습니다.

이미지가 잘렸죠.

그 이유는 PcitureBox의 기본 크기보다 Image의 크기가 크기 때문입니다.

 

 

이것을 해결하는 방법은 두 가지가 있습니다.

 

먼저 PictureBox의 Width, Height 옵션을 직접 지정해주는 방법입니다.

아래 코드를 봅시다.

 

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

class MyProgram
{
    public static void Main()
    {
        Form fm = new Form();
        fm.Width = 500;
        fm.Height = 300;

        PictureBox pb = new PictureBox();
        pb.Parent = fm;
        pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");
        pb.Width = 400;
        pb.Height = 250;


        Application.Run(fm);
    }
}

 

pb.Width, pb.Height 옵션을 각각 400, 250으로 지정해줬더니 아래처럼 이미지가 잘리는 부분 없이 잘 보입니다.

 

 

하지만 이렇게 Width, Height 옵션을 직접 지정하는 것은 사실 대부분의 경우에 권장되지는 않습니다.

 

그 이유는 다뤄야 할 이미지의 크기를 항상 알고있어야하며, 다루는 이미지의 크기가 제각각이기 때문에 매번 설정해줘야하기 때문입니다.

 

따라서 이번에는 PictureBoxSizeMode 옵션을 이용해보겠습니다.

 

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

class MyProgram
{
    public static void Main()
    {
        Form fm = new Form();
        fm.Width = 500;
        fm.Height = 300;

        PictureBox pb = new PictureBox();
        pb.Parent = fm;
        pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");
        pb.SizeMode = PictureBoxSizeMode.AutoSize;


        Application.Run(fm);
    }
}

 

SizeMode를 조절한 결과입니다.

PictureBox의 Width, Height 옵션을 조절하지 않았으나 이미지가 모두 표시되는걸 볼 수 있습니다.

 

 

 

 

        pb.SizeMode = PictureBoxSizeMode.AutoSize;

그것은 바로 위 부분 때문입니다.

SizeMode는 이미지를 PictureBox에 표시할 때 크기를 어떠한 방식으로 조절할 것인가를 의미합니다.

 

여기서는 PcitureBoxSizeMode.AutoSize라고 적어놨는데, 이것은 원본 사진의 크기와 동일하게 PictureBox의 크기를 맞추라는 의미입니다.

 

 

 

아래 예시는 SizeMode를 어떻게 설정하는지에 따라 그 결과가 어떻게 보이는지에 대한 내용입니다.

 

 

        pb.SizeMode = PictureBoxSizeMode.CenterImage;

CenterImage 옵션은 이미지를 PictureBox의 가운데에 정렬하여 나타냅니다.

원본 이미지랑 비교해보면 이미지 가운데에있는 화살표만 보이고 PcitureBox 밖에 있는 파란색 배경은 대부분이 잘려나간걸 볼 수 있습니다.

 

 

 

 

 

        pb.SizeMode = PictureBoxSizeMode.Normal;

Normal 옵션은 위처럼 결과가 나타납니다.

이것은 기본값입니다.

PictureBox에 원본 이미지가 나타나며 PictureBox를 넘어서는 부분은 모두 잘려서 보입니다.

 

 

 

 

 

 

        pb.SizeMode = PictureBoxSizeMode.StretchImage;

StretchImage 옵션은 PictureBox의 크기에 맞게 원본 이미지의 크기를 축소/확대시켜서 PictureBox에 꽉 차게 표시하는 옵션입니다. 이때 원본 이미지의 가로/세로 길이 비율은 무시되고 PcitureBox를 꽉 채우도록 원본 이미지를 확대/축소시킵니다.

위 결과 이미지를 보시면 원본 이미지와는 다르게 뭔가 좀 축소되어서 PictureBox에 맞춰진 것을 볼 수 있습니다.

 

 

 

 

 

        pb.SizeMode = PictureBoxSizeMode.Zoom;

Zoom 옵션은 PictureBox에 맞게 이미지를 확대/축소시키는데 원본 이미지의 가로/세로 길이 비율을 유지합니다.

 

Zoom 옵션은 StretchImage옵션과 동일하게 PictureBox에 맞게 이미지의 크기를 확대/축소시킵니다.

다만 이 두가지 옵션이 다른점은 다음과 같습니다.

Zoom --> 원본 이미지의 가로/세로 길이 비율을 유지시키며 PictureBox를 최대한 채우기 위해 이미지를 확대/축소시킴

StretchImage --> 원본 이미지의 가로/세로 길이 비율을 유지하지 않고 PictureBox를 꽉 채우기 위해 이미지를 확대/축소시킴

 

https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.pictureboxsizemode?redirectedfrom=MSDN&view=windowsdesktop-6.0​

SizeMode 옵션에 대한 자세한 내용은 위 링크를 참고하면 됩니다.

 

 

 

 

 

 

 

 

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

class MyProgram
{
    public static void Main()
    {
        Form fm = new Form();
        fm.Width = 500;
        fm.Height = 300;

        PictureBox pb = new PictureBox();
        pb.Parent = fm;
        pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");
        pb.Location = new Point(50, 100);


        Application.Run(fm);
    }
}

 

PictureBox의 Location 옵션을 설정하면 PictureBox가 Window의 어디에 표시될지에 대한 좌표를 지정할 수 있습니다.

 

- pb.Location = new Point(50, 100);

PictureBox의 Location을 설정할 때에는 Point 객체를 통해 전달해야 합니다.

Point 객체의 첫 번째 인자는 x좌표이며 두 번째 인자는 y좌표입니다.

 

 

위 코드를 실행한 결과입니다.

표시되는 이미지의 좌측 상단 꼭지점의 좌표가 (50, 100)으로 설정되었습니다.

 

 

 

 

 

 

 

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

class MyProgram
{
    public static void Main()
    {
        Form fm = new Form();
        fm.Width = 500;
        fm.Height = 300;

        PictureBox pb = new PictureBox();
        pb.Parent = fm;
        pb.Image = Image.FromFile("C:\\Users\\Public\\arrow.png");
        pb.Location = new Point(50, pb.Location.Y);


        Application.Run(fm);
    }
}

 

비슷한 맥락이지만 pb.Location.Y property를 Point 객체의 하나의 인자로서 전달할 수 있습니다.

 

- pb.Location = new Point(50, pb.Location.Y);

위 코드는 결국 pb의 Y좌표는 기존 Y좌표를 사용하고 x좌표만 50으로 설정한다는 의미가 됩니다.

 

 

 

실행한 결과를 보면 원래 pb의 y 좌표는 0이었으니 y좌표는 그대로 0입니다.

근데 x좌표는 50으로 설정되었습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

PictureBox에는 단순히 어떤 이미지를 띄우는 것 뿐 아니라 FillEllipse, DrawString 등 여러 Graphic Event를 통해 PictureBox 안에 도형을 그릴 수 있습니다.

 

그리고 이러한 PictureBox에 클릭 Event를 적용해서 PictureBox를 클릭하면 PictureBox에 그려진 도형이나 글자를 바꾸는 등의 동작도 가능합니다.

 

아래 코드는 PictureBox를 클릭할 경우 Apple로 표시되어있던 PictureBox 내의 글자를 Banana로 바꿔주는 기능을 구현한 코드입니다.

 

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

class Sample2 : Form
{
    public static void Main()
    {
        // Constants //
        Form fm = new Form();
        PictureBox pb = new PictureBox();
        int client_size_width = 200;
        int client_size_height = 200;
        string show_text = "Apple";
        Font font = new Font("Arial", 15);


        // Settings //
        fm.ClientSize = new Size(client_size_width, client_size_height);

        pb.Parent = fm;
        pb.Width = client_size_width;
        pb.Height = client_size_height;


        // Events //
        void pb_paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            g.FillEllipse(new SolidBrush(Color.Gray), 0, 0, client_size_width, client_size_height);

            SizeF string_size = g.MeasureString(show_text, font);

            float string_x = client_size_width / 2 - string_size.Width / 2;
            float string_y = client_size_height / 2 - string_size.Height / 2;

            g.DrawString(show_text, font, new SolidBrush(Color.White), string_x, string_y);
        }
        pb.Paint += new PaintEventHandler(pb_paint);


        void pb_click(object sender, EventArgs e)
        {
            show_text = "Banana";
            pb.Invalidate();
        }
        pb.Click += new EventHandler(pb_click);



        // Main //
        Application.Run(fm);
    }
}

 

 

위 코드를 실행하면 일단 위같은 화면이 나옵니다.

 

저기서 클릭을 해봅시다.

 

 

그러면 위처럼 Banana로 글씨가 바뀌게 됩니다.

 

 

 

 

        void pb_click(object sender, EventArgs e)
        {
            show_text = "Banana";
            pb.Invalidate();
        }
        pb.Click += new EventHandler(pb_click);

 

위 코드에서 주의할 점이 있는데 바로 위 부분입니다.

DrawString method가 PictureBox에 띄워주는 글자는 show_text 라는 변수에 저장된 문자입니다.

즉, show_text가 처음에는 Apple이라는 텍스트를 담고있으니 첫 화면은 Apple이 표시됩니다.

 

그리고 pb_click Event method에서 show_text 변수에 Banana를 할당하게되죠.

 

근데 주의할 점은 이렇게 show_text 변수에 새로운 문자를 할당했다고 해서 화면에 나타나는 글자가 변하지 않습니다.

저희가 웹 사이트를 이용하다가 여러 새로운 정보들을 적용한 후 새로고침을 하듯이, C#의 Drawing들도 새로운 값들을 할당한 후 새로고침을 해야 Drawing들이 새로운 값들을 기반으로 다시 그려지게 됩니다.

 

즉, show_text에 Banana를 할당한 후 pb.Invalidate()로 PictureBox를 새로고침한거죠.

이렇게 반드시 내가 새로고침할 객체(PictureBox, Form 등)에 대해 Invalidate method를 적용해줘야만 도형들이 새로운 값들을 기준으로 다시 그려지게 됩니다.

 

 

 

또한 주의할 점이 하나 더 있습니다.

 

위에서 아래와 같이 말했습니다.

 

내가 새로고침할 객체(PictureBox, Form 등)에 대해 Invalidate method를 적용해줘야만 도형들이 새로운 값들을 기준으로 다시 그려지게 됩니다.

 

근데 반드시 내가 새로고침할 객체에 대해 Invalidate를 적용해야한다고 했습니다.

 

        void pb_click(object sender, EventArgs e)
        {
            show_text = "Banana";
            fm.Invalidate();
        }
        pb.Click += new EventHandler(pb_click);

 

즉, 위처럼 pb_click method내부에 Invalidate를 넣어뒀는데 Invalidate를 fm(Form 객체)에 적용했습니다.

이렇게되면 Form만 새로고침되고 PictureBox는 새로고침되지 않으므로, show_text에 Banana가 할당되었다고 해도 PictureBox에는 Apple이라는 글자가 표시되게 됩니다.

 

이렇게 Invalidate method를 사용할 때에는 반드시 내가 새로고침하길 원하는 개체에 적용해야하는 것을 명심합시다.

 

 

 

 

 

 

 

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

class Sample2 : Form
{
    public static void Main()
    {
        // Constants //
        Form fm = new Form();
        PictureBox pb = new PictureBox();
        int client_size_width = 200;
        int client_size_height = 200;
        string show_text = "Apple";
        Font font = new Font("Arial", 15);


        // Settings //
        fm.ClientSize = new Size(client_size_width, client_size_height);

        pb.Parent = fm;
        pb.Width = client_size_width;
        pb.Height = client_size_height;


        // Events //
        void pb_paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            g.FillEllipse(new SolidBrush(Color.Gray), 0, 0, client_size_width, client_size_height);

            SizeF string_size = g.MeasureString(show_text, font);

            float string_x = client_size_width / 2 - string_size.Width / 2;
            float string_y = client_size_height / 2 - string_size.Height / 2;

            g.DrawString(show_text, font, new SolidBrush(Color.White), string_x, string_y);
        }
        pb.Paint += new PaintEventHandler(pb_paint);


        void pb_click(object sender, EventArgs e)
        {
            show_text = "Banana";
            client_size_width = 100;
            client_size_height = 100;
            pb.Invalidate();
        }
        pb.Click += new EventHandler(pb_click);



        // Main //
        Application.Run(fm);
    }
}

 

위 코드는 이전에 봤던 것과 동일하지만 아래 부분만 약간 수정하였습니다.

 

 

        void pb_click(object sender, EventArgs e)
        {
            show_text = "Banana";
            client_size_width = 100;
            client_size_height = 100;
            pb.Invalidate();
        }
        pb.Click += new EventHandler(pb_click);

 

보시면 pb_click method 내부에 client_size_width와 client_size_height을 각각 100으로 설정하는 부분을 넣어놨습니다.

 

자 그러면 PictureBox를 클릭했을 때 원래 client size가 200 x 200이었던 Window가 client size 100x100 으로 축소될까요?

 

그리고 FillEllipse와 DrawString도 client_size_width, client_size_height 값을 이용하는데 이러한 도형들은 client_size 100x100에 맞게 재설정될까요?

 

 

 

위 코드를 실행한 첫 화면입니다.

첫 화면은 동일합니다.

 

이제 클릭을 하면 아래처럼 변합니다.

 

 

보시면 Window의 크기는 동일한데 원이 더 작아지고 Banana로 표시되는 글자의 위치가 변경되었죠.

 

어떻게 이렇게 된걸까요?

이것은 바로 Invalidate가 어디에 적용되었는지를 보면 알 수 있습니다.

 

 

        void pb_click(object sender, EventArgs e)
        {
            show_text = "Banana";
            client_size_width = 100;
            client_size_height = 100;
            pb.Invalidate();
        }
        pb.Click += new EventHandler(pb_click);

 

자 이 부분을 다시 봅시다.

Invalidate method가 어디에 적용되었나요? pb.Invalidate() 로서 바로 PictureBox에 적용되었습니다.

 

즉, 새로운 client_size_width, client_size_height 값을 기반으로 새로고침되는 것은 PictureBox와 PictureBox에 속한 객체들(도형/사진 등)입니다.

 

따라서 Form은 그대로라는 의미이죠.

 

 

client_size_width같은 변수들은 Invalidate에 영향을 받지 않습니다.

pb_click method 내부에서 client_size_width는 100으로 재설정되었죠.

그 다음 pb.Invalidate() 부분에서 Invalidate method가 실행되었습니다.

 

pb.Invalidate() method는 PictureBox에 적용되어 PictureBox에 속해있는 Paint Event를 현재 update된 값들을 기준으로 다시 그려줄 뿐입니다.

 

- 코드 상단에서 가장 처음 client_size_width, client_size_height을 정하는 부분

int client_size_width = 200;
int client_size_height = 200;

Invalidate method가 코드 전체를 다시 실행시켜 코드 상단에 있는 client_size_width를 200으로 설정하는 부분에 의해 client_size_width를 100에서 다시 200으로 변경하지 않습니다.

 

Invalidate를 실행했다고 해서 코드의 모든 부분을 다시 시작하는 것이 아니라, Invalidate가 적용된 객체(PictureBox, Form 등)의 Paint Event가 새로 update된 값들을 기반으로 다시 그려진다고 생각하면 편합니다.

 

 

 

 

 

 

728x90
반응형
Comments