프로그래밍 관련/프로그래밍 관련팁

메시징 구동 방식 메세지 대정리

AlrepondTech 2020. 9. 15. 11:53
반응형



=================================

=================================

=================================

 

 

 

컨트롤과 함께 이걸 한번 정리하고 싶었다.

한번 대정리를 해보도록 하자.

 

1. Action Script 3.0

액션 스크립트부터 하는 이유는 액션스크립트가 2.0에서 3.0으로 넘어가면서 메세지 처리하는 방식이 좀 바뀌었기 때문이다. 언어내부에서 이런 변화가 일어났기 때문에 그에 관한 설명이 액션스크립트책에 꽤나 자세히 방대하게 나와있다. 이 메세징 방법이 전반적인 현재 언어에 다 적용이 되는 것이기 액션스크립트를 공부하면 메세징에 대해 가장 잘 이해할 수 있다.

 

- 내장이벤트 와/ 사용자 정의 이벤트가 있다.

- 이벤트리스너(이벤트발생시 사용되는 함수,메서드)

- 이벤트 객체는 Event 클래스를 상속

- 이벤트를 받는 target이 있는데 이를 EventDispatcher라 한다.

- 이벤트dispatcher는 addeventlistener/removeeventlistener 를 가짐.

 

ex) theURLLoader.addEventListener(Event.COMPLETE, someListener);

이런식으로... 근데 이때 complete라는 이벤트는 스펙에서 확인해야된다.

 

스펙을 확인하다보면 어떤 개체에서 받을수 있는 이벤트가 정해져있고

예를 들어 complete,또 그 이벤트들은 하나의 속성을 가진다. 대략 중요한 4가지요소가 있는데

currentTarget, target 이 두가지는 이벤트가 도달하는 개체를 나타낸다.

왜 두가지냐면 그래픽 속성들은 부모개체를 가리키거나 관련 개체를 가리킬때도 있기 때문이라고 생각한다.

 

디폴트 행동 막기,

1. 이벤트가 cancelable 이어야한다.

2. 리스너에서 preventdefault() 호출

 

** 객체에 이벤트리스너를 등록할때는 항상 해지 코드를 넣는것이 좋다 ** 고 한다.

가비지컬랙션이 있는데 이벤트가 있으면 참조가 끝나고 객체 소멸이 잘 안되는 모양이다. 그럼 불편한데... 일단 지금은 메모리 생각할 때가 아니니 그냥 넘어간다. 이걸 해결하는게 useWeakReference..

 

 

=======================자 여기까지는 이벤트를 받는방법이다=================================

중요한것은 이벤트는 자동으로 발사된다는거다.

위처럼 이벤트리슨너를 등록하든 javascript처럼 이벤트처리기를 이용하든지간에

어떤 개체의 정해진 이벤트는 자동 발사된다. (아닌가? 근데 이벤트를 발생시키는 로직은 위에 없다)

.......................................................................................................................................

 

사용자이벤트는 이벤트를 보내는것도 정의해야된다.(즉 이벤트를 보낼수 있는 체제는 사용자이벤트를 정의할수 있는곳이다.)

방법은 eventdispatch 객체를 상속하거나 인터페이스를 구현하고, 그 객체에 이벤트를 등록하고(상수값) 그 이벤트를 eventdispatch로 발사하는것이다.

dispatchEvent를 한 클래스가 이벤트의 타겟이 된다.

 

??? 이 부분이 이상한데 자기객체가 자기한테 이벤트를 보내서 자기가 처리한다????

사실 dispatchEvent를 한 클래스가 다른 개체가 이벤트를 보낼수 있어야 쓸모가 있는것 아닌가????

 

이 의문에 대해 약간의 해답을 인터넷에서 찾았는데(essential 책에도 없었음)

dispatchEvent의 주어를 받을 객체로 주면된다.

즉 dispatchEvent를 쏴주는 장소가 중요한것이 아니고 개체의 참조가 있으면 그걸 쓰면 된다는말..

======================================================================================

자 이제, AS 자체가 가진 특성 이벤트에 대해 논해보면,(물론 다른 언어와 통용되지만)

그래픽 개체는 부모,자식이 동시에 이벤트를 받는다.

 

1.Capture(부모)->2.Target(자식)->3.Bubble(다시부모로)

 

이 단계이고, capture 옵션을 요리조리 처리해서 이용한다.

 

그밖에 플래시에 특화된것들...

겹쳐진 오브젝트는 보이는것만 이벤트전달, 그러나 보이는개체의 mouseEnabled를 false로 주면 뒷개체에 전달가능

키보드 포커스는 InteractiveObject를 상속받아야한다.

키보드포커스를 마우스로 주는데있어 Sprite와 MovieClip은 기본은 불가함.(buttonMode,tabenable)

 

그리고 다른 플래시 플레이어와의 이벤트 등..

 

2. MFC

MFC를 두번째로 하는 이유는 완전한 메세징 구조인 Window API를 메세지맵이란것을 도입하여 좀 구분하기 쉽게 꾸몄기 때문이다. 그리고 클래스 위자드라는것이 전반적으로 활짝 메세지를 나타내 보이기 때문에 인지하기 좋다. MFC를 바탕으로 Window API도 까보도록 하자.

MFC는 사실 이 메세지보다는 아키텍쳐에 대한 책이 많아서 액션스크립트처럼 메세징구조에 대한 자세한 해설이 없다. 단지 버튼을 더블클릭하면 메세지, 콜백함수가 등록되고 그걸 작성할 뿐이다.

일단 실제적인 예를 나열해보겠다.

 

/*

기본적인 sdi를 만들고 클래스를 보면 mainfrm, app, view, doc 가 있고, 또 dlg도 하나 나온다.

요걸 한번 분석해본다.

 

app와 doc 는 일단 연결할수 있는 메세지가 없다.

왜그럴까... 클래스위자드가 판단하는건 아마 derived 일수있으니 그걸 보면

app는 CWinAppEx, doc는 CDocument 이다. mainfrm은 CFrameWndEx, view는 CView 이다.

그러면 임의의 클래스를 4개 만들겠다.위의 derived로...

그러면 네개의 클래스중 CFrameWndEx, CView만 메세지가 나온다.

 

일단 결론은 보이는 클래스만 메세지가 들어가있는듯하다....

 

그러면 CFrameWndEx, CView의 메세지들을 확인한다. 이글은 긴그르..

*/

 

