달력

12

« 2024/12 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
스크롤바 관련해서 정보를 찾던 도중 유용하게 쓰일 정보를 발견해서 스크랩 하였다
Ospace님께 다시한번 감사 드립니다
---------------------------------------------------------------------------------------------------

[컨트롤이야기] 아무도 말해주지않은 스크롤바 기능

작성자: Ospace (ospace114 at naver.com) http://discount77.com/blog/ospace

스크롤바는 상당히 많이 사용하고 있는 컨트롤 중에 하나이다. 내용을 한 화면에 표시하지 못할 경우 그 일부를 표시하고 나머지 내용은 스크롤 바로 상하, 혹은 좌우로 이동하면서 볼 수 있게 한다.
또는 일정 정수 구간 값을 이동하면서 원하는 지점의 정수 값을 가져올 수도 있다.

먼저 스크롤 바의 기본적인 이야기를 하고 후반에 좀더 깊이 다루겠다.

Note: 예제 코드는 거의 없기에 간단하게 스크롤바를 작성하는 강좌를 보고 읽는게 이해하기 쉽다.



스크롤바 들어가기


사용자 삽입 이미지

Fig 1. Scroll Bar (from Windows XP)


위의 그림은 Windows XP에 있는 스크롤 바이다. 그전 스크롤바보다 이쁘게 변했다. ^^;
MS에서는 이렇게 스킨을 어떻게 했길래 선택적으로 바뀔 수 있지. 컨트롤 스킨 변경하려면 수많은 코드와 삽질을 해야만 되는데.. OTL

아래는 실제 스크롤 바에 구성요소들이다.
사용자 삽입 이미지

Fig 2. Scroll Bar 구성요소



스크롤바는 많이 사용해서 각 기능이야 대충 알고 있지만, 다시 대충 살표보자.

좌우에 삼각형 모양의 버튼이 보인다. 이는 좌후로  1 발자국(step)씩 움직인다. 이는 스크롤바 전체 값 범위에서 실제 1값을 의미의한다.
예를 들어 스크롤바 값 범위가 1~100 혹은 1~1000이든 상관없이 무조건 1을 의미한다.

가운데 홀로 떨어진 스크롤박스가 보인다. 이는 MSDN의 스크롤바 설명을 참조했다. 이를 이용해 값을 찾아간다. 좌우측에 빈 페이지가 보인다.

스크롤바의 정보를 가져오는 API로 GetScrollInfo 함수가 있다. 그리고 정보를 설정하는 API는 SetScrollInfo 함수가 있다.

스크롤바는 100% 수동적인 컨트롤이다. 이말은 모든 부분을 프로그래머가 제어해줘야한다.
예를 들어 가운데 스크롤박스를 움직이면 끝나는 것이 아니라. 그때 스크롤바 위치를 얻고, 해당 값을 다시 스크롤바에 갱신하고 화면을 다시 그리게 한다는 의미이다. 주의하길 바란다!

스크롤바 값은 정수형이다. 실수는 사용할 수 없다. 사용하려면 값변환을 통해서 사용해야 한다. 최대 최소값을 지정하지 않으면 기본 범위는 0~100을 가진다. 일단 정확히 지정해주는게 좋다.

예를 들어 문서를 불러와서 표시하려 한다. 총 줄수가 150개라고 하면, 스크롤바 값 범위는 0~150으로 설정한다. 이건 고정된 값은 아니다. 스크롤바 범위는 300, 450도 가능하고 200도 가능하다.
단지 스크롤바 1스텝이 라인 1줄에 해당하는 형태로 일치시키기 위해서이다.

이렇게 최대 최소 값을 지정하는건 그다지 큰 문제가 없고 다른 사이트에서 많이 다루는 예제이다.


스크롤바 깊게 들어가기


