파일 저장, 불러오기 등에 사용되는 옵션이 있습니다.

 

양식은

(2개 이상 받기)

설명문|파일확장자;파일확장자2

 

(1개씩 받기)

파일명1 | 파일1확장자 | 파일2 | 파일2확장자

형태로 입력하면 됩니다.

 

 

using System.Windows.Forms;

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

        private void button1_Click(object sender, System.EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.Filter = "Tif(Tiff),JPG|*.tif;*.jpg|ALL|*.*";

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                //선택 된 경우!
            }
        }
    }
}

프로젝트에 리소스를 등록하여 사용하는 방법을 알아보겠습니다.

Properties.Resources.  을 이용하여 접근 가능합니다.

 

먼저 완성된 코드와 그림입니다.

using System.Windows.Forms;

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

            label1.Text = Properties.Resources.TestString;
            pictureBox1.Image = Properties.Resources.myImage;
        }
    }
}

 

 

1. 프로젝트 오른쪽 클릭하고 속성을 눌러줍니다.

 

 

2. 리소스 탭을 클릭하면 기본 문자열에 대해 입력이 가능해집니다.

 

3. 문자열 외에도 여러 리소스를 등록할 수 있습니다. (이미지 등록 등은 드래그 앤 드롭해서 사용해주면 됩니다!)

Process.Start 혹은 Process 클래스를 구현하여 사용하여 프로그램 실행이 가능합니다.

아래는 엑셀, 텍스트 문서를 열은 예제입니다.

using System;
using System.Diagnostics;
using System.IO;

namespace ConsoleApp
{    
    class Program
    {
        public static void Main(string[] args)
        { 
            Process process = new Process();

            /* 프로그램 실행 시키고 기다리기.
            Process pro = Process.Start("...");
            pro.WaitForExit();
            */

            Console.WriteLine("[Excel] 프로세스 시작!");

            process.StartInfo.Arguments = Path.Combine(Directory.GetCurrentDirectory(), "test.xlsx");
            process.StartInfo.FileName = "excel.exe";
            process.StartInfo.UseShellExecute = true; //엑셀 실행 시 UseShellExecute 필요!

            process.Start();
            process.WaitForExit();

            //위와 동일한 작업을 합니다!
            //Process pro = Process.Start(new ProcessStartInfo("excel.exe", Path.Combine(Directory.GetCurrentDirectory(), "test.xlsx")) { UseShellExecute = true });
            //pro.WaitForExit();

            Console.WriteLine("[Excel] 프로세스 종료!");

            Console.WriteLine("[notepad] 프로세스 시작!");

            process.StartInfo.Arguments = Path.Combine(Directory.GetCurrentDirectory(), "test.txt");
            process.StartInfo.FileName = "notepad.exe";

            process.Start();
            process.WaitForExit();

            Console.WriteLine("[notepad] 프로세스 종료!");
        }
    }
}

 

결과! (해당 경로에 파일이 있어야 열립니다)

 

FormBorderStyle = None 인 폼은 기본적으로 크기 조절 및 이동이 불가능합니다.

해결해야 할 것은 3가지 입니다.

 

1. 마우스가 테두리에 위치하여 크기를 변경 가능

2. 마우스 이동에 따른 이벤트 추가 (혹은 WndProc에서 제어도 가능!)

3. 최대화 시 작업표시줄을 가리지 않게 수정 (타이틀이 없기에 크기가 더 커집니다.)

 

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

namespace MoveableForm
{
    public partial class Form1 : Form
    {
        private int titleHeight = 32;
        private int padding = 5;

        private int offsetX;
        private int offsetY;
        private bool isMoving;

        //테스트용 출력 값
        private Dictionary<int, string> positionDict = new Dictionary<int, string>()
        {
            {0, "pass" },
            //{1, "clientInner" },
            //{2, "moveable and changeSize" },
            {10, "left" },
            {11, "right" },
            {12, "top" },
            {15, "bottom" },
            {13, "topLeft" },
            {14, "topRight" },
            {16, "bottomLeft" },
            {17, "bottomRight" }
        };