MFC는 기본적으로 메세징방식이 WinAPI와 같다. 같은 언어를 쓰므로, 메세지를 보내는것은 SendMessage, 받는것은 WinProc 콜백을 쓰면 될것이다.

새로 만들어진 개체들은 어떻게 하느냐... 재정의를 쓰면 된다. 즉 재정의를 하면 WinProc을 새로 만들수 있고 거기에 받은 메세지를 처리하면 된다.

재정의를 하지 않는다면 SetWindowlong 이란 함수를 쓰면 된다는데, 사실 이름이 애매모호한 함수라 잘 모르겠다. 이 함수만 정리하고 끝내도록 하겠다.

 

SetWindowLong

LONG SetWindowLong( HWND hWnd, int nIndex, LONG dwNewLong);

▶hWnd : 속성을 변경하고자 하는 윈도우의 핸들

▶nIndex : 변경하고자 하는 속성을 지정하며 다음 중 하나의 값을 줄 수 있다.

설명
GWL_EXSTYLE 확장 스타일
GWL_STYLE 윈도우 스타일
GWL_WNDPROC 윈도우 프로시저의 번지
GWL_HINSTANCE 인스턴스 핸들
GWL_ID 윈도우의 ID
GWL_USERDATA 윈도우와 관련된 사용자 데이터
DWL_DLGPROC 대화상자 프로시저의 주소
DWL_MSGRESULT 대화상자 프로시저의 리턴값
DWL_USER 사용자 데이터

또는 윈도우에 여분 메모리가 있을 경우 여분 메모리의 오프셋을 지정할 수도 있다. 이 값은 반드시 양수여야 하며 cbWndExtra-4보다는 작아야 한다. 예를 들어 여분 메모리가 16바이트 지정되어 있으면 nIndex는 0~12까지 지정할 수 있다.

▶dwNewLong : 새로 변경할 32비트값이며 nIndex에 따라 값의 의미는 달라진다.

 

뭐 이런 설명인데 아주 다용도로 쓰는 함수인듯.

 

3. Window API

결과적으로는 MFC와 같은 방향을 띄나 오히려 메세지 부분은 MFC를 먼저 보는쪽이 더 좋은것 같다. 왜냐면 Window API는 다 비슷비슷한 방식으로 처리하여, 구분지어 생각하기가 힘들기 때문이다.

 

윈도우프로시저는 윈도우클래스당 하나씩 배정된다.

메세지의 구조

typedef struct tagMSG

{HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt;} MSG; // hwnd는 메세지를 받을윈도우, UINT는 메세지 종류이다.

 

이 메세지가 윈도우프로시저로 보내져서 사용된다.

 

WA는 이벤트 개념은 아니고, 메세지 개념이다. 이게 뭔고 하니 액션스크립트같은 경우는 어떤 이벤트가 발생되었다고 한다면, 그 사이에서 통신이 흐르는것은 상관하지 않는다. 그러나 WA는 아무래도 시스템쪽과 연관이 되어있다보니 SendMessage라는 함수를 통해 수시로 서로 메세지를 보낸다. 이것이 dispathevent 개념일수도 있는데, 다시 말하면 메세지 다수가 모여 이벤트 하나를 이룰수도 있다. 즉 캡쳐,타겟,버블 단계는 여기선 SendMessage로 서로 서로 연락을 취하면서 구현할수 있다는 말이다. 사용자가 만들수 있는 메세지는 WM_USER 부터 간단하게 정리되어있다.

 

이벤트의 개념이 없어서인지 WA는 그런 설명은 없고, 저수준의 메세지 처리, 메세지 처리함수, 메세지큐에서의 메세지 처리 방법등 다양하고 광범위한 메세지 이용을 설명하고 있다.

 

WM_ACTIVATE

- LOWORD(wParam) : 윈도우가 활성화되었는지 비활성화 되었는지를 표현한다.

WM_ACTIVATEAPP

- wm_activate는 응용프로그램 내부에서 창이동같은것이고 위의 메세지는 다른 응용프로그램으로 이동할때 발생한다.

 

WM_APP

응용프로그램 사용자정의 메세지, WM_USER와 조금 차이가 있는듯...

WM_CHAR 문자키가 눌러졌을때

WM_CLEAR 에디터,콤보박스로 보내 내용을 삭제시킴

WM_CLOSE 윈도우가 닫히지전 호출

 

WM_COMMAND

1.메뉴, 액셀러레이터를 선택했을 때 이 메시지가 전달되며

2.차일드 컨트롤이 부모 윈도우로 통지 메시지를 전달할 때도

LOWORD(wParam) : 통지 메시지를 보낸 항목의 ID이다. 메뉴의 ID, 액셀러레이터의 ID 또는 컨트롤의 ID가 전달된다.

HIWORD(wParam) : 컨트롤이 이 메시지를 보낼 때는 통지 코드가 전달된다. 통지 코드의 종류는 에디트, 리스트 박스 등의 컨트롤에 따라 다양하다. 메뉴 항목이 선택된 경우 이 값은 0이며 액셀러레이터가 선택된 경우 이 값은 1이다.

lParam : 통지 메시지를 보낸 컨트롤의 윈도우 핸들이 전달된다. 메뉴나 액셀러레이터로부터 이 메시지가 전달된 경우 이 값은 NULL이다.

 

WM_COPY 복사시 메세지

 

WM_CREATE

CreateWindow(Ex) 함수에 의해 윈도우가 생성될 때 보내진다. 메모리에 윈도우를 생성한 후 화면에 보이기 전에 보내지며 주로 윈도우에 관련된 초기화 작업을 할 때 사용된다. 윈도우 동작을 위한 메모리 할당, 리소스 생성, 차일드 컨트롤 생성, 윈도우 속성 초기화 작업에 이 메시지가 사용된다.