지금은 스크롤바 페이지에 대해서 보도록 하겠다. 페이지는 스크롤바 가운데 있는 스크롤상자 크기에 대한 이야기이다. 스크롤상자는 때에 따라서 커지고 작아지기도 한다. 손오공 여의봉처럼 마음대로 커지고 마음대로 작아지는 놈일까?
그렇지 않다. 사용 목적이 있기 때문에 크기가 그에 따라 변경이된다.

스크롤바 크기는 페이지 크기이다. 한페이지에 들어가는 값 개수이다. 어차피 정수이니 한페이지에 들어가는 정수값 크기라고 보면 된다.

페이지 크기라고 하면 감이 안잡힌다. 예를 들어 책을 살펴보는게 쉽게 이해할 수 있다.
책에는 방대한 양의 글자들이 들어가 있다. (물론 그림, 사진 등도 있다^^;) 모든 내용을 하나의 종이메 모두 담지 못했기에 페이지 단위로 나눠서 표시한다.

예를 들어, 총 1234줄의 내용이 있고 한페이지에 들어가는 줄수는 100줄이라고 가정하면 총 페이지 수는 얼마가될까?

답은 13 페이지
마지막 12페이지를 꽉채우고 34줄이 남지만 한 페이지는 반으로 나눌 수는 없기에 1페이지로 만들어서 넣는다. 추가로 표지, 목차, 속지, 인지 등을 포함하면 더 늘어날 수 있지만 ㅡ.ㅡㅋ

마찬가지로 컴퓨터 화면도 방대한 내용을 한화면에 모두 표시할 수 없기에 페이지 단위로 나눠서 표시한다. 책과 다른 점은 컴퓨터는 연속적으로 페이지를 볼 수 있다.

앞의 책의 예를 일반 텍스트 문서라고 가정해서 스크롤바 설정을 해보자.

최대 최소 값 범위: 0~1234
페이지크기(스크롤박스 크기): 100


너무 쉬어 보인다. 내가 설명을 잘해서인가? ㅋㅋㅋ

그러나 주의할 점이 있다. 바로 스크롤박스가 움직일 수 있는 최대값이 스크롤바 최대값과는 틀리다.
이는 스크롤박스 크기에 따라 가변적이기에 주의해야한다.
사용자 삽입 이미지

Fig 3. Scroll Bar 값들

위 그림은 스크롤바에서 사용되는 값을 표시하였다.
스크롤박스 왼쪽 경계가 현재 값이다. (Current Position)
스크롤바 양쪽 버튼을 제외한 영역이 스크롤바 전체 값 범위이다.(MAX Range Value)
그리고, 양쪽 버튼과 스크롤박스 크기를 제외한 나머지 영역이 스크롤박스가 움직일 수 있는 범위이다.(MAX Scroll Pos)

여기서 중요한 값이 MAX Scroll Pos이다. 현재 위치(Current Position)이 MAX Scroll Pos값을 넘을 수 없다. 단, 최소값은 항상 0으로 가정한다.

0 <= Current Position <= MAX Scroll Pos

그럼 MAX Scroll Pos을 구하는게 중요하다.

MAX Scroll Pos = MAXRangeValue - (Page Size - 1)

Page Size에서 -1은 당연히 Page Size 왼쪽 경계까지 값 범위이기 때문이다. 이를 앞에 책의 예를 이용해서 다시 스크롤바 설정을 해보자.

최대 최소 값 범위: 0~1234
페이지크기(스크롤박스 크기): 100
스크롤박스 이동 범위: 0 ~ 1135




스크롤바 전문가 과정

좀더 스크롤바를 깊게 들어가자. 지금 부터 조금 머리 아파질 수 있으니, 지금 머리가 조금 아프신 분들은 창문가서 밖같 하늘 한번 구경하자.

지금 다를 내용은 크게 두가지 이다. 하나는 프로그램 크기(resize)와 데이터 변경되는 경우와 다른하나는 스크롤을 좀더 세밀하기 하기 위해 스크롤바 최대 최소 범위를 변경하는 경우이다.


프로그램 크기와 데이터 변경

