하위 form2, form3 은 항상 form1보다 앞에 위치!

(Winform) 하위 form2, form3이 form1 앞에 위치


(WPF에 Winform 삽입) form1이 MainWindow 앞에 위치

 

Form 앞쪽에 고정 시키고 싶은 Form이 있는 경우입니다.

Winform에서는 Owner 속성을 지정해주면 해당 Form보다 항상 앞에 위치하게 됩니다.

WPF에서는 창의 Handle 값을 가져와서 Winform에서 Show 할 때 지정해줄 수 있습니다.

 

[Winform]

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

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private Form2 form2 = new Form2();
        private Form3 form3 = new Form3();
        private int titleSize = 30;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            form2.Owner = this; //Form3은 Form1 앞에 위치
            form3.Owner = this; //Form3은 Form1 앞에 위치

            form2.Size = new Size(this.Width / 2, this.Height - titleSize);
            form3.Size = new Size(this.Width / 2, this.Height - titleSize);

            form2.Show();
            form3.Show();

            form2.Location = new Point(this.Location.X, this.Location.Y + titleSize);
            form3.Location = new Point(this.Location.X + form2.Size.Width, this.Location.Y + titleSize);
        }

        private void Form1_Move(object sender, EventArgs e)
        {
            form2.Location = new Point(this.Location.X, this.Location.Y + titleSize);
            form3.Location = new Point(this.Location.X + form2.Size.Width, this.Location.Y + titleSize);
        }
    }
}

 

[WPF 에서 Winform]

using System;
using System.Windows;
using System.Windows.Interop;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Form1 form1 = new Form1();

            WindowInteropHelper helper = new WindowInteropHelper(this);
            IntPtr handle = helper.Handle; //WPF 핸들값 가져오기

            form1.Show(new Win32Window(handle)); //form1이 항상 윗쪽에!
        }
    }

    class Win32Window : System.Windows.Forms.IWin32Window
    {
        private IntPtr handle;
        public IntPtr Handle
        {
            get
            {
                return handle;
            }
        }

        public Win32Window(IntPtr handle)
        {
            this.handle = handle;
        }
    }
}

IPC (Inter Process Communication) 방식 중 네임드파이프 통신이 있습니다.

서로 다른 프로그램에서 정의된 파이프 이름만 알면 통신이 가능한 방식입니다.

아래는 결과와 소스코드!

 

왼쪽은 A_Pipe, 오른쪽은 B_Pipe

 

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class NamedPipe : Form
    {
        private const string myName = "A_Pipe"; //이름만 바꿔서 사용 가능
        private const string targetName = "B_Pipe"; //이름만 바꿔서 사용 가능

        private bool isOpen = true;

        public NamedPipe()
        {
            InitializeComponent();

            this.FormClosing += Form1_FormClosing;
            button1.Click += button1_Click;

            Task.Run(() =>
            {
                Receive();
            });
        }

        private void Receive()
        {
            while (isOpen)
            {
                try
                {
                    NamedPipeServerStream namedPipeServerStream = new NamedPipeServerStream(myName, PipeDirection.InOut, 10);
                    namedPipeServerStream.WaitForConnection(); //데이터 받을때까지 기다립니다.

                    StreamReader streamReader = new StreamReader(namedPipeServerStream);
                    string line = streamReader.ReadLine();

                    AddListItem(line);

                    streamReader.Close();
                    namedPipeServerStream.Close();
                }
                catch (Exception ex)
                {
                    AddListItem("Receive Error : " + ex.Message);
                }
            }
        }

        private void AddListItem(string message)
        {
            Invoke((MethodInvoker)delegate
            {
                listBox1.Items.Add(message);
            });
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream(".", targetName, PipeDirection.InOut);
                namedPipeClientStream.Connect(10000); //10초

                StreamWriter streamWriter = new StreamWriter(namedPipeClientStream);

                streamWriter.WriteLine(textBox1.Text);
                streamWriter.Flush();

                streamWriter.Close();
                namedPipeClientStream.Close();
            }
            catch (Exception ex)
            {
                AddListItem("Send Error : " + ex.Message);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            isOpen = false;
        }
    }
}

