作者:khler
E-mail:khler@163.com
日期:2007.05.30
(申明:文章可任意轉載,但必須保證以上資訊完整性)
最近在做一個SDI程式時,發現Scroll的一個不大不小的問題,當你的 scroll size 過大(一般大於32767)時,一旦拖動滑塊使滑塊的位置超過一定值,那麼滑塊的位置參數就會迴歸為0,即用GetScrollPos()返回的值為0,一旦釋放滑塊,滑塊立即返回到零點位置。
究其原因,WM_VSCROLL和WM_HSCROLL是使用wParam參數來傳遞滑塊當前位置和滑塊動作flag的,WPARAM 是一個UINT類型的,高位的兩個位元組,即16bit,用來攜帶滑塊位置資訊,低位的16bit用來攜帶flag資訊,這樣,位置資訊的最大值理論上不能超過65535,而實際上,如果高位的第一個bit被置為1,那麼它將被WM_HSCROLL、WM_VSCROLL當作一個負數來處理,所以 scroll size 實際上不能超過 32767,即0x7FFF。
難道 scroll size 的大小就僅僅局限於32767這樣一個尺寸了嗎?答案當然是否定的,解決辦法是使用GetScrollInfo()函數,配合SCROLLINFO結構體,將SCROLLINFO的fMask欄位設定為SIF_TRACKPOS,我們即可在nTrackPos中獲得正確的int類型的滑塊位置資訊,方法如下:
重載OnScroll()函數,取得滑塊flag資訊,如果是SB_THUMBTRACK或者SB_THUMBPOSITION,則進行處理。這裡要注意,如果處理對象是水平捲軸,則取nScrollCode的低位元組,如果處理對象是垂直捲軸,則取nScrollCode的第二個位元組:
BOOL CHexViewView::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll)
{
// TODO: Add your specialized code here and/or call the base class
BOOL rtnCod = CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);
BYTE cod = nScrollCode>>8; // 這裡只處理垂直捲軸的情況
if( SB_THUMBTRACK == cod || SB_THUMBPOSITION == cod )
{
SCROLLINFO si;
memset( &si, 0, sizeof(si) );
si.cbSize = sizeof(si);
si.fMask = SIF_TRACKPOS;
if( GetScrollInfo( SB_VERT, &si, SIF_TRACKPOS ) )
{
m_nScrollPos = si.nTrackPos; // 取得正確的滑塊位置
SetScrollPos( SB_VERT, si.nTrackPos ); // 設定
RedrawWindow(); // 重繪
}
}
return rtnCod;
}
這裡需要注意五個問題:
1、如果你處理的對象是水平捲軸,則取ScrollCode的低位元組,BYTE cod = nScrollCode&0xFF;
2、一定要在調用基類的OnScroll()之後處理並發送重繪訊息,否則在拖動滑塊、停止拖動但未釋放滑鼠左鍵的瞬間不會顯示任何內容,所以應該先調用基類的BOOL rtnCod = CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);並將傳回值儲存,等處理完成後再返回其傳回值;
3、取得正確的位置資訊後應該使用SetScrollPos()將正確的位置資訊重新設定,否則同樣不能正確設定滑塊位置;
4、必鬚髮送重繪訊息重回視窗;
5、不能在OnVScroll()、OnHScroll()中處理,因為這兩個函數只在滑鼠按鍵按下或者釋放的時候才響應,而OnScroll()響應整個拖動動作。
khler@163.com
2007.5.30