먼저 프로그램 크기와 데이터가 변경되는 경우를 살펴보자.
프로그램 크기가 변경되는 의미는 내용이 나타나는 화면 크기가 바뀌었다는 의미이고 스크롤 바에서는 한 화면에 표시되는 크기를 가리키는 스크롤박스 크기가 달라졌다는 의미이다.

앞의 책의 예를 보면 한페이지에 표시할 수 있는 줄수가 100줄에서 130줄 혹은 50줄로 커지거나 작아졌다는 의미이다.
일단 내용 변경이 없다면 스크롤바 최대 최소 값 범위은 변경되지 않고 페이지 크기가 변경되었고, 즉, 스크롤박스가 이동할 수 있는 범위가 변경되었다는 의미이다.

책의 예제에서 한페이지(한화면)에서 표시할 수 있는 줄수가 100줄에서 130줄로 변경되었다면 스크롤바 설정을 살펴보자.

스크롤바 최대 최소 값 범위: 0 ~ 1234
페이지크기(스크롤박스 크기): 100 -> 130 (변경)
스크롤박스 이동 범위: 1234 - (130 - 1) = 1103 (변경)
페이지수: 10 페이지 (이는 스크롤바 설정 값은 아니다.)


그리고 데이터가 변경되었다면 어떻게 될까?
일단 스크롤바 최대 최소 값 범위가 변경된다. 페이지 크기는 고정이지만, 스크롤 박스 이동 범위도 바뀌게 된다. 즉, 전체 크기가 변경되었으므로 이동가능한 범위도 변경되었다는 의미이다. 그럼 마찬가지로 앞의 책의 예로 계산해보자.
가정은 데이터 량이 1234줄에서 1500줄로 늘어낳고 한 페이지 크기는 130줄로 계산해보자.

스크롤바 최대 최소 값 범위: 0 ~ 1500 (변경)
페이지크기(스크롤박스 크기): 130
스크롤박스 이동 범위: 1500 - (130 - 1) = 1371 (변경)
페이지수: 12 페이지 (이는 스크롤바 설정 값은 아니다.)


화면 크기 혹은 데이터 크기 변경은 각각 페이지 크기 변경 혹은 스크롤바 최대 값 변화가 따로 생기지만 스크롤박스 이동 범위는 둘 다 새로 계산해야 된다.


스크롤바 정밀도 조절하기

데이터 크기와 스크롤바 크기가 일치하지 않은 경우를 살펴보자.
앞에서는 텍스트 한줄이 스크롤바 한스텝과 일치한다. 스크롤바에서 3값이 움직이면 텍스트에서 3줄이 움직인다.

간혹 1 스텝 이동이 1/2 줄 이동이나 1 스텝 이동이 2줄 이동으로 할 경우도 있다.

사용자 삽입 이미지

Fig 4. 1스텝 이동시 반줄 이동


이 그림은 1 스텝 이동은 반 줄 이동하는 예를 보여둔다. 즉 2 스텝 이동해야 한 줄이 이동된다는 의미이다. 즉 한 스텝 이동하면 글자 반정도 크기만 움직이고 화면 경계에 있다면 반은 보이고 반은 가려서 보이지 않게된다. 한 스텝이 글자 반 정도 움직이는 느린 스크롤이 된다.

사용자 삽입 이미지

Fig 5. 1스텝 이동시 2줄 이동



이 그림은 1 스템 이동이 2줄 이동하는 예이다. 즉, 반 스텝 이동이 한줄 이동이지만 반 스텝 이동은 없다. 스크롤 바는 정수 값만 사용하기 때문이다.
한스텝 이동은 2줄이 한꺼번에 움직이므로 빠른 화면 스크롤을 볼 수 있다.

그러면 위와 같은 경우 스크롤바 값 설정은 어떻게 될 것인다.
간단하게 계산하기 위해서 텍스트 줄수는 100줄이고 한화면 표시되는 페이지 크기는 10라고 하자.


1스텝에 1/2줄 이동