현재 영역에 대해서만 그려주기! (4개만 보이는 상태)

 

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

namespace WindowsFormsApp1
{
    public partial class Form1 : Form, IMessageFilter
    {
        private const int buttonOffset = 10;

        private List<Button> buttonList = new List<Button>();
        private List<Control> focusedList = new List<Control>();
        private int scrollPosition;

        public Form1()
        {
            InitializeComponent();

            panel1.AutoScroll = true; //스크롤 활성화

            //스크롤 이벤트 등록
            this.panel1.Resize += new System.EventHandler(this.panel1_Resize);
            this.panel1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.panel1_Scroll);

            //버튼 이벤트 등록
            this.button_add.Click += new EventHandler(this.button_add_Click);

            Application.AddMessageFilter(this); //IMessageFilter 사용하기 위해 등록!
        }

        public bool PreFilterMessage(ref Message m) //IMessageFilter 콜백
        {
            if (m.Msg == 0x20a) // WM_MOUSEWHEEL (마우스 휠)
            {
                if (panel1.Bounds.Contains(PointToClient(Cursor.Position)))
                {
                    short x = (short)(((int)m.WParam >> 16) & 0xffff);

                    if (x < 0)
                    {
                        ScrollMove(20);
                    }
                    else
                    {
                        ScrollMove(-20);
                    }

                    return true;
                }
            }

            return false;
        }

        private void ScrollMove(int move)
        {
            int preValue = scrollPosition;

            scrollPosition += move;
            scrollPosition = Math.Max(scrollPosition, 0);
            scrollPosition = Math.Min(panel1.DisplayRectangle.Width - panel1.Width, scrollPosition);

            panel1.HorizontalScroll.Value = scrollPosition;

            //기존 값과 같다면 동작하지 않게 합니다.
            if (preValue == scrollPosition)
            {
                return;
            }

            UpdateThumbnail(scrollPosition);
        }

        private void button_add_Click(object sender, EventArgs e)
        {
            Button button = new Button();

            button.Text = buttonList.Count.ToString("D4");
            button.Hide();

            panel1.Controls.Add(button);
            panel1.AutoScrollMinSize = new Size(panel1.AutoScrollMinSize.Width + button.Width + buttonOffset, 0);

            buttonList.Add(button);
            UpdateThumbnail(scrollPosition);
        }

        private void UpdateThumbnail(int startPosition)
        {
            int totalWidth = 0;

            //돌면서 그려줘야 할 영역에 대해서만 그려주기!
            for (int i = 0; i < buttonList.Count; ++i)
            {
                totalWidth += buttonList[i].Width + buttonOffset;

                //처음으로 커지는 구간을 찾기! 시작 지점!
                if (totalWidth >= startPosition)
                {
                    int showPositionX = totalWidth - startPosition - buttonList[i].Width - buttonOffset;

                    List<Control> newFocusList = new List<Control>();

                    for (int j = i; j < buttonList.Count; ++j)
                    {
                        Point position = new Point(showPositionX, 0);
                        showPositionX += buttonList[j].Width + buttonOffset;

                        buttonList[j].Location = position;
                        buttonList[j].Show();

                        newFocusList.Add(buttonList[j]);

                        if (this.Width <= showPositionX)
                        {
                            break;
                        }
                    }

                    //현재 영역에 들어있지 않은 기존 항목은 제거해줍니다.
                    foreach (Control item in focusedList)
                    {
                        if (newFocusList.Contains(item) == false)
                        {
                            item.Hide();
                        }
                    }

                    //현재 항목을 갱신합니다.
                    focusedList = newFocusList;

                    break;
                }
            }
        }

        private void panel1_Scroll(object sender, ScrollEventArgs e)
        {
            scrollPosition = e.NewValue;
            Console.WriteLine("scrollPosition : " + scrollPosition);
            UpdateThumbnail(scrollPosition);
        }