        public Form1()
        {
            InitializeComponent();
        }

        private IntPtr GetMousePosition (Point point)
        {
            int x = point.X;
            int y = point.Y;

            int width = this.ClientRectangle.Width;
            int height = this.ClientRectangle.Height;

            bool innerWidth = padding <= x && x < width - padding;
            bool innerHeight = padding <= y && y < height - padding;

            bool isTop = y <= padding / 2; //타이틀쪽 살짝 보정
            bool isBottom = height - padding <= y;
            bool isLeft = x <= padding;
            bool isRight = width - padding <= x;

            if (isLeft && innerHeight)  return (IntPtr)10; //left
            if (isRight && innerHeight) return (IntPtr)11; //right
            if (isTop && innerWidth)    return (IntPtr)12; //top
            if (isBottom && innerWidth) return (IntPtr)15; //bottom

            if (isTop && isLeft)        return (IntPtr)13; //topLeft
            if (isTop && isRight)       return (IntPtr)14; //topRight
            if (isBottom && isLeft)     return (IntPtr)16; //bottomLeft
            if (isBottom && isRight)    return (IntPtr)17; //bottomRight

            //1 : 클라이언트 내부인 경우.
            //2 : 윈도우창 움직이거나 더블클릭이 가능.
            //return y <= titleHeight ? (IntPtr)2 : (IntPtr)1;

            return (IntPtr)0;
        }

        protected override void WndProc(ref Message m)
        {
            //https://learn.microsoft.com/ko-kr/windows/win32/inputdev/wm-nchittest
            //WM_NCHITTEST = 0x84
            if (m.Msg == 0x84)
            {
                Point point = new Point(m.LParam.ToInt32());
                point = PointToClient(point);
                
                m.Result = GetMousePosition(point);
                label3.Text = positionDict[(int)m.Result]; //테스트용 값입니다.
                return;
            }

            base.WndProc(ref m);
        }

        private void label1_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void panel_title_MouseDown(object sender, MouseEventArgs e)
        {
            if (this.WindowState == FormWindowState.Maximized)
            {
                return;
            }

            offsetX = Cursor.Position.X - Location.X;
            offsetY = Cursor.Position.Y - Location.Y;

            isMoving = true;
        }

        private void panel_title_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMoving)
            {
                Location = new Point(Cursor.Position.X - offsetX, Cursor.Position.Y - offsetY);
            }
        }

        private void panel_title_MouseUp(object sender, MouseEventArgs e)
        {
            isMoving = false;
        }

        private void panel_title_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (this.WindowState == FormWindowState.Normal)
            {
                //화면 최대화 시 하단의 작업표시줄을 덮지 않도록 해줍니다.
                this.MaximumSize = new Size(Screen.FromControl(this).WorkingArea.Width,
                                            Screen.FromControl(this).WorkingArea.Height);

                this.WindowState = FormWindowState.Maximized;
            }
            else
            {
                this.WindowState = FormWindowState.Normal;
            }
        }
    }
}

 

클라이언트 서버 공부 중 나온 궁금증이 있어 정리해봅니다.

 

TestRequestDto 소스 (Setter 가 있어야 값 적용이 가능합니다. - lombok 사용)

@Setter
class TestRequestDto
{
    private String a;
    private String b;
    
    //lombok Setter
    //public void setA (String a) { this.a = a; }
    //public void setB (String b) { this.b = b; }

    public void Show () {
        System.out.println(String.format("a: %s\nb: %s", a, b));
    }
}

 

1. json 으로 통신

클라이언트)

  ContentType = application/json

  data = {

       a : "123",

       b : "456",

    }

 

서버) @RequestBody 가 사용되면 json 통신