먼저 한 스텝에 반 줄 이동하는 예를 보자.
즉, 한줄이 2스텝을 차지하므로 총 100줄이므로,

100줄 x 2 스텝 / 줄 = 200 스텝

결국 스크롤바 값 범위가 0 ~ 200를 가진다. 좀더 생각을 하면 앞에서 스크롤바는 정수값만 사용한다고 했지만, 위의 값의 범위는 0~100줄은 0.5줄 단위까지 잘라서 얻을 수 있다는 의미이다.
페이지 크기가 10는 스템 수가 아닌 화면에 나타나는 줄 수 이다. 그러면 페이지 크기는

10줄 x 2 스텝 / 줄 = 20 스텝

페이지 크기는 20스텝이 된다.
그러면 스크롤박스 최대 이동 가능한 값이 계산된다.

스크롤박스 최대 이동 값 = 200 - (20 - 1) = 181

스크롤박스 최대 이동 값은 181이 된다.


1스텝에 2줄 이동

다음으로 1스텝이 2줄 이동하는 예를 살펴보자. 앞의 계산 법의 확인일 뿐이다.
앞의 계산 법에 의해서 단위 줄에 변하는 스텝 값은 1/2이 된다.

스크롤바 최대값: 100줄 x 1/2 스텝/줄 = 50 스텝
페이지 크기: 10줄 x 1/2 스텝/줄 = 5 스텝
스크롤박스 최대 이동 값: 50 - (5 - 1) = 46


계산 방법은 동일하다. 이부분은 텍스트를 예로 들어서 설명했지만, 그래픽 처리에서 상당히 유용하리라 생각한다.


후기

이 부분을 해결하는데 많은 시간이 소요되었다. 이에 웹 사이트를 아무리 뒤져봐도 이에 대해 잘 설명해준 곳이 없었다. 심지어 책에도 없었다. 분명 모든 개발자가 스크롤바를 사용함에 있어서 이에 대해서 한번 쯤은 고민해봤을 거라 생각한다.
대부분 화면이 고정인 경우만 고려했을 거라 생각한다. 홤녀이 고정이면 계산은 간단하다. 약간의 계산 오류가 있어도 시행착끝에 근사 계산값을 얻을 수도 있다.

결정적인 힌트를 준 자료는 MSDN의 "About Scroll Bars"이다. 결국 MSDN으로 오게 된다.
아직 못다른 부분도 많을 것이다. 스크롤바로 활용가능한 부분도 상당히 광범위하기 때문이다.

위의 내용은 스크롤 바를 사용하고 있으면서 좀더 넓은 생각을 가지기 위해서 작성했다. 첫 사용자는 다른 자료를 참고하기길 바란다.

스크롤바 강좌: http://www.winapi.co.kr/win32lec/lec7/lec7-5-1.htm

앞으로 어떻게 응용하는가는 본인의 의지에 달려있다.
모두 즐프를 향해~


참고: MSDN "About Scroll Bars"
:
Posted by Lunaness
SHGetFileInfo는 매우 유용한 API다
어찌나 친절한 지 File의 정보를 몽땅 건내준다
(심지어 아이콘 정보까지 말이다)

여기서는 간단하게 파일 속성을 알기 위한 용도로 사용했다

CString strDrive;
SHFILEINFO shInfo;
....
SHGetFileInfo(strDrive,0,&shInfo,sizeof(SHFILEINFO),SHGFI_TYPENAME | SHGFI_ATTRIBUTES);
....

if ( !(shInfo.dwAttributes & SFGAO_HIDDEN) )
    // 숨김파일은 제외한다 코딩

