在图像编程中,经常要查看矩阵数组中的元素值,大量的数据在调试窗口中查看是特别麻烦的,于是我想着,能不能编写一个表格一样的东西,在需要的时候让它直接弹出来显示。
我的思路是这样的,在一个对话框上放置一个CListView,设置成列表风格,然后把数据插入到列表中,这样还不用自己写滚动条。(如何在对话框上放置CListView可以参考sanbiangongzi.bokee.com/1733920.html)。但是在编程中发现,CListView的列表风格没有行方向的表头,当然可以自己插入一列数据作为表头,但是这样的话,在水平滚动条向右滑动的过程中,行方向的表头就会移出对话框,总感觉这样不是很方便。于是我在对话框的左边又放置了一个CListView做行方向的表头,这样就牵扯到一个问题,如何控制两个CListView同步滚动。
在网上找了很久,再加上自己摸索,我的方法是这样的:
假设CMatrixLeftHeader和CMatrixView继承自CListView,分别用来显示行方向的表头和矩阵数据。在CMatrixView中设置CMatrixLeftHeader的指针,用CMatrixView的滚动条来控制二者同步滚动。重写CMatrixView的OnVScroll方法,在其滚动条动作触发的同时向CMatrixLeftHeader发送滚动消息。
新的问题又出现了,如果CMatrixLeftHeader的滚动条要显示的话(不设置 LVS_NOSCROLL),按行滚动和翻页都可以成功,但滑块移动却不行(网上说是因为CListView的假窗口的原因),如果禁用CMatrixLeftHeader的滚动条(设置 LVS_NOSCROLL),就只有按行滚动可以成功,其他都不行。
::SendMessage(m_hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),NULL);//可以成功下滚1行
::SendMessage(m_hWnd,WM_VSCROLL,MAKEWPARAM(SB_PAGEDOWN,0),NULL);//可以成功下滚1页
::SendMessage(m_hWnd,WM_VSCROLL,MAKEWPARAM(SB_THUMBTRACK,nPos),NULL); //不成功
::SendMessage(m_hWnd,WM_VSCROLL,MAKEWPARAM(SB_THUMBPOSITION,nPos),NULL); //不成功
我的做法是,用按行滚动来获得翻页以及滑块移动时的效果。在CMatrixView里设置一成员变量m_nCurrFirstRow记录当前处在视图第一行的行号,用SCROLLINFO类型的变量可以获得当前列表的最小最大行号(nMin,nMax),包括每页的行数(nPage),这样需要滚动到什么地方就让它逐行滚动到那里。
void CMatrixView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
switch (nSBCode)
{
case SB_LINEDOWN:
ListLineDown(m_pLeftHeader->m_hWnd);
break;
case SB_LINEUP:
ListLineUp(m_pLeftHeader->m_hWnd);
break;
case SB_PAGEDOWN:
ListPageDown(m_pLeftHeader->m_hWnd);
break;
case SB_PAGEUP:
ListPageUp(m_pLeftHeader->m_hWnd);
break;
case SB_THUMBPOSITION:
ListThumbPosition(m_pLeftHeader->m_hWnd,nPos);
break;
case SB_THUMBTRACK:
ListThumbTrack(m_pLeftHeader->m_hWnd,nPos);
break;
default:
break;
}
CListView::OnVScroll(nSBCode, nPos, pScrollBar);
}
同步控制函数如下:
void CMatrixView::ListLineDown(HWND hWnd)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
if(m_nCurrFirstRow<=info.nMax-info.nPage)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0);
m_nCurrFirstRow++;
}
}
void CMatrixView::ListLineUp(HWND hWnd)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
if(m_nCurrFirstRow>0)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0);
m_nCurrFirstRow--;
}
}
void CMatrixView::ListPageDown(HWND hWnd)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
int n=min(info.nPage, info.nMax-info.nPage-m_nCurrFirstRow+1);
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0);
m_nCurrFirstRow++;
}
}
void CMatrixView::ListPageUp(HWND hWnd)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
int n=min(info.nPage, m_nCurrFirstRow);
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0);
m_nCurrFirstRow--;
}
}
void CMatrixView::ListThumbPosition(HWND hWnd,UINT nPos)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
if(nPos>m_nCurrFirstRow)
{
int n=nPos-m_nCurrFirstRow;
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0);
m_nCurrFirstRow++;
}
}
else
{
int n=m_nCurrFirstRow-nPos;
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0);
m_nCurrFirstRow--;
}
}
}
void CMatrixView::ListThumbTrack(HWND hWnd,UINT nPos)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
if(nPos>m_nCurrFirstRow)
{
int n=nPos-m_nCurrFirstRow;
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0);
m_nCurrFirstRow++;
}
}
else
{
int n=m_nCurrFirstRow-nPos;
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0);
m_nCurrFirstRow--;
}
}
}
当对话框的窗口拉大到超过列表最大行数的时候,CMatrixView的滚动条会回滚,这里我们同样要控制CMatrixLeftHeader回滚,相关函数如下:
void CMatrixView::ListScrollPageChange(HWND hWnd)
{
SCROLLINFO info;
GetScrollInfo(SB_VERT,&info,SIF_ALL);
int n=m_nCurrFirstRow+info.nPage-info.nMax-1;
for(int i=0;i<n;i++)
{
::SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0);
m_nCurrFirstRow--;
}
}
可以看到,整个控制过程只要算清楚滚动的行数即可。至此,两个CListView就可以实现同步滚动了。效果如以下网址中所示:
http://my.youkuaiyun.com/my/album/detail/1154049
http://my.youkuaiyun.com/my/album/detail/1154050