@PostMapping("/test")
public Long TestRequest (@RequestBody TestRequestDto requestDto) {
  requestDto.Show(); //결과 값 보려고 만든 함수
  System.out.println("요청 들어왔습니다~~");
  return 1L;
}

 

2. x-www-form-urlencoded 으로 통신

클라이언트)

  ContentType = application/x-www-form-urlencoded

  data = "a=123&b=456"

 

서버) @RequestBody 를 지워줍니다.

@PostMapping("/test")
public Long TestRequest (TestRequestDto requestDto) {
  requestDto.Show(); //결과 값 보려고 만든 함수
  System.out.println("요청 들어왔습니다~~");
  return 1L;
}

 

간단 정리

shallowCopy ( 얕은 복사 - 기본값 ) : 단순 변수는 복사되고

        오브젝트/배열에 대해서는 같은 메모리를 사용         (값을 바꾸면 기존 오브젝트/배열 값도 같이 바뀜)

 

deepCopy ( 깊은 복사 ) : 오브젝트/배열을 새로운 메모리에 할당    (값을 바꿔도 기존 오브젝트/배열 값과 무관)

 

간단한 얕은 복사 예시..

  const zzz = [123, 456, 789];
  const xxx = zzz; //같은 메모리 참조

  zzz[0] = 66; //xxx[0]도 66
  xxx[1] = 876; //zzz[1] 도 876

  console.log(zzz, xxx); 
// zzz = [66, 876, 789],     xxx = [66, 876, 789]  운명을 함께..

 

 

 _ (lodash) 라는 좋은 라이브러리가 있습니다만 직접 구현해봤습니다. (왜 이런거에 관심이 있으까..ㅋ)

 

origin 과 shallow은 서로 값에 영향. deepcopy는 값 영향 x

 

 

const DeepCopy = (target) => {
  //단순 변수는 그냥 반환
  if (!(target instanceof Object)) {
    return target;
  }

  //배열인 경우는 배열 순회 후 반환
  if (Array.isArray(target)) {
    let arr = [];

    for (let i = 0; i < target.length; ++i) {
      arr.push(DeepCopy(target[i]));
    }

    return arr;
  }

  let newValue = {};
  //오브젝트 형태인 경우 모든 키에 대해 순환 후 반환
  for (let key of Object.keys(target)) {
    if (target[key] instanceof Object) {
      // console.log("key : ", key, "target[key] : ", target[key]);
      if (Array.isArray(target[key])) {
        let arr = [];

        for (let i = 0; i < target[key].length; ++i) {
          arr.push(DeepCopy(target[key][i]));
        }

        newValue = {
          ...newValue,
          [key]: arr,
        };
      } else {
        newValue = {
          ...newValue,
          [key]: DeepCopy(target[key]),
        };
      }
    } else {
      // console.log("key : ", key, "target[key] : ", target[key]);
      newValue = { ...newValue, [key]: target[key] };
    }
  }

  return newValue;
};

const DeepCopyTest = () => {
  const origin1 = {
    a: 123,
    target: {
      b: 456,
      target2: [
        {
          c: 789,
          x: [123, 456, 789],
        },
      ],
    },
  };

  const shallowCopy1 = origin1;
  const deepCopy1 = DeepCopy(origin1);

  shallowCopy1.a = 3;
  origin1.target.target2[0].c = 54;
  origin1.target.target2[0].x[0] = 11111;
  origin1.target.b = 88;

  console.log("origin 1 : ", origin1);
  console.log("shallowCopy 1 : ", shallowCopy1);
  console.log("deepcopy 1 : ", deepCopy1);

  const origin2 = [
    {
      v1: 12,
    },
    {
      v2: 56,
      v3: [
        {
          v4: 123,
        },
        123123123123,
      ],
    },
  ];

  const shallowCopy2 = origin2;
  const deepCopy2 = DeepCopy(origin2);

  origin2[0].v1 = 43666;
  origin2[1].v3[0].v4 = 88;
  origin2[1].v3[1] = 543;

  console.log("origin 2 : ", origin2);
  console.log("shallowCopy 2 : ", shallowCopy2);
  console.log("deepcopy 2 : ", deepCopy2);

  return <></>;
};