        private void panel1_Resize(object sender, EventArgs e)
        {
            if (panel1.AutoScrollMinSize.Width - panel1.Width <= scrollPosition)
            {
                Console.WriteLine("*************************** 크기 변경!!!");

                scrollPosition = Math.Max(0, panel1.AutoScrollMinSize.Width - panel1.Width);
                panel1.HorizontalScroll.Value = scrollPosition;
            }

            UpdateThumbnail(scrollPosition);
        }
    }
}

 

이미지 회전에 따른 여백 생성!

 

우선은 코드만 올립니다..

using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private string workFolder = Path.Combine(Application.StartupPath, "workFolder");
        private int sequence = 0;

        public Form1()
        {
            InitializeComponent();

            //이미지 확인을 위해 오토사이즈로 해줍니다.
            pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
            pictureBox2.SizeMode = PictureBoxSizeMode.AutoSize;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();

            //파일을 불러옵니다.
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                pictureBox1.Image = Image.FromFile(ofd.FileName);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (Directory.Exists(workFolder) == false)
            {
                Directory.CreateDirectory(workFolder);
            }

            sequence++; //돌린 횟수! (파일명 겹치지 않도록 해줍니다.)
            Image originImage = pictureBox1.Image; //사용될 원본 이미지!

            for (int rotation = 1; rotation <= 360; ++rotation) //360도 회전시켜줍니다.
            {
                int maxAngle = 91;

                int width = originImage.Width;
                int height = originImage.Height;

                int rot; //항상 0 ~ 90도로 만들어줍니다.

                if (90 < rotation && rotation <= 181) //해당 구간에서는 반전이 필요합니다.
                {
                    rot = 180 - rotation;
                }
                else if (270 < rotation && rotation <= 360) //해당 구간에서는 반전이 필요합니다.
                {
                    rot = 360 - rotation;
                }
                else
                {
                    rot = rotation % maxAngle;
                }

                double widthRadian = rot * Math.PI / 180;
                double heightRadian = (90 - rot) * Math.PI / 180; //90도 기준으로 반전 시켜줍니다.

                double w = Math.Cos(widthRadian) * width + Math.Sin(widthRadian) * height;
                double h = Math.Cos(heightRadian) * width + Math.Sin(heightRadian) * height;

                //기존 이미지 기준으로 돌려줍니다.
                DrawImage(originImage,
                          pictureBox1,
                          rotation,
                          width,
                          height,
                          width,
                          height,
                          "origin_");

                //크기가 확장 된 이미지 기준으로 돌려줍니다.
                DrawImage(originImage,
                          pictureBox2,
                          rotation,
                          (int)w,
                          (int)h,
                          width,
                          height,
                          "extend_");
            }

            originImage.Dispose();

            Process.Start(workFolder);
        }

        private void DrawImage(Image originImage,
                                PictureBox pictureBox,
                                int rotation,
                                int width,
                                int height,
                                int centerX,
                                int centerY,
                                string imageType)
        {
            Color backgroundColor = Color.FromArgb(255, 255, 0, 0);      //빨강 배경 (알파값을 통해 투명하게 가능!)

            Bitmap bitmap = new Bitmap(width, height);
            Graphics graphics = Graphics.FromImage(bitmap);

            graphics.Clear(backgroundColor);                             //배경 칠해주기
            graphics.TranslateTransform(width / 2, height / 2);          //이미지 가운데로 이동
            graphics.RotateTransform(rotation);                          //가운데에서 회전
            graphics.DrawImage(originImage, centerX / -2, centerY / -2); //이미지를 가운데 그려줍니다.

            string fileName = string.Format("{0}_{1}{2}.png", sequence.ToString("D4"), imageType, rotation.ToString("D4"));
            string savePath = Path.Combine(workFolder, fileName);

            bitmap.Save(savePath);

            graphics.Dispose();
            bitmap.Dispose();

            pictureBox.Image = Image.FromFile(savePath);
            pictureBox.Update();
        }
    }
}

 

 