CreateWindow(Ex) 함수는 이 메시지를 완전히 처리한 후에 리턴한다. 만약 이 메시지 처리중에 차일드 윈도우를 생성했다면 각 차일드 윈도우로도 WM_CREATE 메시지가 전달되어 개별적인 초기화를 한다. 인수로 전달되는 LPCREATESTRUCT 구조체는 보통 사용하지 않으며 무시하나 이 구조체의 lParam 멤버는 CreateWindow 함수의 제일 마지막 인수를 전달하며 윈도우로 사용자 정의값을 전달하고자 할 때 사용할 수 있다.

참고:대화상자는 이 메시지 대신 WM_INITDIALOG 메시지를 받는다.

 

WM_CTLCOLORBTN

WM_CTLCOLORDLG

WM_CTLCOLOREDIT

WM_CTLCOLORLISTBOX

WM_CTLCOLORSCROLLBAR

WM_CTLCOLORSTATIC 그리기전 호출

 

WM_CUT 잘라내기때 메세지

WM_DEADCHAR 유럽어 사용시, 한국X

 

WM_DESTROY 윈도우가 파괴될 때 이 메시지가 전달된다

이 메시지를 받은 윈도우는 윈도우의 종료를 위한 처리를 해야 하는데 예를 들어 열어 놓은 파일을 닫고 할당한 메모리를 해제하는 등의 정리 작업을 한다. WM_CREATE에서의 초기화 처리의 반대 동작이 이 메시지에 작성되는 것이 일반적이며 그외 레지스트리에 미보관 정보를 저장하는 등의 작업을 할 수 있다. 만약 파괴되는 윈도우가 클립보드 체인에 속해 있으면 자신을 클립보드 체인에서 제거해야 한다.

DestroyWindow 함수는 파괴할 윈도우를 화면에서 숨긴 후 이 메시지를 보내므로 이 메시지를 받은 시점에서는 윈도우 자체가 파괴되지 않은 상태이다. 또한 DestroyWindow 함수는 자식 윈도우에게도 이 메시지를 차례대로 보내주는데 부모 윈도우가 먼저 이 메시지를 받고 자식 윈도우에게로 이 메시지가 보내진다. 따라서 부모 윈도우가 이 메시지를 처리하는 동안은 모든 자식 윈도우가 아직 파괴되기 전이므로 자식 윈도우를 프로그래밍할 수 있다.

파괴되는 윈도우가 메인 윈도우일 경우 PostQuitMessage 함수를 반드시 호출하여 프로세스의 메시지 루프를 종료하도록 해야 한다. 만약 이 처리를 생략하면 윈도우만 파괴되고 메시지 루프는 계속 실행중인 상태가 되므로 프로세스가 종료되지 않는다.

 

WM_DRAWITEM 오너 드로우 버튼, 리스트 박스, 콤보 박스, 메뉴가 그려져야 할 필요가 있을 때 오너 윈도우에게 이 메시지가 전달된다

WM_DROPFILES DragAcceptFiles 함수로 파일을 드롭받겠다고 등록한 윈도우로 파일이 드롭될 때 이 메시지가 보내진다

WM_ENDSESSION

운영체제는 종료되기 전에 실행중인 모든 프로그램에게 WM_QUERYENDSESSION 메시지를 보내 종료 허가를 받는다. 각 프로그램이 종료를 허가하면 WM_ENDSESSION 메시지를 보내 운영체제가 종료된다는 사실을 알려준다. 즉 이 메시지를 받았을 때는 이미 셧다운이 결정된 상태이며 더 이상 운영체제 종료를 거부할 수 없다.

 

WM_ERASEBKGND 윈도우 크기 변경되었거나, 다른 윈도우에 가려진 부분이 드러났다거나 할 때 배경을 지우기 위해 이 메시지가 보내진다.

 

WM_FONTCHANGE  시스템의 폰트 구성이 변경되면 모든 탑 레벨 윈도우에게 이 메시지가 전달된다

WM_GETDLGCODE  메시지는 대화상자내의 컨트롤들에게 어떤 종류의 입력을 원하는지 질문하기 위해 보내진다

WM_GETFONT 컨트롤에 설정된 폰트를 조사한다

WM_GETMINMAXINFO 이 메시지는 윈도우의 크기나 위치가 변경되기 직전에 윈도우에게 보내진다.

WM_GETTEXT 이 메시지는 윈도우의 텍스트를 조사하기 위해 사용된다

WM_GETTEXTLENGTH 윈도우의 텍스트 길이를 조사한다

WM_HSCROLL 윈도우의 아래쪽에 부착되는 표준 수평 스크롤 바, 또는 SBS_HORZ 스타일을 가지는 수평 스크롤 바 컨트롤이 부모 윈도우로 스크롤 메시지를 보낼 때 이 메시지가 전달된다. 다른 컨트롤은 자신의 변화를 WM_COMMAND로 전달하지만 스크롤 바는 WM_COMMAND 대신 WM_HSCROLL, WM_VSCROLL 메시지를 보낸다. 또한 이 메시지는 트랙 바 컨트롤에 의해 사용되기도 한다.

 

WM_INITDIALOG

이 메시지는 대화상자가 메모리에 만들어지고 화면에 보이기 직전에 보내진다. 그래서 대화상자내의 모든 컨트롤을 참조할 수 있으며 아직 대화상자가 보이기 전이므로 컨트롤의 재배치, 생성, 삭제, 속성 변경 등을 자유롭게 할 수 있다. 오버랩드 윈도우의 WM_CREATE에 해당하는 함수이며 대화상자가 가장 먼저 받는 메시지이므로 주로 대화상자 초기화에 이 메시지가 사용된다.

 

WM_INITMENU

사용자가 메뉴 바의 메뉴를 클릭하거나 메뉴 키를 눌러 메뉴가 열리기 직전에 이 메시지가 보내진다. 응용 프로그램은 이 메시지를 받았을 때 메뉴 항목에 대한 초기화나 수정을 한다. 선택된 메뉴 항목에 체크 표시를 하거나 사용 금지된 메뉴 항목을 Disable시킬 수 있으며 추가로 더 필요한 메뉴 항목을 AppendMenu 등의 함수로 만들 수 있다.