export default DeepCopyTest;

window.open,

window.addEventListener

window.opener.postMessage

를 통해 아래와 같이 페이지 간 메시지를 주고 받을 수 있습니다.







 

 

부모 페이지 (window.open 호출)

import { useEffect, useState } from 'react';

const PostMessageExample = () => {
  const [msg, setMsg] = useState([]);

  useEffect(() => {
    //다른 웹 페이지를 띄웁니다.
    const w = window.open(
      'http://172.18.144.1:3001',
      'popup',
      'width=500, height=500'
    );

    const listen = (message) => {
      console.log('받은 메시지 : ', message);
      if (message?.data?.msg) {
        setMsg((prev) => {
          return [...prev, message.data.msg];
        });
      }

      //메시지가 종료 플래그면 이벤트를 제거해줍니다.
      if (message?.data?.msg === 'end bye!') {
        console.log('이벤트 제거!');
        window.removeEventListener('message', listen);
      }
    };

    window.addEventListener('message', listen, false);

    //윈도우 닫히는 것을 확인합니다.
    //메시지를 통해서도 제어 가능!
    const timer = setInterval(() => {
      if (w.closed) {
        clearInterval(timer);
      }
    }, 1000);

    return () => {
      console.log('메시지 및 타이머 제거!!');
      if (timer) {
        clearInterval(timer);
      }
      window.removeEventListener('message', listen);
    };
  }, []);

  return (
    <div>
      <div>
        <div>
          {msg.map((item, idx) => {
            return <div key={idx}>{item}</div>;
          })}
        </div>
      </div>
    </div>
  );
};

export default PostMessageExample;

 

 

open 되는 페이지 (window.opener를 통해 open한 곳에 값 전달)

import { useEffect, useState } from 'react';

const Home = () => {
  const [input, setInput] = useState('');

  useEffect(() => {
    //윈도우가 종료 될 때 메시지를 보냅니다.
    window.onbeforeunload = () => {
      //*는 CORS의 origin 에 대한 값
      window.opener.postMessage({ msg: 'end bye!' }, '*');
    };
  }, []);

  return (
    <div>
      <div>
        <input
          type="text"
          value={input}
          onChange={(e) => {
            setInput(e.target.value);
          }}
        />
        <button
          onClick={() => {
            window.opener.postMessage({ msg: input }, '*');
            setInput('');
          }}
        >
          보내기
        </button>
      </div>
    </div>
  );
};

export default Home;

'React > 문법' 카테고리의 다른 글

[React] DeepCopy 직접 구현 (깊은 복사)  (1) 2023.12.30
[Crypto-js] AES256 사용 방법  (0) 2023.10.31

[모듈 없을 시 설치]

npm install crypto-js

 

import crypto from 'crypto-js';
import { useState } from 'react';

const CryptoAES256 = () => {
  const [encrypt, setEncrypt] = useState('');
  const [decrypt, setDecrypt] = useState('');

  const Go = () => {
    const key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456'; //32자리

    const initVector = key.substr(0, 16); //16자리

    const dataObject = {
      value1: 'v1',
      value2: 'v2',
      value3: 'v3',
    };

    const data = JSON.stringify(dataObject);
    console.log(data);

    //암호화
    let cipher = crypto.AES.encrypt(data, crypto.enc.Utf8.parse(key), {
      iv: crypto.enc.Utf8.parse(initVector),
      padding: crypto.pad.Pkcs7,
      mode: crypto.mode.CBC,
    });

    console.log('Encrypt cipher : ', cipher);
    const encryptResult = cipher.toString();
    setEncrypt(encryptResult);

    //복호화
    cipher = crypto.AES.decrypt(encryptResult, crypto.enc.Utf8.parse(key), {
      iv: crypto.enc.Utf8.parse(initVector),
      padding: crypto.pad.Pkcs7,
      mode: crypto.mode.CBC,
    });

    console.log('Decrypt cipher : ', cipher);
    const decryptResult = cipher.toString(crypto.enc.Utf8); //★복호화 후 인코딩 지정!
    setDecrypt(decryptResult);
  };

  return (
    <div>
      <div>
        <button onClick={Go}>암/복호화</button>
        <div>암호화 : {encrypt}</div>
        <div>복호화 : {decrypt}</div>
      </div>
    </div>
  );
};

