델파이

ScrollBar 윈도우메시지 TWMVScroll 한계 극복 GetScrollInfo , TMemo 스크롤바 이동

미스터몽키 2015. 6. 10. 19:11

컬럼헤더에 여러줄로 캡션을 쓸 수 있고, 컬럼헤더를 병합하고, 체크박스를 디자인 타임에서 구현하는

StringGrid와 비슷한 사용자 패키지 컴포넌트를 만들다가 (수정?)하다.  이상한 현상을 발견했다.


자료가 2000건 정도일때

세로스크롤바의 상버튼(SB_LINEUP), 하버튼(SB_LINEDOWN)으로 이동은 잘 된다.

그런데 세로 스크롤바의 썸(Thumb)을 드래그 하여 자료를 이동하면 1638건에서 더이상 드래그가

되지 않는다. (행의 높이가 20일때) 


이유는 세로스크롤과 관계된 윈도우 메시지는 TWMVScroll 였다.  Messages 유닛에 선언되어 있는데

선언부분을 보면 TWMVScroll = TWMScroll 처럼   TWMScroll Class 이다.


  TWMScroll = record
    Msg: Cardinal;
    MsgFiller: TDWordFiller;
    ScrollCode: Smallint; { SB_xxxx }
    Pos: Smallint;   <- 문제원인
    ScrollCodePosFiller: TDWordFiller;
    ScrollBar: HWND;
    Result: LRESULT;
  end;


문제의 원인은 바로 Pos 멤버가 Smallint 타입 (-32768~32767) 이다.


행의 높이가 20일때     32767/20 = 1638 이므로 그리드의 헤드컬럼 포함해서 약 1638건만 드래그 되었던 것이다.

물론 스크롤바의 상버튼 하버튼은 TWMVScroll  윈도우 메시지를 사용하지 않고 직접 약 20씩 상하로 이동되게 코딩되어 있어

잘 작동되었던 것이다.


며칠 고생하여 해결했다.


Windows 유닛의 TScrollInfo = tagSCROLLINFO  구조체인데.


  tagSCROLLINFO = record
    cbSize: UINT;
    fMask: UINT;
    nMin: Integer;
    nMax: Integer;
    nPage: UINT;
    nPos: Integer;
    nTrackPos: Integer;
  end;


GetScrollInfo 함수를 통해

function GetScrollInfo(hWnd: HWND; BarFlag: Integer; var ScrollInfo: TScrollInfo): BOOL; stdcall;


스크롤바의 모든 정보를 알수 있다.

또한 썸드래그시 위치인 nTrackPos 멤버가 integer타입이다   -2147483648 ~ 2147483647  (약21억)

이 정도면 이론상 약 1억 건도 스크롤된다.


그래서 원래 윈도우메시지 TWMVScroll을 사용해던 부분을


FVertOffset := Msg.Pos;   //Msg는 윈도우메시지 TWMVScroll,  FVertOffset 는 변경할 위치


다음처럼 GetScrollInfo 함수를 통해 해결했다.


var   si : TScrollInfo;
        rt : Boolean;


rt := GetScrollInfo(Handle, SB_VERT, si);   // 꼭 rt로 할당하고나서 비교해야한다.


if rt then
   FVertOffset  := si.nTrackPos;


함수의 리턴값인 rt 가 True면  nTrackPos가 현재의 썸위치를 가져온다.

가져온 썸위치를 세로 스크롤바의 위치에 대입하면 된다.


(참고)

https://msdn.microsoft.com/en-us/library/windows/desktop/bb787583(v=vs.85).aspx




또한 일반적인 TMemo 컨트롤의 세로 스크롤바를 맨아래로 강제 이동시키는 방법은

Memo1.Perform(WM_VSCROLL, SB_BOTTOM, 0);  // Memo1 맨아래로 이동