간단한 용어 정리

Invoke : 메인 쓰레드에서 동작하도록 해주는 기능 (다른 스레드 => 메인UI 스레드 동작 시켜줌)

InvokeRequired : Invoke가 필요한 상황인지 체크. (메인UI 스레드 != 현재 실행중인 스레드 체크)

 

Windows Form에서 UI는 메인 스레드에서만 변경이 가능하며 그 외의 접근에 대해서는 예외가 발생됩니다.

 

우선 사용 방법은 다음과 같습니다. 방법1 혹은 방법2를 참고하여 사용하면 됩니다.

(예외와 주의 사항은 더 아래쪽에 적어두겠습니다.)

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form3 : Form
    {
        //방법2를 위한 delegate ******************
        private delegate void SetLabelDelegate();
        private SetLabelDelegate setLabelDelegate;
        //****************************************

        private string message1 = "";
        private string message2 = "";
        private string message3 = "";
        
        public Form3()
        {
            InitializeComponent();

            //방법2를 위한 delegate 등록 *************
            setLabelDelegate += SetLabel;
            //****************************************
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(ChangeUI);

            message1 = "mainThread " + AppDomain.GetCurrentThreadId().ToString();
            thread.Start();
        }

        private void ChangeUI ()
        {
            message2 += "subThread " + AppDomain.GetCurrentThreadId().ToString();

            if (InvokeRequired)
            {
                //방법1. MethodInvoker, delegate 조합
                Invoke((MethodInvoker)delegate
                {
                    message3 = "InvokeThread " + AppDomain.GetCurrentThreadId().ToString();
                    SetLabel();
                });

                //방법2. delegate 호출
                //Invoke(setLabelDelegate);
            }
            else
            {
                SetLabel();
            }
        }

        private void SetLabel ()
        {
            label1.Text = message1;
            label2.Text = message2;
            label3.Text = message3;
        }
    }
}

 

결과 (SetLabel은 메인쓰레드 7724로 실행)

 

 

오류 예시.

[System.InvalidOperationException - Cross Thread]

: 메인 스레드가 아닌 다른 스레드에서 UI 변경을 한 경우 발생

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(ChangeUI);

            MessageBox.Show(AppDomain.GetCurrentThreadId().ToString()); //main thread number
            thread.Start();
        }

        private void ChangeUI ()
        {
            MessageBox.Show(AppDomain.GetCurrentThreadId().ToString()); //other thread number
            label1.Text = "1";
        }
    }
}

 

결과 : 당연하게 오류 발생

 

 

주의 사항)

 쓰레드 번호를 확인할 수 있도록 코드를 작성해놨습니다.

이것으로 현재 함수가 어떤 쓰레드에서 실행 중인지 볼 수 있는데,

Invoke를 통해 MainThread -> SubThread -> MainThread 방식의 흐름이 가능합니다.

반드시 필요한 곳에서만 Invoke를 사용해야 올바른 비동기 코드가 진행이 될 수 있습니다.