이 메시지는 메뉴가 활성화될 때 딱 한번만 보내지며 메뉴 바의 팝업 메뉴를 옮겨 다녀도 추가적인 메시지는 발생하지 않는다. 메뉴 항목에 대한 정보는 별도로 제공하지 않으므로 직접 구해서 사용해야 한다.

 

WM_INITMENUPOPUP

드롭 다운 메뉴나 서브 메뉴가 열리기 직전에 보내진다. 이 메시지를 받았을 때는 아직 메뉴가 화면에 출력되기 전이므로 응용 프로그램은 메뉴를 수정할 수 있다. 각각의 팝업 메뉴에 대해 이 메시지가 전달되므로 전체 메뉴를 수정하지 않고도 원하는 서브 메뉴만 수정하고 싶을 때 이 메시지를 사용한다.

 

WM_KEYDOWN

키보드 포커스를 가진 윈도우에서 키보드를 누를 때 이 메시지가 전달된다. 단, Alt키와 함께 키를 눌렀을 때는 이 메시지 대신 WM_SYSKEYDOWN 메시지가 전달된다

WM_KEYUP

눌러진 키가 떨어질 때 이 메시지가 발생한다. 키보드를 계속 누르고 있다가 뗀 경우 반복 기능에 의해 여러번의 WM_KEYDOWN이 발생하므로 반드시 이 메시지가 WM_KEYDOWN과 일대일로 대응되는 것은 아니다

 

WM_KILLFOCUS

키보드 포커스를 잃은 직후에 이 메시지가 전달된다. 이 메시지를 받았을 때는 이미 키보드 포커서가 이동 완료된 후이다. 주로 캐럿 처리를 위해 이 메시지를 프로그래밍하는데 이 메시지를 받았을 때 캐럿을 파괴한다. 이 메시지를 받았을 때 출력 함수나 활성화 상태를 변경하는 함수를 호출해서는 안된다.

키보드 포커스는 키보드 입력을 받을 수 있는 상태를 가리키며 한번에 하나의 윈도우만 포커스를 가질 수 있다. 포커스가 이동될 때는 포커스를 잃는 윈도우에게 WM_KILLFOCUS 메시지가 먼저 전달되며 이어서 포커스를 얻는 윈도우에게 WM_SETFOCUS 메시지가 전달된다.

 

WM_LBUTTONDBLCLK
WM_LBUTTONDOWN
WM_LBUTTONUP 

 

WM_MENUSELECT

사용자가 메뉴 항목을 선택할 때 메뉴의 소유자에게 보내진다. 이때 선택이란 메뉴 항목을 클릭한 것을 의미하는 것이 아니며 메뉴 항목 위로 마우스 커서나 반전 막대가 움직이고 있다는 뜻이다.

 

WM_MOUSEACTIVATE

사용자가 비활성화된 윈도우에서 마우스 버튼을 누를 때 이 메시지가 전달된다

WM_MOUSEMOVE

WM_MOVE 윈도우 위치 변경

 

WM_NCACTIVATE 비작업 영역이 활성화 또는 비활성화되어 변경되어야 할 필요가 있을 때 보내진다

WM_NCCREATE CreateWindow(Ex) 함수에 의해 윈도우가 만들어질 때 보내진다. 비작업 영역이 만들어진다는 의미를 가지고 있으며 WM_CREATE보다 먼저 이 메시지가 보내진다. 윈도우가 만들어질 때 가장 먼저 보내지는 메시지이며 윈도우가 제일 먼저 받는 메시지이기도 하다. 그러나 이 메시지는 일반적으로 사용되지 않으며 초기화를 할 필요가 있을 때는 통상 WM_CREATE 메시지가 대신 사용된다

 

WM_NCDESTROY

비작업 영역이 파괴될 때 보내진다. 윈도우와 그 차일드들이 먼저 파괴된 후에 비작업 영역이 파괴되므로 이 메시지는 윈도우가 가장 마지막으로 받는 메시지이다. WM_DESTROY보다 뒤에 발생되며 이 메시지를 받았을 때는 모든 차일드가 이미 파괴된 후이다. 반면 WM_DESTROY 메시지는 차일드가 아직 파괴되기 전이다. 종료 처리가 필요할 경우는 일반적으로 WM_DESTROY 메시지에 코드를 작성하므로 이 메시지는 실용적인 가치가 거의 없는 셈이며 처리하는 경우가 극히 드물다.

 

WM_NCHITTEST

마우스를 이동하거나 버튼을 누르거나 놓을 때마다 이 메시지가 발생한다

WM_NEXTDLGCTL 대화상자 컨트롤의 포커스를 이동시킨다.

WM_NULL 무동작 메세지

 

WM_NOTIFY 자식윈도우가 자신에게 일어난 여러가지 상황을 부모윈도우에게 전달할때 사용

 

WM_PAINT

WM_PASTE

WM_QUERYENDSESSION 사용자가 운영체제를 종료 또는 로그 오프하고자 할 때, 또는 응용 프로그램이 ExitWindows 함수로 운영체제를 종료하고자 할 때(단, EXW_FORCE 플래그가 없어야 한다)모든 응용 프로그램으로 이 메시지가 보내진다

WM_QUIT
응 용 프로그램을 종료하라는 신호이다. PostQuitMessage 함수 호출에 의해 발생하며 GetMessage 함수가 0을 리턴하도록 함으로써 메시지 루프를 종료시키는 역할을 한다. GetMessage 함수는 WM_QUIT 이외의 모든 메시지에 대해 0이 아닌 값을 리턴하므로 계속 루프를 돌지만 WM_QUIT에 대해서만 0을 리턴한다. 그래서 메시지 루프는 통상 다음과 같이 작성된다.

 

WM_RBUTTONDOWN
WM_RBUTTONUP

WM_SETCURSOR 커서가 윈도우 영역에서 이동될 때마다 이 메시지가 보내지며 새 위치에서 커서를 어떤 모양으로 바꿀 것인가를 질문한다. 단, 커서가 캡처되어 있을 때는 이 메시지가 보내지지 않는다.

 

WM_SETFOCUS 이 함수는 키보드 포커스가 이동될 때 발생한다.

WM_SETFONT 컨트롤의 폰트를 변경한다

WM_SETTEXT 윈도우의 텍스트를 변경한다

