最近遇到了这样的问题,使用某个皮肤软件来为界面换皮肤,但必须取得控件的句柄,才能对控件换皮肤。对于标准控件,如CEdit,CRichEdit, CTreeCtrl等控件,内建的滚动条句柄无法取得,为滚动条换肤就成了问题。于是,本人用自建的滚动条代替那些控件内建的滚动条,来为滚动条换肤.。
控件内建的滚动条属于控件的非客户区,因而可考虑把非客户区去掉,然后在客户区建一个滚动条,来代替内建的滚动条。但要考虑以下问题:
1. 如何把非客户区(内建滚动条区域)去掉。
2. 自建的滚动条如何与内建的滚动条显示保持一致。主要是何时显示与隐藏,以及滚动保持一致。
3. 控件的数据内容不能覆盖滚动条的显示。
以下例子在CEdit中创建自建的垂直滚动条。.cpp文件主要代码如下:
void CScrollEdit::PreSubclassWindow()
{
//自建滚动条 ,m_vBar为自建滚动条 ,BAR_WIDTH为m_vBar的宽度 =16
CRect rc;
GetClientRect(&rc);
rc.left =rc.right - BAR_WIDTH;
m_vBar.Create(WS_CHILD|SBS_VERT,rc,this,SB_VERT_ID);
EnableScrollBarCtrl(SB_VERT);
CEdit::PreSubclassWindow();
}
void CScrollEdit::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
{
//解决问题1
// CEdit::OnNcCalcSize(bCalcValidRects, lpncsp);
//把原来的消息处理方法删去, 这样非客户区(内建的滚动条区域)将不会创建.
}
void CScrollEdit::OnSize(UINT nType, int cx, int cy)
{ //解决问题2
CEdit::OnSize(nType, cx, cy);
m_vBar.SetWindowPos(0,cx - BAR_WIDTH,0,BAR_WIDTH,cy,SWP_NOZORDER);
m_vBar.Invalidate();
ShowBarState(); //决定是否显示滚动条
}
void CScrollEdit::OnUpdate()
{ //解决问题2,3
SCROLLINFO ScrInfo;
GetScrollInfo(SB_VERT,&ScrInfo);
m_vBar.SetScrollInfo(&ScrInfo);
m_vBar.Invalidate();
}
HBRUSH CScrollEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{ //解决问题3
m_vBar.Invalidate();
return NULL;
}
void CScrollEdit::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
//解决问题2,3
SCROLLINFO ScrInfo;
GetScrollInfo(SB_VERT,&ScrInfo);
m_vBar.SetScrollInfo(&ScrInfo);
int pos;
pos = m_vBar.GetScrollPos();
switch ( nSBCode )
{
case SB_LINEUP:
pos -= 1;
break;
case SB_LINEDOWN:
pos += 1;
break;
case SB_PAGEUP:
pos -= 10;
break;
case SB_PAGEDOWN:
pos += 10;
break;
case SB_TOP:
pos = ScrInfo.nMin;
break;
case SB_BOTTOM:
pos = ScrInfo.nMax;
break;
case SB_THUMBPOSITION:
pos = nPos;
break;
default:
return;
}
if ( pos < ScrInfo.nMin )
pos = ScrInfo.nMin;
else if ( pos > ScrInfo.nMax )
pos = ScrInfo.nMax;
m_vBar.SetScrollPos(pos);
CEdit::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CScrollEdit::OnVscroll()
{
//解决问题2,3
CRect rc;
GetClientRect(&rc);
SCROLLINFO ScrInfo;
GetScrollInfo(SB_VERT,&ScrInfo);
m_vBar.SetScrollInfo(&ScrInfo);
m_vBar.ShowWindow(TRUE);
}
void CScrollEdit::OnChange()
{
//解决问题2
ShowBarState();
}
void CScrollEdit::ShowBarState() //决定是否显示滚动条
{
//解决问题2
int li = LineIndex(0);
CPoint po = PosFromChar(li + 1);
CRect rc;
GetClientRect(&rc);
if(po.y < -1)
{
m_vBar.ShowWindow(TRUE);
rc.right -= BAR_WIDTH;
}
else
{
m_vBar.ShowWindow(FALSE);
}
SetRect(&rc);
}
另外要注意,CSrollEdit是通过重写OnNcCalcSize事件处理过程来去掉内建的滚动条,所以在父窗体中Edit控件如果不是动态创建的,需要在父窗体中的OnInitDialog方法中Edit控件(已用CSrollEdit子类化了)调用SetWindowPos方法来触发OnNcCalcSize事件。
控件内建的滚动条属于控件的非客户区,因而可考虑把非客户区去掉,然后在客户区建一个滚动条,来代替内建的滚动条。但要考虑以下问题:
1. 如何把非客户区(内建滚动条区域)去掉。
2. 自建的滚动条如何与内建的滚动条显示保持一致。主要是何时显示与隐藏,以及滚动保持一致。
3. 控件的数据内容不能覆盖滚动条的显示。
以下例子在CEdit中创建自建的垂直滚动条。.cpp文件主要代码如下:
void CScrollEdit::PreSubclassWindow()
{
//自建滚动条 ,m_vBar为自建滚动条 ,BAR_WIDTH为m_vBar的宽度 =16
CRect rc;
GetClientRect(&rc);
rc.left =rc.right - BAR_WIDTH;
m_vBar.Create(WS_CHILD|SBS_VERT,rc,this,SB_VERT_ID);
EnableScrollBarCtrl(SB_VERT);
CEdit::PreSubclassWindow();
}
void CScrollEdit::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
{
//解决问题1
// CEdit::OnNcCalcSize(bCalcValidRects, lpncsp);
//把原来的消息处理方法删去, 这样非客户区(内建的滚动条区域)将不会创建.
}
void CScrollEdit::OnSize(UINT nType, int cx, int cy)
{ //解决问题2
CEdit::OnSize(nType, cx, cy);
m_vBar.SetWindowPos(0,cx - BAR_WIDTH,0,BAR_WIDTH,cy,SWP_NOZORDER);
m_vBar.Invalidate();
ShowBarState(); //决定是否显示滚动条
}
void CScrollEdit::OnUpdate()
{ //解决问题2,3
SCROLLINFO ScrInfo;
GetScrollInfo(SB_VERT,&ScrInfo);
m_vBar.SetScrollInfo(&ScrInfo);
m_vBar.Invalidate();
}
HBRUSH CScrollEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{ //解决问题3
m_vBar.Invalidate();
return NULL;
}
void CScrollEdit::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
//解决问题2,3
SCROLLINFO ScrInfo;
GetScrollInfo(SB_VERT,&ScrInfo);
m_vBar.SetScrollInfo(&ScrInfo);
int pos;
pos = m_vBar.GetScrollPos();
switch ( nSBCode )
{
case SB_LINEUP:
pos -= 1;
break;
case SB_LINEDOWN:
pos += 1;
break;
case SB_PAGEUP:
pos -= 10;
break;
case SB_PAGEDOWN:
pos += 10;
break;
case SB_TOP:
pos = ScrInfo.nMin;
break;
case SB_BOTTOM:
pos = ScrInfo.nMax;
break;
case SB_THUMBPOSITION:
pos = nPos;
break;
default:
return;
}
if ( pos < ScrInfo.nMin )
pos = ScrInfo.nMin;
else if ( pos > ScrInfo.nMax )
pos = ScrInfo.nMax;
m_vBar.SetScrollPos(pos);
CEdit::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CScrollEdit::OnVscroll()
{
//解决问题2,3
CRect rc;
GetClientRect(&rc);
SCROLLINFO ScrInfo;
GetScrollInfo(SB_VERT,&ScrInfo);
m_vBar.SetScrollInfo(&ScrInfo);
m_vBar.ShowWindow(TRUE);
}
void CScrollEdit::OnChange()
{
//解决问题2
ShowBarState();
}
void CScrollEdit::ShowBarState() //决定是否显示滚动条
{
//解决问题2
int li = LineIndex(0);
CPoint po = PosFromChar(li + 1);
CRect rc;
GetClientRect(&rc);
if(po.y < -1)
{
m_vBar.ShowWindow(TRUE);
rc.right -= BAR_WIDTH;
}
else
{
m_vBar.ShowWindow(FALSE);
}
SetRect(&rc);
}
另外要注意,CSrollEdit是通过重写OnNcCalcSize事件处理过程来去掉内建的滚动条,所以在父窗体中Edit控件如果不是动态创建的,需要在父窗体中的OnInitDialog方法中Edit控件(已用CSrollEdit子类化了)调用SetWindowPos方法来触发OnNcCalcSize事件。