여기서 붉은색 플래그는
#define SHGFI_ICON              0x000000100             // get icon
#define SHGFI_DISPLAYNAME       0x000000200      // get display name
#define SHGFI_TYPENAME          0x000000400        // get type name
#define SHGFI_ATTRIBUTES        0x000000800         // get attributes
#define SHGFI_ICONLOCATION      0x000001000       // get icon location
#define SHGFI_EXETYPE           0x000002000           // return exe type
#define SHGFI_SYSICONINDEX      0x000004000        // get system icon index
#define SHGFI_LINKOVERLAY       0x000008000         // put a link overlay on icon
#define SHGFI_SELECTED          0x000010000           // show icon in selected state
#define SHGFI_ATTR_SPECIFIED    0x000020000        // get only specified attributes
#define SHGFI_LARGEICON         0x000000000          // get large icon
#define SHGFI_SMALLICON         0x000000001         // get small icon
#define SHGFI_OPENICON          0x000000002          // get open icon
#define SHGFI_SHELLICONSIZE     0x000000004       // get shell size icon
#define SHGFI_PIDL              0x000000008               // pszPath is a pidl
#define SHGFI_USEFILEATTRIBUTES 0x000000010    // use passed dwFileAttribute

로 정의 되어 있다
이 플래그를 or로 추가해주면 필요한 정보를 얻어올 수 있다

여기서 오랜지색 플래그는
#define SFGAO_CANCOPY           DROPEFFECT_COPY // Objects can be copied    (0x1)
#define SFGAO_CANMOVE           DROPEFFECT_MOVE // Objects can be moved     (0x2)
#define SFGAO_CANLINK           DROPEFFECT_LINK // Objects can be linked    (0x4)
#define SFGAO_STORAGE           0x00000008L     // supports BindToObject(IID_IStorage)
#define SFGAO_CANRENAME         0x00000010L     // Objects can be renamed
#define SFGAO_CANDELETE         0x00000020L     // Objects can be deleted
#define SFGAO_HASPROPSHEET      0x00000040L     // Objects have property sheets
#define SFGAO_DROPTARGET        0x00000100L     // Objects are drop target
#define SFGAO_CAPABILITYMASK    0x00000177L
#define SFGAO_ENCRYPTED         0x00002000L     // object is encrypted (use alt color)
#define SFGAO_ISSLOW            0x00004000L     // 'slow' object
#define SFGAO_GHOSTED           0x00008000L     // ghosted icon
#define SFGAO_LINK              0x00010000L     // Shortcut (link)
#define SFGAO_SHARE             0x00020000L     // shared
#define SFGAO_READONLY          0x00040000L     // read-only
#define SFGAO_HIDDEN            0x00080000L     // hidden object
#define SFGAO_DISPLAYATTRMASK   0x000FC000L
#define SFGAO_FILESYSANCESTOR   0x10000000L     // may contain children with SFGAO_FILESYSTEM
#define SFGAO_FOLDER            0x20000000L     // support BindToObject(IID_IShellFolder)
#define SFGAO_FILESYSTEM        0x40000000L     // is a win32 file system object (file/folder/root)
#define SFGAO_HASSUBFOLDER      0x80000000L     // may contain children with SFGAO_FOLDER
#define SFGAO_CONTENTSMASK      0x80000000L
#define SFGAO_VALIDATE          0x01000000L     // invalidate cached information
#define SFGAO_REMOVABLE         0x02000000L     // is this removeable media?
#define SFGAO_COMPRESSED        0x04000000L     // Object is compressed (use alt color)
#define SFGAO_BROWSABLE         0x08000000L     // supports IShellFolder, but only implements CreateViewObject() (non-folder view)
#define SFGAO_NONENUMERATED     0x00100000L     // is a non-enumerated object
#define SFGAO_NEWCONTENT        0x00200000L     // should show bold in explorer tree
#define SFGAO_CANMONIKER        0x00400000L     // defunct
#define SFGAO_HASSTORAGE        0x00400000L     // defunct
#define SFGAO_STREAM            0x00400000L     // supports BindToObject(IID_IStream)
#define SFGAO_STORAGEANCESTOR   0x00800000L     // may contain children with SFGAO_STORAGE or SFGAO_STREAM
#define SFGAO_STORAGECAPMASK    0x70C50008L     // for determining storage capabilities, ie for open/save semantics