WM_SHOWWINDOW 윈도우의 보임 상태가 변경되기 직전에 보내진다. 즉 숨겨져 있던 윈도우가 보이게 되었거나 보이던 윈도우가 숨겨지게 되었을 때 이 메시지가 보내진다

WM_SIZE 윈도우의 크기가 변경될 때 이 메시지가 보내진다

WM_SYSCHAR WM_SYSKEYDOWN 메시지가 TranslateMessage 함수에 의해 문자 코드로 번역될 때 포커스를 가진 윈도우에게 전달된다.

 

WM_SYSCOMMAND

시스템 메뉴에 있는 메뉴 항목을 선택하면 WM_COMMAND 메시지 대신 이 메시지가 전달된다. 시스템 메뉴를 직접 선택하는 동작 외에도 타이틀 바에 있는 최대, 최소, 닫기 버튼 등의 명령들도 이 메시지를 발생시킨다. 시스템 메뉴에 있는 명령들은 윈도우를 관리하기 위한 기본적인 명령이므로 응용 프로그램은 이 메시지를 직접 처리하지 않고 보통 DefWindowProc으로 그냥 보내 준다.

 

WM_SYSDEADCHAR
WM_SYSKEYDOWN
WM_SYSKEYUP

 

WM_TIMECHANGE 시스템 시간이 변경되면 이 메시지가 보내진다

WM_TIMER
WM_UNDO
WM_USER

 

WM_VSCROLL
WM_WINIINICHANGE 이 메시지는 하위 호환성을 위해서만 제공되므로 Win32 응용 프로그램은 이 메시지 대신 WM_SETTINGCHANGE 메시지를 대신 사용해야 한다. 이 두 메시지는 이름만 다른 같은 메시지이다.

 

결론은 SendMessage와 메시지 루프로 통신한다는 것.

 

4. 닷넷

WinApi 시리즈는 액션스크립트나 자바스크립트같은 w3c 모델이 아니다. 이벤트의 개념은 없고 메세지의 개념이 있다.  거의 흡사한듯 하고 동일하게 쓴다고 하는데, 개인적으로 느끼는것은 메세지는 저수준의 짧막한 알림의 개념이라면 이벤트는 큰 일덩어리의 시작 개념정도???

메세지가 모여서 이벤트를 구성한다고 말할수도 있겠다.1대1일수도 있고...

닷넷은 w3c기준이 아니라서 그런지 일반적인 listener/dispatch 개념은 아니다.

 

델리케이트는 대표자란 뜻인데, 간단하게 보면 함수 포인터이다. 함수를 가리킬수 있다는 말이다.

public delegate void deleA(int a,string b,double c); 선언문

델리케이트는 클래스와 동등자격이므로 클래스 외부에 둘수 있다.

사용문,

이게 헛갈리는데 계속적으로 반복해서 확실히 익히는게 좋겠다.

public static method(int a,string b,double c){}

deleA dd = new deleA(method); //생성

그냥 dd=method 같이 했으면 덜 헷갈릴텐데, 생성하는 방식이 함수모양이라 이게 뭔지 계속헷갈린다.

그리고 dd(1,'1',1.1); 이런 식으로 사용.

 

델리케이트가 이벤트에 중요한것은 아니다. 이벤트 타입은 어떤 델리케이트 타입을 기본으로 만든다.

 

public event deleA Event1;

 

이처럼 선언하면 위 deleA 델리케이트 타입의 이벤트가 된다. 이벤트도 델리케이트과 사용모양이 흡사하나 하나 차이가 있다..

안쪽을 들어야보면 add와 remove를 제공하는데 이는 +=, -= 연산자를 제공한다. 이는 액션스크립트의 addEventListener, removeEventListener와 동일한 기능을 한다.

그러나 핵심적인 차이가 있는데, AS나 JS의 이벤트는 하나의 스트링같은것으로 대표되고 나타나는 반면에 닷넷의 이벤트는 함수같은 것이다.(위에 설명한데로....)

이게 뭘뜻하냐면 AS나 JS는 이벤트를 발생시키고 이를 처리하는 식이라면,

닷넷은 그냥 이벤트가 처리된다.

 

어렵지 않은 내용인데... 즉 발생시키는 dispatch, fire 개념이 없다. 그냥 사용자 정의 이벤트를 발생시킬때는 그 객체의 이벤트를 실행시켜버린다. 보통 On~~ 식의 메서드로 발생시킨다.

(그냥 이벤트고 어쩌고 할 필요없이 함수실행과 유사하다)

 

만약 사용자 정의 이벤트를 발생시키고 받으려면 보낼려는 객체에 이벤트를 사용자 정의 하고

그 이벤트에 받는 객체의 처리함수를 넣어주면 될것이다.

 

delegate void customEventHandler(object s);

object sender,receiver;

...

//센더의 어딘가 sendEvent를 customEventHandler 타입으로 생성하고

...

sender.sendEvent = new  customEventHandler(receiver.receiveEvent);

...

//이렇게 해준후에 sender의 OnEvent 메서드에서 다시 sendEvent(this); 를 호출하면 자동으로 receiver의 receiveEvent 가 실행될 것이다.

 

(위 코드는 실행코드가 아님)

 

뭐 어차피 w3c 스타일도 이벤트를 받는 객체의 참조가 필요해서 동작은 같겠지만,

 

굉장한 불만은 가독성이 엄청 떨어진다는 것이다. (사실 그냥 메서드 호출과 같다)

 

닷넷도 신형 언어라면 신형언어라 할수 있는데 좀 이렇게 만든건 불만이다.

 

이런 스타일이 아니고 다른 방법이 있는지 모르겠는데 이렇게 결론을 내린건 msdn에 나와있기 때문.

 

http://msdn.microsoft.com/ko-kr/library/wkzf914z.aspx

 

아래의 예제를 보는게 낫겠다.

 

5. Javascript

자바스크립트는 아주 편하게 접하는 메세징 처리 언어이다. 아마 접근하기는 가장 쉬울 것이나, 정리가 잘 안되어있어 나중에 깊숙이 이해하기는 좀 더 힘들다. 깊숙이 알기 위해서는 위는 메세징 방식을 알고 보는 것이 더 좋을 것이다. 방식은 좀 옛날식이다. javascript 명세 2.0이 나오면 아마 좀더 표준적인 방식으로 갈 것이다.