잘 못된 코드와 올바른 코드 예시를 남깁니다.

 

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread validInvokeThread = new Thread(ChangeUI);
            validInvokeThread.Start();

            //이렇게 호출되면 안됨.
            //Thread invalidInvokeThread = new Thread(ChangeUI2);
            //invalidInvokeThread.Start();
        }

        private void ChangeUI()
        {
            //스레드 동작과 메인UI 스레드가 원하는대로 잘 동작하게 됩니다.
            SetLabel(label1, AppDomain.GetCurrentThreadId().ToString());
            Thread.Sleep(1000);
            SetLabel(label2, AppDomain.GetCurrentThreadId().ToString());
            Thread.Sleep(1000);
            SetLabel(label3, AppDomain.GetCurrentThreadId().ToString());
        }

        private void ChangeUI2()
        {
            //이렇게 사용하면 MainThread에서 모든 것을 처리해주게 됩니다.
            //즉 "스레드의 사용 의미가 없다" 입니다.
            if (InvokeRequired)
            {
                Invoke((MethodInvoker)delegate
                {
                    ChangeUI2();
                });
            }
            else
            {
                SetLabel(label1, AppDomain.GetCurrentThreadId().ToString());
                Thread.Sleep(1000);
                SetLabel(label2, AppDomain.GetCurrentThreadId().ToString());
                Thread.Sleep(1000);
                SetLabel(label3, AppDomain.GetCurrentThreadId().ToString());
            }
        }

        private void SetLabel(Label label, string text) //delegate로 함수를 만들어서 처리도 가능
        {
            if (InvokeRequired)
            {
                Invoke((MethodInvoker)delegate
                {
                    label4.Text = "메인스레드 : " + AppDomain.GetCurrentThreadId().ToString();
                    label.Text = text;
                });
            }
            else
            {
                label4.Text = "메인스레드 : " + AppDomain.GetCurrentThreadId().ToString();
                label.Text = text;
            }
        }
    }
}

 

ChangeUI (UI는 메인에서, Sleep은 다른 스레드에서 처리)

ChangeUI2 (메인 스레드에서 처리. Sleep에 의해 멈추기도함)

 

GraphicsPath 를 이용하여 둥근 버튼, 별표 등 다양한 형태의 도형을 만들 수 있습니다.

둥근 버튼

 

1. 새 항목 추가로 클래스를 만들어줍니다.

 

2. 아래와 같이 Button을 상속 받고 로직을 작성해줍니다.

  (path 첫 위치에 따라 회전각도가 달라집니다. 회전은 0도 = 12시 방향이고, 시계방향으로 돕니다!)

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

namespace MyButton
{
    class CustomButton : Button
    {
        public int Radius { get; set; } //외부에서 크기를 받습니다.
        public bool IsMaximum { get; set; } //외부에서 최대 사이즈를 받습니다.

        public CustomButton ()
        {
            Radius = 20;
            IsMaximum = true;

            this.BackColor = Color.Red; //기본 색을 지정해줍니다.
            this.FlatStyle = FlatStyle.Flat; //버튼을 플랫하게 만들어줍니다.
            this.FlatAppearance.BorderSize = 0; //보더가 지저분해지기에 제거해줍니다.
        }

        protected override void OnPaint(PaintEventArgs p)
        {
            GraphicsPath path = new GraphicsPath();
            Rectangle rectangle = ClientRectangle;

            int radius = Math.Min(this.Height, Radius); //Height를 넘어가면 모양이 이상해집니다!

            if (IsMaximum) //최대 사이즈인 경우는 Height로!
            {
                radius = this.Height;
            }

            int x = rectangle.X;
            int y = rectangle.Y;
            int width = rectangle.Width;
            int height = rectangle.Height;

            //그리는 순서가 굉장히 중요합니다!
            path.AddArc(x, y, radius, radius, 180, 90); //왼쪽 상단
            path.AddArc(x + width - radius, y, radius, radius, 270, 90); //오른쪽 상단
            path.AddArc(x + width - radius, y + height - radius, radius, radius, 0, 90); //오른쪽 하단
            path.AddArc(x, y + height - radius, radius, radius, 90, 90); //왼쪽 하단

            path.CloseAllFigures();

            this.Region = new Region(path); //영역을 둥근형태로 만들어줍니다.

            base.OnPaint(p);
        }
    }
}

 

프로젝트를 "빌드" 하면 아래와 같이 구성요소에서 재정의한 버튼을 볼 수 있습니다.

해당 버튼을 알맞게 배치하고, 값을 설정하여 사용합시다!

디자인 문서개요는 컨트롤들의 순서 등 정리가 아주 편리합니다. (특히 도킹 관련해서 순서 지정이 가능!)

(+ 꿀팁 : 컨트롤에 포커싱 있는 상태에서 ESC 키를 통해 상위 컨트롤로 선택이 가능합니다.)

 