....
겁나 길다, 하지만 대충 봐도 알아 챌거니 자세한 설명은 생략한다
보기에는 많아 보이지만 예를들어
파일 속성에는 (읽기전용, 숨김, 링크, 삭제 가능/불가 등등)이 들어간다
:
Posted by Lunaness
가장자리를 깎아낼 때 쓴다

<선언>

void CXXXDialog::RoundEdge(CRect clientRect)
{
      // 모서리를 둥글게
      CRgn rgnTop;
      CRgn rgnBottom;
      // 상단 모서리를 자르고.
      rgnTop.CreateRoundRectRgn(0,0,clientRect.Width(),clientRect.Height(),9,9);
      rgnBottom.CreateRectRgn(0, clientRect.Height()-10, clientRect.Width()-1, clientRect.Height());
      // 해당 영역을 합치고.
      CombineRgn(rgnTop, rgnTop, rgnBottom, RGN_OR);
      // 잘려진 영역을 적용하고
      SetWindowRgn(static_cast<HRGN>(rgnTop.GetSafeHandle()), TRUE);
      // 영역을 해제.
      rgnTop.Detach();
      rgnBottom.Detach();
}

<사용>
CRect clientRectt;
GetClientRect(&clientRectt);
RoundEdge(clientRectt);

뭔소리인고 하니...
사용자 삽입 이미지
빨간색 태두리 처럼 만든다 이거다...


:
Posted by Lunaness
2008. 5. 17. 15:13

프로그램 Alt+F4 감지 Programing/Windows Programing2008. 5. 17. 15:13

프로그램에서 Alt+F4를 입력받으면
중간에 어떠한 일을 수행하고 종료를 해야 하는 경우가 있다
그 때 Alt+F4 를 감지해서 특정 일을 수행 한 후 종료하게 하려면
다음과 같이 작성해라

BOOL CDirectVideoDialog::PreTranslateMessage(MSG* pMsg)
{
    // 중간 생략
    if(pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_F4) 
    {
        if (::GetKeyState(VK_MENU) < 0)
        {
             //여기에 할 일을 작성
             return TRUE;
        }
    }
    // 중간 생략
}

PreTranslateMessage에서 감지 하여도 되지만

WM_SYSCOMMAND에서도 처리할 수 있습니다.
아래와 같이 하면 프로그램 오른쪽 위에 있는 닫기 버튼에 대해서도 닫기가 안되도록 처리 할  수있고
Alt+F4에 대해서도 그리고 메뉴->닫기 에 대해서도 처리할 수 있습니다.

void CMyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
   if( nID == SC_CLOSE )
   {
        // 할일 작성
        return;
   }
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    } 
}

출처 : Devpia

:
Posted by Lunaness

리스트컨트롤을 사용하는데 자동으로 선택해와야 하는 경우가 생겨 찾아보니
데브피아에 저런 내용이 검색되었다

저도 같은 내용을 검색 하다가 다음과 같은 내용을 찾았습니다. 즐프.. ^^;

 ListCtrl::SetSelectionMark(int i)  : i번째 item을 선택하는 것은 맞습니다.

단지 포커스까지 주지 않기때문에 표시(파란줄)가 되지 않을 뿐이지 내부적으로 현재 선택된 아이템은 i번째 아이템입니다.

m_list1.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED)  : 이것만 사용하시면 문제가 발생할 수 있습니다.

내부적으로 자동 선택된 아이템의 인덱스와 표시(파란줄)된 아이템의 인덱스가 달라질 수 있거든요..
표시된 아이템을 삭제하려고 삭제 버튼을 눌렀는데 딴 아이템이 삭제되는 일이  생길 수 있겠죠..

그래~서 이렇게 사용하시면 깔끔하실 듯 싶습니다.^^호호

m_cListCtrl.SetSelectionMark(i);
m_cListCtrl.SetItemState(i, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
m_cListCtrl.SetFocus();


깔끔하게 해결되는구만 -ㅂ-;
:
Posted by Lunaness