export default CryptoAES256;

 

클라이언트/서버 대칭 키 알고리즘입니다. (암/복호화에 같은 키 사용)

사용할 일이 있어 간단히 정리해봅니다.

 

AES ( Advanced Encryption Standard ) 128, 192, 256  숫자는 bit를 의미하며 key의 길이가 결정 됩니다.

128 = 128 / 8 = 16 (byte)

192 = 192 / 8 = 24 (byte)

256 = 256 / 8 = 32 (byte)

 

mode :

1. ECB (Electronic Code Block) : 각 블록 단위 별개로 나눠서 암호화.

2. CBC (Cipher Block Chaining) : 앞 뒤 값에 대해 XOR 연산하여 모든 블록이 연관 (iv 개념 필요!)

 

iv (Initialize Vector) : CBC에서 첫 자리에 대해 XOR 연산을 할 값을 의미합니다. (길이가 맞아야 합니다.)

 

padding : 부족한 값 채우는 방식

- 영어와 띄어쓰기만 남기기

value.replace(/[^(a-z )]/gi, "");     //    g는 모든 /  / 정규식에 대하여,   i는 대소문자 구분 제외

 

- 3자리 마다 콤마 찍기 (금액 포맷)

value.replace(/\B(?=(\d{3})+(?!\d))/g,",");

 

- , 콤마 지우기

value.replace(/,/g, "");

 

- 숫자만 남기기

value.replace(/[^0-9]/g, "")

 

- 전화번호 포맷

value.replace(/(^02.{0}|^01.{1}|[0-9]{3})([0-9]+)([0-9]{4})/, '$1-$2-$3');

 

- 보너스

const input ="123";

input.charCodeAt(0); // "1" 에 대한 숫자 49 반환

input.charCodeAt(1); // "2" 에 대한 숫자 50 반환

 

String.fromCharCode(65)       A 반환

클래스, 구조체 등 내부 값이 있는 경우에 IComparer<T> 를 이용하여 비교하여 정렬이 가능합니다.

 

using System;
using System.Collections.Generic;

namespace ConsoleApp
{    
    class Program
    {
        class DescCompare : IComparer<Data>
        {
            //작으면 -1, 같으면 0, 크면 1
            //반대인 경우는 Asc 가능
            public int Compare(Data data1, Data data2)
            {
                return data1.A < data2.A ? -1 : 1;
            }
        }

        class Data
        {
            public int A { get; set; }
            public string B { get; set; }
        }

        static void PrintList (List<Data> datas, string message)
        {
            Console.WriteLine(message);
            foreach (Data data in datas)
            {
                Console.WriteLine(string.Format("{0}) {1}", data.A, data.B));
            }
        }

        public static void Main(string[] args)
        {
            List<Data> datas = new List<Data>();
            datas.Add(new Data { A = 30, B = "Test30" });
            datas.Add(new Data { A = 20, B = "Test20" });
            datas.Add(new Data { A = 10, B = "Test10" });

            PrintList(datas, "정렬 전");
            datas.Sort(new DescCompare());
            PrintList(datas, "정렬 후");
        }
    }
}

 

+ Recent posts