자바스크립트는 표준이 여러가지로 되어있다. 확실한 역사는 모르지만, 일단 첫 브라우저인 네스케이프가 기본 모델을 정의했고, 다른 브라우저가 그 기능은 따라갔는데, 다른 기능을 따로 따로 추가되었다. 특이한 점은 대세적 형식은 인터넷 익스플로어가 쓰지 않는다는 말이다.

그게 좀 어렵게 되는 이유인데 표준으로 삼자는 방식을 영향력이 큰 MS가 쓰질 않으니 둘다 배워야 된다는 말.... 여기서는 IE(인터넷 익스플러어 버전8이다)와 불여우(파이어폭스 버전4인다) 브라우저 크게 두가지로 테스트를 해보도록 하겠다.

기본 이벤트는 설명 제외하겠다. 엘리먼트의 이벤트 처리기에 등록 시키면 된다.

<img src='a.jpg' onerror="javascript:alert('go');">

이와 같은 식이다. (예제를 실행시키려면 a.jpg가 없어야된다) 엘리먼트에 이벤트 처리기가 결합되어있고 이 이벤트 처리기는 엘리먼트 특성에 따라 다르다.

 

DOM Level 2

브라우저/이벤트 모델의 중요한 표준이다. 브라우저사들이 모여 만든 것으로 비록 IE가 다르긴 하지만 핵심 기능들은 유사성을 띈다.

액션 스크립트 처럼 이벤트 전파 기능이 있다. 즉 조상객체도 이벤트를 받는다는것.

 

stopPropagation() => 이벤트 전파를 막는다.

preventDefault() => 이벤트의 기본 행동을 막는다.

이걸 보면 액션 스크립트와 유사하다는걸 알수 있다.

 

addEventListener

위와 같은 이벤트를 등록해보면

<img id=xxx src=a.jpg />
<script>
    document.xxx.addEventListener("error", function (e) { return alert('aaa'); }, false);
</script>

근데 이는 파이어폭스 O, IE 안된다.

IE는 이벤트가 이벤트 객체마다 전달되는게 아니고 전역이벤트가 있다. window.event

document.xxx.attachEvent("onerror",function(){alert('aaa');});

위와 같이 IE에서는 처리한다.

여기서 차이는 직접받는 이벤트 e가 없고(대신 전역), 캡쳐,버블단계가 없다.(이는 다르게 처리한다)

 

 

 

반응형

 

728x90

 

 

 

removeEventListener는 같은 모습으로 사용한다. 위의 익명함수는 제거가 안된다. 다른 방법으로

IE detachEvent

 

전달되는 e

Event 인터페이스

type, target, currentTarget, eventPhase,timeStamp,bubbles,cancelable

UIEvent 인터페이스

view,detail

MouseEvent

Button, altKey,ctrlKey,metaKey,shiftKey,clientX,clientY,screenX,screenY,relatedTarget

 

IE Event 객체

type, srcElement, button,clientX, clientY, offsetX, ofsetY, altKey/strlKey/shiftKey, keyCode, fromElement,toElement, cancelBubble, returnValue

 

이벤트 발생시키기

dispatchevent,

IE fireevent

 

사용자 정의 이벤트

document.createevent,

initEvent( 'type', bubbles, cancelable )

initUIEvent( 'type', bubbles, cancelable, windowObject, detail )

initMouseEvent( 'type', bubbles, cancelable, windowObject, detail, screenX, screenY,

clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget )

initMutationEvent( 'type', bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange )

 

IE document.createEventObject

==========================================================================================

객체 혹은 윈도우간 메세지 전송(주고받기)

 

객체가 기본이 되는 actionscriopt, javascript, 닷넷은 객체끼리 WA,MFC는 윈도우끼리...

 

*****************************************************************************************

Java awt, Swing

 

자바는 이벤트에 관한 전반적인 이해는 아니고 추가적으로 해볼셈인데, awt, Swing이 그래픽 환경의 기본이다.

 

액션은 아래와 같이 전개된다.

 private class AboutActionListener implements ActionListener {
  public void actionPerformed(ActionEvent e){
   String[] mesg = {
     "JNotepad v 0.1",
     "Author: 전용준" 
   };
   JOptionPane.showMessageDialog(
   JNotepad.this,
   mesg,
   "About JNotepad",
   JOptionPane.INFORMATION_MESSAGE
   );
  }
 }

ActionListener에 대한 인터페이스를 정의해서 특정 액션리스너를 만들었다.

그리고 actionPerformed라는 메서드를 정의했는데, 그안에 showMessageDialog 메서드를 실행시키는걸로 봐서 actionPerformed라는 메서드가 자동 실행되는것으로 생각할수 있다.(재정의된것으로)

 

 

 

*****************************************************************************************

 

 

1, ActionScript

좌가 teacher, 우가 student 라는 무비클립을 만들었다.

간단히 1프레임에 script를 넣어보면

stop();

teacher.addEventListener(MouseEvent.CLICK, Question);

function Question(event:MouseEvent):void
{
 trace("예 ㅇㅇ아");
 student.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}

student.addEventListener(MouseEvent.CLICK, Answer);

function Answer(event:MouseEvent):void
{
 trace("네");
}

 

좌클릭시는 선생이 부르면 학생이 대답하고, 우클릭시는 그냥 대답만 한다.

여기서 dispatchEvent 구문은 어떤 특정한 이벤트를 그냥 불러준다고 볼수 있다. 여기는 마우스 클릭을 이벤트로 줬는데 사실 이런 프로그램은 문제가 있다.

 

뭐냐면, 이 프로그램의 의도는 선생이 부를때 학생이 대답하게 하는 어떤 특정한 루트이다.

그러나 시스템에서 주는 이벤트 처리는 다른 이벤트에 포함되어야 하므로 '학생이 그냥 대답' 만 하는 필요없는 구문이 생긴 것이다.

 

이걸 해결할려면 사용자 정의 이벤트가 필요할것 같다.

