原文地址:http://www.vckbase.com/index.php/wv/237
为了最大限度的发挥属性页的效用,首先让我们先从 CPropertySheet 继承一个新类,取名为 CMyPropSheet.
接着便可以进行下面的各种操作:
一、隐藏属性页默认按钮
隐藏掉Apply应用按钮:
1.
propsheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;
或隐藏掉Cancel取消按钮:
1.
CWnd *pWnd = GetDlgItem( IDCANCEL );
2.
pWnd->ShowWindow( FALSE );
二、移动属性页按钮
首先,要获取按钮的句柄,然后就可以象对待窗体一样处理它们了. 下面代码先隐藏掉Apply和Help铵钮,再把OK和Cancel按移动到右侧。
01.
BOOL
CMyPropSheet::OnInitDialog ()
02.
{
03.
BOOL
bResult = CPropertySheet::OnInitDialog();
04.
05.
int
ids [] = {IDOK, IDCANCEL};
//, ID_APPLY_NOW, IDHELP };
06.
07.
// Hide Apply and Help buttons
08.
CWnd *pWnd = GetDlgItem (ID_APPLY_NOW);
09.
pWnd->ShowWindow (FALSE);
10.
pWnd = GetDlgItem (IDHELP);
11.
pWnd->ShowWindow (FALSE);
12.
13.
CRect rectBtn;
14.
int
nSpacing = 6;
// space between two buttons...
15.
16.
for
(
int
i =0; i <
sizeof
(ids)/
sizeof
(
int
); i++)
17.
{
18.
GetDlgItem (ids [i])->GetWindowRect (rectBtn);
19.
20.
ScreenToClient (&rectBtn);
21.
int
btnWidth = rectBtn.Width();
22.
rectBtn.left = rectBtn.left + (btnWidth + nSpacing)* 2;
23.
rectBtn.right = rectBtn.right + (btnWidth + nSpacing)* 2;
24.
25.
GetDlgItem (ids [i])->MoveWindow(rectBtn);
26.
}
27.
28.
29.
return
bResult;
30.
}
下面代码移动所有按钮到右侧,并且重新置属性页为合适的大小.
01.
BOOL
CMyPropSheet::OnInitDialog ()
02.
{
03.
BOOL
bResult = CPropertySheet::OnInitDialog();
04.
05.
06.
int
ids[] = { IDOK, IDCANCEL, ID_APPLY_NOW };
07.
08.
CRect rectWnd;
09.
CRect rectBtn;
10.
11.
GetWindowRect (rectWnd);
12.
GetDlgItem (IDOK)->GetWindowRect (rectBtn);
13.
14.
int
btnWidth = rectBtn.Width();
15.
int
btnHeight = rectBtn.Height();
16.
int
btnOffset = rectWnd.bottom - rectBtn.bottom;
17.
int
btnLeft = rectWnd.right - rectWnd.left;
18.
19.
rectWnd.bottom = rectBtn.top;
20.
rectWnd.right = rectWnd.right + btnWidth + btnOffset;
21.
MoveWindow(rectWnd);
22.
23.
rectBtn.left = btnLeft;
24.
rectBtn.right = btnLeft + btnWidth;
25.
26.
for
(
int
i = 0; i <
sizeof
(ids) /
sizeof
(
int
); i++)
27.
{
28.
rectBtn.top = (i + 1) * btnOffset + btnHeight * i;
29.
rectBtn.bottom = rectBtn.top + btnHeight;
30.
GetDlgItem (ids [i])->MoveWindow (rectBtn);
31.
}
32.
33.
return
bResult;
34.
}
三、改变属性页上的标签文字
首先修改TC_ITEM结构,然后用 SetItem 来修改标签文字,如下代码:
1.
TC_ITEM item;
2.
item.mask = TCIF_TEXT;
3.
item.pszText =
"New Label"
;
4.
5.
//Change the label of the first tab (0 is the index of the first tab)...
6.
GetTabControl ()->SetItem (0, &item);
四、改变属性页标签文字的字体属性
代码如下
1.
m_NewFont.CreateFont (14, 0, 0, 0, 800, TRUE, 0, 0, 1, 0, 0, 0, 0, _T(
"Arial"
) );
2.
GetTabControl()->SetFont (&m_NewFont);
五、在属性页标签上显示位图
可以用 CImageList 建立图像. 用 SetItem 来设置,如下代码所示:
01.
BOOL
CMyPropSheet::OnInitDialog ()
02.
{
03.
BOOL
bResult = CPropertySheet::OnInitDialog();
04.
05.
m_imageList.Create (IDB_MYIMAGES, 13, 1, RGB(255,255,255));
06.
CTabCtrl *pTabCtrl = GetTabControl ();
07.
pTabCtrl->SetImageList (&m_imageList);
08.
09.
TC_ITEM item;
10.
item.mask = TCIF_IMAGE;
11.
for
(
int
i = 0; i < NUMBER_OF_TABS; i++)
12.
{
13.
item.iImage = i;
14.
pTabCtrl->SetItem (i, &item );
15.
}
16.
17.
return
bResult;
18.
}
六、在属性页左下角显示位图
如下代码所示:
01.
void
CMyPropSheet::OnPaint ()
02.
{
03.
CPaintDC dc(
this
);
// device context for painting
04.
05.
int
nOffset = 6;
06.
// load IDB_BITMAP1 from our resources
07.
CBitmap bmp;
08.
if
(bmp.LoadBitmap (IDB_BITMAP1))
09.
{
10.
// Get the size of the bitmap
11.
BITMAP bmpInfo;
12.
bmp.GetBitmap (&bmpInfo);
13.
14.
// Create an in-memory DC compatible with the
15.
// display DC we''re using to paint
16.
CDC dcMemory;
17.
dcMemory.CreateCompatibleDC (&dc);
18.
19.
// Select the bitmap into the in-memory DC
20.
CBitmap* pOldBitmap = dcMemory.SelectObject (&bmp);
21.
22.
// Find a bottom-left point for the bitmap in the client area
23.
CRect rect;
24.
GetClientRect (&rect);
25.
int
nX = rect.left + nOffset;
26.
int
nY = rect.top + (rect.Height () - bmpInfo.bmHeight) - nOffset;
27.
28.
// Copy the bits from the in-memory DC into the on-
29.
// screen DC to actually do the painting. Use the centerpoint
30.
// we computed for the target offset.
31.
dc.BitBlt (nX, nY, bmpInfo.bmWidth, bmpInfo.bmHeight, &dcMemory,
32.
0, 0, SRCCOPY);
33.
34.
dcMemory.SelectObject (pOldBitmap);
35.
}
36.
37.
// Do not call CPropertySheet::OnPaint() for painting messages
38.
}
七、在属性页右下角显示3D文字Logo
代码如下:
01.
void
CMyPropSheet::OnPaint ()
02.
{
03.
/////////////////////////////////////////////////////////////////
04.
//在TAB按钮旁边显示3D文字提示,jingzhou xu
05.
Cstring m_LogoName = “属性页”;
06.
// if(m_LogoName == "")
07.
// return;
08.
09.
GetWindowRect(rect);
10.
ScreenToClient(rect);
11.
12.
LOGFONT logFont;
13.
ZeroMemory((
void
*)&logFont,
sizeof
(logFont));
14.
strcpy
(logFont.lfFaceName,
"宋体"
);
15.
logFont.lfHeight = -12;
16.
logFont.lfWeight = 400;
17.
logFont.lfCharSet = GB2312_CHARSET;
18.
logFont.lfOutPrecision = 3;
19.
logFont.lfClipPrecision = 2;
20.
logFont.lfQuality = 1;
21.
logFont.lfPitchAndFamily = 2;
22.
m_font.CreateFontIndirect(&logFont);
23.
SetFont(&m_font);
24.
CFont *pOldFont = pDC->SelectObject(&m_font);
25.
26.
rect.left += 6;
27.
rect.right -= 6;
28.
rect.bottom -= 1;
29.
rect.top = rect.bottom - ITEMBUTTON_HEIGHT + 1;
30.
31.
32.
CFont m_LogoFont;
33.
CString sLogoString;
34.
35.
m_LogoFont.CreateFont(rect.Height()*4/5, 0, 0, 0, FW_BOLD, 1, FALSE, FALSE,
36.
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
37.
FIXED_PITCH | FF_ROMAN,
"楷体_GB2312"
);
38.
39.
sLogoString = m_LogoName;
40.
41.
RECT m_rDataBox;
42.
CopyRect(&m_rDataBox,&rect);
43.
44.
TEXTMETRIC
tm
;
45.
pDC->GetTextMetrics(&
tm
);
46.
CFont* oldFont = pDC->SelectObject(&m_LogoFont);
47.
CSize sz = pDC->GetTextExtent(sLogoString, sLogoString.GetLength());
48.
//用GetTextExtent来计算字体logo大小,依靠于设备环境,使用logo位于右下角
49.
m_rDataBox.left = m_rDataBox.right - sz.cx -
tm
.tmAveCharWidth/2;
50.
m_rDataBox.top = m_rDataBox.bottom - sz.cy -
tm
.tmHeight/5;
51.
pDC->SetBkMode(TRANSPARENT);
52.
//用3D字体显示,先黑后白,最后再用默认色
53.
COLORREF
oldColor = pDC->SetTextColor(GetSysColor(COLOR_3DDKSHADOW));
54.
pDC->DrawText(sLogoString, sLogoString.GetLength(), &m_rDataBox, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
55.
m_rDataBox.left -=
tm
.tmAveCharWidth;
56.
pDC->SetTextColor(GetSysColor(COLOR_3DHILIGHT));
57.
pDC->DrawText(sLogoString, sLogoString.GetLength(), &m_rDataBox, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
58.
m_rDataBox.left += 3*
tm
.tmAveCharWidth/5;
59.
pDC->SetTextColor(RGB(0,0,255));
60.
pDC->DrawText(sLogoString, sLogoString.GetLength(), &m_rDataBox, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
61.
62.
//释放资源
63.
pDC->SelectObject(oldFont);
64.
pDC->SetTextColor(oldColor);
65.
m_LogoFont.DeleteObject();
66.
//////////////////////////////////////////////////////////////////
67.
}
八、在属性页中动态加入其它控件
下面演示如何在左下角加入一Edit控件:
MyPropSheet.h中:
1.
public
:
2.
CEdit m_edit;
MyPropSheet.cpp中:
01.
BOOL
CMyPropSheet::OnInitDialog ()
02.
{
03.
BOOL
bResult = CPropertySheet::OnInitDialog ();
04.
05.
06.
CRect rect;
07.
08.
int
nHeight = 24;
09.
int
nWidth = 120;
10.
int
nOffset = 6;
11.
12.
GetClientRect (&rect);
13.
14.
// Find a bottom-left point for the edit control in the client area
15.
int
nX = rect.left + nOffset;
16.
int
nY = rect.top + (rect.Height() - nHeight) - nOffset;
17.
18.
// finally create the edit control
19.
m_Edit.CreateEx (WS_EX_CLIENTEDGE, _T(
"EDIT"
), NULL,
20.
WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER,
21.
nX, nY, nWidth, nHeight, m_hWnd, 0, 0 );
22.
23.
return
bResult;
24.
}
首先要了解的是CPropertySheet实际上是由一个TabCtrl加多个Page和多个按钮组成
这里强调一点,DoModal的时候并没有把所有的PAGE都创建出来,只有在SetActivePage的时候才创建
所以如果你初始化的代码在OnInitDialog,那么刚开始最好先用SetActivePage切换一下
在讲述之前,大家可以先看看《深入浅出CPropertySheet》(翻译的不错)
http://www.vckbase.com/document/viewdoc/?id=427
其实原文后面还有一个例子,不过原理上帖中也表现出来了,就是窗口的移动MoveWindow和HeaderCtrl的创建等
参见 http://www.codeguru.com/Cpp/controls/propertysheet/article.php/c3973/
所以在此一些简单按钮的隐藏和移动及其Enable使能就不多说了
一、布局修改
a.修改字体
重载OnInitDialog,CreateFont创建字体pFont
SendMessageToDescendants(WM_SETFONT, (WPARAM)pFont->GetSafeHandle(), TRUE);
如果字体的改变(如高度、宽度)可能需要改变控件的位置,这样看起来才和谐一点,参见
Prpfont.exe sample demonstrates how to set the desired font for your CPropertyPages in Visual C++
http://support.microsoft.com/default.aspx?scid=kb;en-us;142170
b.设置初始的焦点
GetPage(0)->SetModified ();//使应用按钮有效
SendMessage(DM_SETDEFID,ID_APPLY_NOW); //也可设置到自己添加的按钮中
c.改变大小Resizable(这类文章和发问比较多,所以主要帖URL,见谅)
使用ResizableLib(适用于几乎所有窗口,Dialog,PropSheet,SDI,MDI等)
http://www.codeproject.com/dialog/ResizableLib.asp
也可参看Resizable CPropertySheet
http://www.codeproject.com/property/resizableproperties.asp
http://www.codeguru.com/Cpp/controls/propertysheet/sizing/article.php/c599/
运行期间动态进行调整大小,避免切换后又恢复原来大小,参见
http://support.microsoft.com/default.aspx?scid=kb;en-us;143291
注:如果自己在OnInitDialog做MoveWindow的时候要记住代码放到CPropertySheet::OnInitDialog();后
d.向导方式
最简单的就是设置模式 SetWizardMode //注意最前和最后页的SetWizardButtons
后来添加了Wizard97 风格,主要有两块地方填充的,左边的叫WATERMARK和上部的叫HEADER
注:左边和上部一般不能同时显示,所以要想显示WATERMARK,需要在PAGE中设置PSP_HIDEHEADER风格(VC7默认才设了)
添加WIZARD97风格,同时设置对应的位图
psh.dwFlags = PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER);
如果要HEADER中显示文本TITLE也可设置PSP_USEHEADERTITLE和PSP_USEHEADERSUBTITLE,如
psp.dwFlags = PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_TITLE1);
psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_SUBTITLE1);
更多的详情有兴趣的可自行搜索,如6.0版本中PROPSHEETPAGE新添的hActCtx等,在此点到为止
e.内嵌入对话框中和Modeless
ps.Create(this);//如果要内嵌添加,WS_CHILD|WS_VISIBLE
ps.ShowWindow(SW_SHOW);
但这时候出来的是没有标准按钮,想要添加可重载OnInitDialog
BOOL CMySheet::OnInitDialog()
{
m_bModeless = FALSE;
m_nFlags |= WF_CONTINUEMODAL;
BOOL bResult = CPropertySheet::OnInitDialog();
m_bModeless = TRUE;
m_nFlags &= ~WF_CONTINUEMODAL;
return bResult;
}
P.S.焦点切换后子属性框(modeless)僵死,请在OnInitDialog中添加
ModifyStyleEx(0,WS_EX_CONTROLPARENT);
二、标签TabCtrl的操作
a.修改Text
有三种方法
m_pPage1=newCPage1(IDS_MYCAPTION);//idofstringresource
m_pPage1->m_psp.dwFlags|=PSP_USETITLE;
m_pPage1->m_psp.pszTitle=_T("MyCaption");
运行时也可修改标签
TC_ITEM ti;
ti.mask=TCIF_TEXT;
ti.pszText=pszText;
VERIFY(GetTabControl()->SetItem(nPage,&ti));
b.Tab使用Scrollbar而不是用多行
EnableStackedTabs(FALSE);即可,原理就是GetTabControl()->ModifyStyle(TCS_MULTILINE, TCS_SINGLELINE)
c.如何调整标签位置(下面或者左右)一般自画,参考:
http://www.codeguru.com/Cpp/controls/controls/tabcontrols/article.php/c6385/
d.Disable Tab中如果要重画TabItem使其看起来灰化,也可以看Paul的CTabCtrlWithDisable
http://www.microsoft.com/msj/0398/c0398.aspx
e.CPropertySheet页中的标题栏中显示指定数量的属性页,如:标题栏要求每行显示5个属性页
可使用 MutliRowTabView 参见 http://www.codeguru.com/Cpp/controls/propertysheet/article.php/c623/
三、消息的响应
a.获得CPropertySheet的WM_CLOSE消息
http://dev.csdn.net/develop/article/35/35982.shtm比较简便地实现了此功能,
但需要覆盖每个属性页的OnApply函数,且不利于宏观同步未完成的任务.
现提供一个通过覆盖CMyPropertySheet类的OnCommand函数实现此功能的方法.
OnCommand的实现代码如下:
BOOL CMyPropertySheet::OnCommand(WPARA MwParam,LPARAM lParam)
b.NOTIFY的响应 如:Disable 某个TAB
BOOL CMySheet::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
NMHDR* pnmh = (NMHDR*)lParam;
// tab is about to change
if (TCN_SELCHANGING == pnmh->code)
// save the current page index
m_nLastActive = GetActiveIndex ();
// tab has been changed
else if (TCN_SELCHANGE == pnmh->code)
{
// get the current page index
int nCurrentPage = GetActiveIndex ();
// if current page is in our map of disabled pages
if (m_DisabledPages.Lookup (nCurrentPage, nCurrentPage))
// activate the previous page
PostMessage (PSM_SETCURSEL, m_nLastActive);
}
return CPropertySheet::OnNotify(wParam, lParam, pResult);
}
c.如何响应上下文帮助
Title Bar添加问号(?)按钮的响应:添加WN_HELP响应
ON_WM_HELPINFO()//添加函数BOOL OnHelpInfo(HELPINFO*)
如果响应底部的帮助按钮(原理:一般按钮的点击映射)
ON_COMMAND(IDHELP, OnHelp)//添加函数BOOL OnHelp()
将标签对话框作为一个控件在对话框中实现也是现在比较常见的用户界面,见图5实现的示例。通过它可以将几个不同的模块在一个界面中同用户交互来实现功能,简洁明快和设置手段集中是它的特点。实现的方法简述如下:
①完成标签对话框中标签页的设计,这里我们举例有三个标签页,分别为CPage1、 CPage2和CPage3三个CPropertyPage的派生类;
②在项目中新建一个将包含标签对话框控件的对话框模版,相应的对话框派生类为CDlg,在该模版中加入除标签对话框外的其他控件,布局要考虑清楚。
③在对话框中希望出现标签对话框的位置上放置一个Picture控件,资源ID命名为IDC_STATICPIC,其type为Frame,其余的取缺省值;该Picture控件的尺寸是与前面设计好的标签页的大小有关,经验的方法可以做:找到最大的标签页,在其上放置一个覆盖全部控件域的Picture控件,考察它的Width和Height,前面提到的IDC_STATICPIC的高度与Height近似,宽度比Width略小;当然这里只是一个指导性的方法,具体的Width和Height应该视情况在程序中进行调整,方能做到结果满意。
④在CDlg类中加入下列公有成员数据:
public:
CPropertySheet m_sheet;
CPage1 m_page1;
CPage2 m_page2;
CPage3 m_page3;
⑤在CDlg类中重载WM_INITDIALOG消息的处理函数OnInitDialog,加入下面的代码:
// TODO: Add extra initialization here
m_sheet.AddPage(&m_page1);
m_sheet.AddPage(&m_page2);
m_sheet.AddPage(&m_page3);
//创建标签对话框控件
m_sheet.Create(this, WS_CHILD | WS_VISIBLE, 0);
//使Tab键消息有效,可以通过Tab键转移输入焦点
m_sheet.ModifyStyleEx (0, WS_EX_CONTROLPARENT);
m_sheet.ModifyStyle( 0, WS_TABSTOP );
//确定标签对话框控件在对话框中出现的位置
CRect rcSheet;
GetDlgItem( IDC_STATICPIC )->GetWindowRect( &rcSheet );
ScreenToClient( &rcSheet );
m_sheet.SetWindowPos( NULL,rcSheet.left, rcSheet.top, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE );
至此工作已经完成,值得注意的是,关于标签对话框出现的位置和大小是一个相对棘手的问题,需要在编程时慢慢模索,还有要想保留在标签对话框中的设置,则需要在对话框的DoModal()返回值为IDOK时进行相应的编程。
联系客服