보기 => 다른 창 => 문서 개요  를 통해 복잡한 오브젝트를 쉽게 볼 수 있습니다.

 

문서 개요 매뉴

 

왼쪽 문서 개요 뷰에서 요소의 순서를 바꾸거나 삽입 / 제거 도 쉽답니다.

 

간단한 구조가 마음에 들어 까먹기 전에 다시 작성해두기!

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

namespace LogTest
{
    public enum LogLevel
    {
        Debug, Info, Error
    }

    public static class Log
    {
        private static LogLevel logLevel;
        private static string logPath;

        public static void Init(LogLevel level) //최초 한번 실행해주기!
        {
            logLevel = level;

            //폴더 등 설정
            logPath = Path.Combine(Application.StartupPath, "test.log");
            //기존 불필요한 로그 삭제하기.
        }

        public static void Write(string message, LogLevel level, [CallerMemberName] string callFunction = "")
        {
            if (logLevel <= level)
            {
                string type = level.ToString();

                string logContent = string.Format("[{0}] {1} ({2}) : {3}{4}",
                                                type, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"), callFunction, message, Environment.NewLine);

                File.AppendAllText(logPath, logContent);
            }
        }
    }
}

세 개의 버튼을 만들고 테스트 해봅시다.

 

프로그램 중간에 종료를 시켜야 하는 경우 위의 3가지 방식을 사용할 수 있습니다.

각 특징은 아래와 같습니다.

 

Close() : 해당되는 폼만 닫습니다.

                

Application.Exit() : 다른 서브 폼에서 호출 해도 메인 폼의 Close()까지 동작되게 됩니다.

 

Environment.Exit(0) : 이 곳에서 바로 종료됩니다.

 

using System;
using System.Windows.Forms;

namespace CloseTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Close();
            //이 후 로직도 타게 됩니다.
            //무언가 로직 실행... (+ FormClosing)
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Application.Exit();
            //이 후 로직도 타게 됩니다.
            //무언가 로직 실행... (+ FormClosing)
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Environment.Exit(0);
            //바로 종료 됩니다. (FormClosing을 타지 않습니다.)
            //실행되지 않는 이 후 로직!
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //Close(), Application.Exit() 시에 이 곳까지 도달합니다.

            e.Cancel = true; //프로그램이 종료되지 못하도록 막습니다.
        }
    }
}

base64 바이트를 이용한 파일 읽기 / 생성

 

base64를 통해 파일을 바이트 혹은 "텍스트" 형태로 주고 받을 수 있습니다.

using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;

namespace Base64
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //텍스트가 길어질 수 있으니 늘려줍니다.
            //혹은 파일을 읽어도 괜찮겠네요.
            textBox1.MaxLength = 100000000;
        }

        private void button_fileToBase64_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                string filePath = openFileDialog.FileName;
                byte[] fileBytes = File.ReadAllBytes(filePath); //바이트로 파일을 읽어온 후
                char[] fileToBase64 = new char[(int)Math.Ceiling(fileBytes.Length * 1.5)]; //base64는 33% 증가!
                Convert.ToBase64CharArray(fileBytes, 0, fileBytes.Length, fileToBase64, 0); //base64로 만들어줍니다.

                MessageBox.Show(fileBytes.Length + " " + fileToBase64.Length);

                Clipboard.SetText(new string(fileToBase64)); //클립보드에 복사해줍니다. string으로 만들어서!
            }
        }

        private void button_base64ToFile_Click(object sender, EventArgs e)
        {
            string text = textBox1.Text;
            byte[] textBytes = Convert.FromBase64String(text); //위의 string을 다시 base64 byte로 만들어줍니다.

            string folder = Directory.GetCurrentDirectory();
            string filePath = Path.Combine(folder, "file");
            File.WriteAllBytes(filePath, textBytes); //해당 값으로 파일을 만들어줍니다.

            Process.Start(folder); //파일 생성 확인용 폴더 띄워주기
        }
    }
}

+ Recent posts