package
{
 import flash.events.Event;
 public class CustomEvent extends Event
 {
  public static const ANSWER:String = "answer";
  public function CustomEvent(type:String, bubbles:Boolean = false,
         cancelable:Boolean = false)
  {
   super(type,bubbles,cancelable);
  }
 }
}

그러면 다음과 같은 customevent 클래스를 만들면 된다. 이는 event라는 주어진 클래스의 확장이다.

static 상수 ANSWER가 있는데 이는 아마 이벤트의 종구분자일것이다.

 

그러면 위의 소스는 아래와 같이 바꿔주면 된다.

 

import CustomEvent;
stop();
teacher.addEventListener(MouseEvent.CLICK, Question);

function Question(event:MouseEvent):void
{
 trace("예 ㅇㅇ아");
 student.dispatchEvent(new CustomEvent(CustomEvent.ANSWER));
}

student.addEventListener(CustomEvent.ANSWER, Answer);

function Answer(event:CustomEvent):void
{
 trace("네");
}

 

2. 윈도우API

윈도우 API는 이벤트 개념은 없고 raw 메세지를 던지고 받고 한다.

프로그램 자체가 많이 까발려주기 때문에 수신/착신은 좀 쉽게 이해된다.

 

 

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
   ,LPSTR lpszCmdParam,int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;
 
 WndClass.cbClsExtra=0;
 WndClass.cbWndExtra=0;
 WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
 WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
 WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance=hInstance;
 WndClass.lpfnWndProc=(WNDPROC)WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW | CS_VREDRAW;
 RegisterClass(&WndClass);

 WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
 WndClass.hbrBackground=NULL;
 WndClass.lpfnWndProc=(WNDPROC)ChildProc;
 WndClass.lpszClassName=TEXT("ChildCls");
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 RegisterClass(&WndClass);

 hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
  400,300,190,190,
  NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);
 hWndMain=hWnd;
 
 while(GetMessage(&Message,0,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 TCHAR Mes[]=TEXT("");
 static HWND hP1,hP2,hP3;

 switch(iMessage) {
 case WM_CREATE:
  hP1=CreateWindow(TEXT("ChildCls"),TEXT("student"),WS_POPUP | WS_VISIBLE | WS_CAPTION,
   600,300,190,190,hWnd,(HMENU)NULL,g_hInst,NULL);

  return 0;

 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

LRESULT CALLBACK ChildProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

 

간단하게 부모창과 팝업창을 teacher, student로 생성했다.

 

사용자 정의 Message가 따로 정의되어 있으니 그걸 정의한다.

 

#define WM_QUESTION WM_USER+1

줄 이벤트를 정의하고,

 

 case WM_LBUTTONDOWN:
  MessageBox(hWnd,TEXT("야 ㅇㅇ야"),TEXT("알림"),MB_OK);
  SendMessage(hP1,WM_QUESTION,0,0);
  return 0;

 

마우스버튼시 메세지 전달

 case WM_QUESTION:
  MessageBox(hWnd,TEXT("네"),TEXT("알림"),MB_OK);
  return 0

 

그리고 받아처리한다. 명약관하하다.

 

3. MFC

MFC는 내부적으로 WinAPI와 같으나 클래스로 생각해야되는게 다르다.

SDI를 하나 만들고 거기에 두개의 자식 윈도우를 만들어 이벤트를 넘겨보겠다.

 

 

메인프레임의 OnCreate 뒤에

 CWnd* teacher;
 CWnd* student;
 CString strMyClass = AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW,NULL,(HBRUSH)GetStockObject(GRAY_BRUSH),NULL);
 teacher = new CWnd;
 teacher->Create(strMyClass,TEXT("teacher"),WS_OVERLAPPEDWINDOW|WS_CHILD|WS_VISIBLE,CRect(250,180,440,370),this,WTEACHER,NULL);
 student = new CWnd;
 student->Create(strMyClass,TEXT("student"),WS_OVERLAPPEDWINDOW|WS_CHILD|WS_VISIBLE,CRect(450,180,640,370),this,WSTUDENT,NULL);

소스를 추가시켜 윈도우 두개를 만들었다.

 

사실 지금 이 객체들 사이에 메세지루프와 sendmessage를 어떻게 보내는지 모른다.

(MFC가 참 난해한면이 있다)

일단 상상을 해보면서 하나씩 해보겠다.

 

일단 msdn을 넘기니, CWnd::OnLButtonDown 이라는 메서드가 보인다.

아마 클릭시 처리메서드인것 같다.

그러면 msdn을 보며 집어넣어준다. 그러나 특별히 처리하는 구문이 없다.

이 메서드를 보아하니 메세지맵에 넣는 재정의되는 함수와 같다. 그렇다. 메세지맵을 쓰기 위해서는 재정의가 필요할것 같다.

CTeacher, CStudent라는 CWnd 재정의 클래스를 다시 만들었다.

 

그러면 클래스 위자드에서 CTeacher 의 WM_LBUTTONDOWN을 생성한다.

WM_QUESTION을 정의하고

SendMessage를 하려는데 student의 윈도우핸들이 없다...

 

이런게 MFC가 난해한 점인데, 같은 WinAPI에서는 쉽던것이 처리하기 힘드니...

 

몇몇 검색을 해서 window 핸들관리는 부르는 부모 윈도우에서 해야겠다고 결론지었다.

일단 Teacher 클래스에는 student 클래스 핸들을 담을 변수를 하나 만들자.

그리고 getStudent라는 메소드를 하나 만들고 생성시 핸들을 넘기도록 하겠다.

 

void CTeacher::getStudent(HWND s)
{
 this->student = s;
}

 

---------------------------------

그리고 메인프레임에서 학생값을 넘겨준다.

 HWND temp = student->GetSafeHwnd(); 
 teacher->getStudent(temp);

 

-------------------------------

그리고 LBUTTONDOWN 메세지처리

 AfxMessageBox(TEXT("야 ㅇㅇ야"),MB_OK,0);
 ::SendMessageA(student,WM_QUESTION,0,0);

 

---------------------------------

그러면 받는 student에서 다시 WM_QUESTION을 정의해준다. 숫자가 같으면 WM_ANSWER로 바꾸겠다.

 

WM_ANSWER는 유저 메세지이므로 클래스위자드말고 직접만들려고 했는데,

클래스위자드에 커스텀 메세지가 있었다.

afx_msg LRESULT OnAnswer(WPARAM wParam, LPARAM lParam);

ON_MESSAGE(WM_ANSWER, &CStudent::OnAnswer)

afx_msg LRESULT CStudent::OnAnswer(WPARAM wParam, LPARAM lParam)
{
 AfxMessageBox(TEXT("네"),MB_OK,0);
 return 0;
}

 

요렇게 추가해서 완성.

 

뭔가 쉬운 부분도 있고 어려운 부분도 있고 한게 Mfc 방식인듯하다....

 

4. 닷넷

닷넷은 폼위주의 프로그램이니까 큰 mdi 폼을 하나생성하고 나머지 두개의 폼을 그안에 넣어서 비교했다.

 

        private void Form1_Load(object sender, EventArgs e)
        {
            teacher = new Form2();
            student = new Form2();
            teacher.MdiParent = this;
            student.MdiParent = this;
            teacher.Text = "선생";
            student.Text = "학생";
            teacher.Show();
            student.Show();

            teacher.Click +=new EventHandler(teacher_Click);
            student.Click +=new EventHandler(student_Click);
        }

        private void teacher_Click(object s, EventArgs e)
        {
            MessageBox.Show("야 ㅇㅇ야");
            student_Click(s,e);
        }

        private void student_Click(object s, EventArgs e)
        {
            MessageBox.Show("네");
        }

 

이런식으로 하면 선생이 불렀을때 대답을 한다. 이것말고 학생에게 대답을 요하는 정의 이벤트가 필요하다.

 

닷넷은 주고받는 시스템이 일단 없다. 그냥 주고받는 이벤트 타입을 서로 맞춰주면 된다.

 

그러면 위의 Form2 라는 공통 폼을 썼는데 이벤트가 추가되어야되니 따로 Form3를 만들겠다.

 

    public delegate void customEventHandler(object s);
    public partial class Form2 : Form
    {
        public event customEventHandler Question;
        public Form2()
        {
            InitializeComponent();
        }
        public void teacher_Click(object s, EventArgs e)
        {
            MessageBox.Show("야 ㅇㅇ야");
            OnQuestion();
            //Question(this);
        }
        private void OnQuestion()
        {
            if (Question != null)
            {
                Question(this);
            }
        }
    }

폼2: 선생님을 나타내기 위한 form 재정의

 

    public partial class Form3 : Form
    {
        public void Answer(object s)
        {
            MessageBox.Show("네");
        }
        public Form3()
        {
            InitializeComponent();
        }
    }

폼3: 학생을 위한 폼 재정의

 

    public partial class Form1 : Form
    {
        Form2 teacher;
        Form3 student;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            teacher = new Form2();
            student = new Form3();
            teacher.MdiParent = this;
            student.MdiParent = this;
            teacher.Text = "선생";
            student.Text = "학생";
            teacher.Show();
            student.Show();

            teacher.Click +=new EventHandler(teacher.teacher_Click);
            teacher.Question += new customEventHandler(student.Answer);
        }

    }

메인 mdi폼...

 

여기서 위의 빨간 부분, teacher.click은 같다. 그러나 학생의 대답을 받는 부분은 선생의 질문과 같다. Question 이벤트에 student의 answer가 대입되어있다. 즉 가독성이 떨어진다.

 

그리고 폼2의 OnQuestion도 있으나 없으나 똑같다. msdn에서는 이렇게 하라는데 안해도 되고 그렇다. 가독성을 위해 이렇게 만들어달라는것 같은데... 구조자체가 이벤트가 별로 필요가 없어보임....

 

아무튼 닷넷은 이벤트가 좀 재미없는듯... 다른 방법있으면 쪽지 주시라..

 

5. javascript

 

 

다음과 같은 두개의 div를 만든다.

<div id=teacher style="position:absolute;top:300px;left:320px;width:100px;height:100px;background:blue"></div>
<div id=student style="position:absolute;top:300px;left:500px;width:100px;height:100px;background:blue"></div>

 

그리고 액션스크립트 예제와 같이 작동하게 만들려면

<script>
    document.getElementById("teacher").addEventListener("click", question, false);
    document.getElementById("student").addEventListener("click", answer, false);
    function question(e) {
        alert("야 ㅇㅇ아");
        var evt = document.createEvent('Events');
        evt.initEvent('click', true, false);
        document.getElementById("student").dispatchEvent(evt);
    }
    function answer(e) {
        alert("네");
    }
</script>

 

그리고 아래는 IE 버전이다.

<script>
    document.getElementById("teacher").attachEvent("onclick", question);
    document.getElementById("student").attachEvent("onclick", answer);
    function question(e) {
        alert("야 ㅇㅇ아");
        var evt = document.createEventObject();
        document.getElementById("student").fireEvent("onclick", evt);
    }
    function answer(e) {
        alert("네");
    }
</script>

 

그런데 액션스크립트에서는 사용자정의 이벤트를 기본으로 쓰지 않으면 학생의 대답'네' 가 선생님의 질문 뿐만 아니라, 학생 자신의 클릭때도 생성되어야만 했다. 그러나 위를 잘 보면, 메세지를 넘겨줄때 일부러 이벤트 객체를 생성한다. 그러면 굳이 마우스이벤트를 쓸 필요가 없다.

위에서 학생객체의 이벤트 이름을 따로 answer로 바꿔버리겠다. 위의 빨간 부분을 바꾸면 되는데,

아래의 IE의 부분을 onanswer로 바꾸면 에러가 난다.

 

왜냐면 IE는 자체적으로 지원하는 몇몇의 합성이벤트를 써야되기때문(즉, 사용자정의 이벤트를 정해놨다는 말이다.) 일단 ondataavailable로 설정하면 잘된다.

 

일단 자바스크립트는 우리가 하고 싶은 말하고 답하기를 가장 쉽게 구현하게 되어있다.

 

 출처: http://blog.naver.com/cyjses?Redirect=Log&logNo=112933770

 

 

=================================

=================================

=================================

 

 

반응형