VC++:编程技巧
VC++给人的第一感觉便是过于专业化,高深和晦涩。其实也并非如此,在基本掌握了它之后你就会发现,VC++使用起来是很方便的。至于说难,那是有点。但这并非是VC++的过错,而是Windows,这个庞大无比对用户亲切之至却对程序员
不那么友好的家伙。再说,VC++系出自名门,微软的哪样东西不是先把你服侍得舒舒服服,然后再把你口袋中的钱掏个精光呢?好,下面就向大家介绍几个在使用中的小技巧。
1.使用中文
VC++从1.5版到现在的6.0版,好像还没有出现过像VB一样的中文版。大概是
Microsoft认为能够用VC++编程的人英文水平都很高吧,但这对用户可不行啊。VC++在中文平台下可以使用中文,但编译后那些按钮上的、对话框上的中文都成ASCII码了。何故?因为VC++在安装时是默认按单词节字符安装的,而中文字符是
双字节编码,自然就不能正确显示了。要解决这个问题其实也很容易,将VC++光
盘上DEVSTUDIOSHAREDIDEBINIDE路径下的中文资源语言模块Appwzchs.dll拷贝至
硬盘DevStudioSharedIDEBINIDE路径下即可。
2.位图按钮的使用
我们知道Windows是一个图形界面的操作系统,如果在我们的应用程序中加入
一些图形的话,会起到锦上添花的效果。在VC++中使用位图,并不像在VB中信手
拈来那么简单。不过也并不复杂,其中以位图按钮用途较广。
选定要使用位图的按钮(以OK按钮为例,假设其标识符为IDC_BtonOK),选
其属性中的Owner draw选项,此时,在对话框编辑器中就可看到原先显示在按钮
上的字符都消失了。将按钮的Caption改为OK(必须为大写)。打开Insert菜单,
单击其中的Resource选项,随后选择Bitmap。再按下Import(导入)按钮,将所
需位图导入项目(Project)。可以在Resource View窗口中通过右键单击刚才导
入的位图,将其ID(标识符)改为"OKU",注意:字符必须为大写,双引号及字母
U必不可少。字母U代表的按钮为按下状态时所显示的位图。此外还可使用后缀D、
F、X,分别表示按下、拥有输入焦点时、按钮处于无效状态时所显示的位图。通过对同一个按钮的不同状态使用不同的位图,很容易做出具有动态效果的按钮。
当将按钮所需使用的位图导入到项目中后,就应该在使用位图按钮的那个对话框的类声明文件中加入位图按钮变量的定义CBitmapButton m_BtonOK。同时,在适当的位置(一般是在对话框的OnInitDialog()函数中)加入如下语句:
m_BtonOK.AutoLoad(IDC_BtonOK),使程序在运行时将位图装入内存并显示。至
此,创建位图按钮的整个步骤即告结束。
上述方法不仅限于创建位图按钮,还可用于在对话框上显示简单的位图。嘘,告诉你一个小秘密,位图按钮所使用的颜色最多可达256种,而非位图编辑器中
的16种。但对于256色位图,当在导入VC++的位图编辑器后就不能使用该编辑器作
任何修改。否则在编译连接后,原先256色位图就变成16色位图了。
3.通用对话框的使用
在编程中,自己要设计存储或打开文件时的对话框是件很头疼的事。不过Windows既然已经为我们提供好了现成的通用对话框,只管拿来用就是了。先在使
用文件对话框的类的类定义文件中加入对象定义CFileDialog
m_OpenDialog(TRUE,"txt","*.txt")。随后,在需要使用该对话框的地方加入以下语句:m_OpenDialog.Domodal();
其调用规则为:
CFileDialog(BOOL bOpenFileDialog,LPCTSTR lpszDefExt=NULL,LPCTSTR lpszFileName=NULL,DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPR OMPT,LPCTSTR lpszFilter=NULL,CWnd * pParentWnd=NULL);
第一个参数bOpenFileDialog为TRUE或FALSE。TRUE为打开文件;FALSE为保存文件。
第二个参数lpszDefExt为缺省的扩展名。
第三个参数lpszFileName为显示在文件名组合框的编辑框的文件名,一般可
选NULL第四个参数dwFlags为对话框风格,一般为OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT,即隐藏只读选项和覆盖已有文件前提示。
第五个参数LpszFilter为下拉列表枢中显示文件类型。
第六个参数pParentWnd一般可选NULL。
例如:在文件类型列表框的编辑框要显示"可执行文件(*.exe)",而在它的
下
拉列表框中列出"小型可执行文件(*.com)、批处理文件(*.bat)、All
Files(*.*)"内容,则变量定义如下:CFileDialogc m_OpenDialog(TRUE
m_OpenDialog(TRUE,"exe",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"
可
执行文件(*.exe) | *.exe|小型可执行文件(*.com)|*.com |批处理文件(*.bat) | *.bat | All Files (*.*) | *.* ||",NULL);说明:第五个参数lpszFilter
为
一个指向字符串的指针,在列表框中要显示的字符后跟一个用"|"括起来的字符串
,为要显示文件的文件类型。
4.最高窗口的实现
最高窗口就是总浮动在其他窗口上的,不会被一般窗口遮住的窗口,最高窗口技术在编程中有着很广泛的应用。VC++中对基于SDI、MDI的运用程序,要实现
最高窗口,只要在框架窗口类CMainFrame中的PreCreateWindow()函数中加入"cs.dwExStyle =WS_EX_TOPMOST;"即可。关于函数PreCreateWindow()及结构CREATESTRUCT的详细信息可参见VC++的联机文档。
而对基于对话框的运用程序,如何实现最高窗口却很少论及,以下便是一种实现方法。
重载要实观最高窗口的对话框的OnInitDialog()函数,方法是进入ClassWizard,在Object ID列表框中选择该对话框的ID,在Message列表框中选择
WM_INITDIALOGG,单击Add Function按钮后,即对onlnitDialog函数进行了重载
。再按下Edit code按钮,加入以下语句:
const CWnd * pWndInsertAfter;pWndInsertAfter =
&wndTopMost;SetWindowPos(pWndInsertAfter,0,0,0,0,SWP_NOSIZE |
SWP_NOMOVE);函数SetWindowPos原型为BOOL SetWindowPos(const CWnd * pWndInsertAfter,int x,int y,int cx,int cy,UINT nFlags);pWndInsertAfter 为指向标识窗口类型的CWnd对象的指针。
x,y为窗口左上角的坐标。
cx,cy为窗口的宽与高。nFlags确定窗口的大小及位置。当为SWP_NOSIZE 时
,忽略cx,cy。当为SWP_NOMOVE时,忽略x,y。
5.窗口最大化、最小化的实现
当利用AppWizard生成运用程序框架时,VC++已经为我们在标题条上做好了最
大化,最小化及恢复按钮。但有时我们也想在其它地方使用这些功能。这就可采用下面的办法。在指定的消息处理函效中加入下列语句,则程序在收到该条消息后即执行窗口最小化。
WINDOWPLACEMENT lwndpl;WINDOWPLACEMENT *
lpwndpl;lpwndpl=&lwndpl;GetWindowPlacement(lpwndpl);lpwndpl->showCmd= SW_SHOWMINIMIZED;SetWindowPlacement(lpwndpl);函数BOOL GetWindowPlacement(WINDOWPLACEMENT * lpwndpl) const的作用为获取表示当前
窗口的布局的结构WINDOWPLACEMENT的结构变量指针。结构WINDOWPLACEMENT
定义为:
typedef struct tagWINDOWPLACEMENT {/* wndpl * / UINT length;UINT flags;UINT showCmd;POINT ptMinPosition;POINT ptMaxPosition;RECT rcNormalPosition;} WINDOWPLACEMENT;
其中的成员变量showCmd确定当前窗口的状态。其取值一般为:
SW_HIDE隐藏窗口。SW_MINIMIZE最小化指定的窗口。SW_RESTORE将最大化或
最小化的窗口恢复原来大小。SW_SHOW以原来的大小激话并显示窗口。
SW_SHOWMAXIMIZED激活并最大化窗口。函数BOOL SetWindowPlacement(const WINDOWPLACEMENT * lpwndpl)作用为按结构MENT的设置显示窗口。
6.OCX控件的使用
VC++的初学者可能会很羡慕VB或是C++Builder中那多得令人眼花缭乱且使用
方便的控件。其实,VC++中可使用的控件更多,只不过在通常的主界面上看不到而已。如果打开Project菜单,选择Add To Project选项,在随后出现的子菜单中
,选择Components And Controls,随后就可在Components andControls Gallery
对话框中的列表框中看到Developer Studio Components和Registered ActiveX Controls两个文件夹。随便打开一个文件夹,你会看到什么?这么多控件,够
用
了吧。选定一个,要加入,单击Insert按钮;要帮助,单击More Info按钮。
也可
以直接在对话框编辑器中,右键单击对话框空白处,选择Insert ActiveXControls选项,然后就自己挑选去吧。下面向大家介绍两个常用的控件作为例子。
(1)在前面介绍的位图按钮中,可以用该方式在对话框中加入颜色最多仅256色的位图,现在,利用Microsoft Forms 2.0 Image控件,你就可自由自在的
使用真彩色位图了。方法如下:右键单击要加入位图的对话框空白处,选择Insert ActiveX Controls选项,随后选择Microsoft Forms 2.0 Image控件,加
入后,右键单击该控件,调整该控件的属性。在All标签列出的属性中,Picture 属性决定显示那一幅位图。属性调整完毕后,再编译连接,就一切OK了。
(2)在你的程序中要具有Tips of the Day功能,那定能增色不少。像通用
对话框一样,你无需自己去费时费力,Microsoft已经为你做好了,方法如下:打
开Project菜单,选择Add To Project选项,在随后出现的子菜单中,选择Components And Controls,打开Developer Studio Components选择Tip of the Day,将其加入到Project中去。再编译连接运行,Tips of the Day出来了。
不过
此时Tips of the Day还没有任何内容。此时,需要打开project莱单,选择Add
To Project选项,在打开的子菜单选择New,随后创建一个名为tips.txt的TXT 文
件,并加入到Project中去。用回车符来区分每一条Tip,注意,每条Tip不能以
Tab跳格开头,且长度不能超过1000个字符。这样,加入Tip of the Day就大功告
成了。
也许大家还记得,在用AppWizard创建运用程序框架时,有一步询问程序是否
需要ActiveX控件支持。因此,如果在创建运用程序框架时没有包含这一特性,后
来的Project就不能加入ActiveX控件了。但这也不是绝对的,利用函数AfxEnableControlContainer()就可补上这一特性,在运用程序的
InitInstance()_函数中加入该函数,并在预编译文件StdAfx.h中加入#include 即
可。
VC技巧 - 常用控制之二
可以使用Drag&Drop的CTreeView
CTreeViewExt是MFC使用者可重用的类,该类由CTreeView派生,可以用在文档-
视结构的应用程序中,并且支持Drag&Drop。
使用者所需要做的仅仅是将文件加到AppWizard产生的工程中,(CTreeView为基
类)并且将CTreeView替换为CTreeViewExt。并重载三个Virtual Function。
(WenYY:这很简单吧,下面是源代码,我会在必要的地方加上注释,但由于原作者并未加,所以如果有出入请原谅,作者定义了三处虚拟函数,其作用是让使用者重载后,加入自己的判断条件和结果处理的功能,很巧妙的思想:
CopyItemProperties
IsItemCanBeDroppedOn(HTREEITEM hSource, HTREEITEM hTarget);
BOOL ItemCanBeDragged(HTREEITEM hItem);//检查是否可以对该ITEM
实施Drag&Drop
)
Header file
#if !defined(AFX_TREEVIEWEXT_H__99D8F6F8_79F0_11D1_8DC6_0000E8125FE5_ _INCLUDED_)
#define
AFX_TREEVIEWEXT_H__99D8F6F8_79F0_11D1_8DC6_0000E8125FE5__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// TreeViewExt.h : header file
//
/////////////////////////////////////////
// CTreeViewExt view
class CTreeViewExt : public CTreeView
{
protected:
CTreeViewExt(); // protected constructor used by dynamic creation DECLARE_DYNCREATE(CTreeViewExt)
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTreeViewExt)
protected:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
//}}AFX_VIRTUAL
// Implementation
protected:
HTREEITEM m_hDraggedItem;
BOOL m_bDraggingNow; //标记
CImageList *m_pDragImageList;
virtual ~CTreeViewExt();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// Generated message map functions
protected:
virtual void CopyItemProperties(HTREEITEM hNewItem, HTREEITEM hDraggedItem);
virtual BOOL IsItemCanBeDroppedOn(HTREEITEM hSource, HTREEITEM hTarget);
virtual BOOL ItemCanBeDragged(HTREEITEM hItem);//检查是否可以对该ITEM实施Drag&Drop
//{{AFX_MSG(CTreeViewExt)
afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult);//必须重载的函数
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif
// !defined(AFX_TREEVIEWEXT_H__99D8F6F8_79F0_11D1_8DC6_0000E8125FE5__ INCLUDED_)
Implementation file
// TreeViewExt.cpp : implementation file
//
#include "stdafx.h"
#include "TreeViewExt.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////
// CTreeViewExt
IMPLEMENT_DYNCREATE(CTreeViewExt, CTreeView) CTreeViewExt::CTreeViewExt()
{
m_bDraggingNow = FALSE;
m_hDraggedItem = NULL;
m_pDragImageList = NULL;
}
CTreeViewExt::~CTreeViewExt()
{
}
BEGIN_MESSAGE_MAP(CTreeViewExt, CTreeView)
//{{AFX_MSG_MAP(CTreeViewExt)
ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag) ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////
// CTreeViewExt drawing
void CTreeViewExt::OnDraw(CDC* pDC)
{
CDocument* pDoc = GetDocument();
// TOD add draw code here
}
/////////////////////////////////////////
// CTreeViewExt diagnostics
#ifdef _DEBUG
void CTreeViewExt::AssertValid() const
{
CTreeView::AssertValid();
}
void CTreeViewExt::Dump(CDumpContext& dc) const {
CTreeView::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////
// CTreeViewExt message handlers
void CTreeViewExt::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if (!m_bDraggingNow)
{//先检查是否正在过程中
if (ItemCanBeDragged(pNMTreeView->itemNew.hItem))
{//查询条件,是否允许Drag&Drop
CTreeCtrl& tree = GetTreeCtrl();
tree.SetCapture();
m_bDraggingNow = TRUE;
m_hDraggedItem = pNMTreeView->itemNew.hItem;//保存变量
tree.Select(m_hDraggedItem, TVGN_CARET);
m_pDragImageList = tree.CreateDragImage(m_hDraggedItem); m_pDragImageList->DragEnter(&tree, pNMTreeView->ptDrag); m_pDragImageList->BeginDrag(0, CPoint(0, 0));
}
}
*pResult = 0;
}
void CTreeViewExt::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDraggingNow)
{
CTreeCtrl& tree = GetTreeCtrl();
m_pDragImageList->DragEnter(&tree, point);
m_pDragImageList->DragMove(point);
}
CTreeView::OnMouseMove(nFlags, point);
}
void CTreeViewExt::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bDraggingNow)
{
ReleaseCapture();
m_bDraggingNow = FALSE;
m_pDragImageList->EndDrag();
delete m_pDragImageList;
m_pDragImageList = NULL;
CTreeCtrl& tree = GetTreeCtrl();
UINT flags;
HTREEITEM hTargetItem = tree.HitTest(point, &flags);//得到目标 if (hTargetItem != NULL && IsItemCanBeDroppedOn(m_hDraggedItem, hTargetItem))
{//查询是否可以成功完成,条件是开始时选中的Item和结束使选中的ITEM是否满足你的条件
HTREEITEM hNewItem = tree.InsertItem("Untitled", hTargetItem);
CopyItemProperties(hNewItem, m_hDraggedItem);//进行处理 if (nFlags != MK_CONTROL)
tree.DeleteItem(m_hDraggedItem);
}
m_hDraggedItem = NULL;
}
CTreeView::OnLButtonUp(nFlags, point);
}
BOOL CTreeViewExt::ItemCanBeDragged(HTREEITEM hItem)
{//作用为决定现在是否能开始,可以作为一种运行时的选项
return FALSE;
}
BOOL CTreeViewExt::IsItemCanBeDroppedOn(HTREEITEM hSource, HTREEITEM hTarget)
{//决定现在结束条件是否正常,如不正常则放弃这次操作
return FALSE;
}
void CTreeViewExt::CopyItemProperties(HTREEITEM hNewItem, HTREEITEM hDraggedItem)
{//按照你的需要对两个ITEM进行处理。
}
有关属性对话框(property sheet )的几个提示
闻怡洋
下面的所有例子,都假定你从CPropertySheet中派生了新类。
1、隐藏APPLY按钮
使用 PSH_NOAPPLYNOW 标志.
propsheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;
2、增加新的子窗口
使用成员变量。CEdit m_edit.
BOOL CMyPropSheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
CRect rectWnd;
GetWindowRect(rectWnd);
SetWindowPos(NULL, 0, 0,
rectWnd.Width() + 100,
rectWnd.Height(),
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
m_edit.CreateEx( WS_EX_CLIENTEDGE, _T("EDIT"), NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER,
rectWnd.Width(), 20, 80, 24, m_hWnd, 0, 0 );
m_edit.SetFont( GetFont() );
CenterWindow();
return bResult;
}
3、改变页片上的字体
在 OnInitDialog() 中:
// m_fontEdit is a member variable
// Create a bold font
m_fontEdit.CreateFont( -8, 0, 0, 0, 700, 0, 0, 0, 1,
0, 0, 0, 0, _T("MS Sans Serif") );
GetTabControl()->SetFont( &m_fontEdit );
4、使用Image
m_imageTab为成员变量。
BOOL CMyPropSheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
m_imageTab.Create( IDB_TABIMAGES, 13, 1, RGB(255,255,255) );
CTabCtrl *pTab = GetTabControl();
pTab->SetImageList( &m_imageTab );
TC_ITEM tcItem;
tcItem.mask = TCIF_IMAGE;
for( int i = 0; i <3; i++ )
{
tcItem.iImage = i;
pTab->SetItem( i, &tcItem );
}
return bResult;
}
为TreeCtrl中的项增加ToolTip
文洋译
要点:通过OnToolHitTest来增加ToolTip,CTreeCtrl::HitTest(...)的使用,TTN_NEEDTEXT消息的处理。
定义一个新类,有CTreeCtrl派生
Step 1:打开ToolTip 功能
void CTreeCtrlX::PreSubclassWindow()
{
CTreeCtrl::PreSubclassWindow();
EnableToolTips(TRUE);
}
Step 2: OnToolHitTest() 的重载
首先通过HitTest决定是否增加ToolTip,如果需要则返回非零。
在本例中ToolTip使用了LPSTR_TEXTCALLBACK,而没有立即设定显示字符串。
在本例只在鼠标指向每项的图片时才显示ToolTip
int CTreeCtrlX::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{
RECT rect;
UINT nFlags;
HTREEITEM hitem = HitTest( point, &nFlags );
if( nFlags & TVHT_ONITEMICON )
{
CImageList *pImg = GetImageList( TVSIL_NORMAL );
IMAGEINFO imageinfo;
pImg->GetImageInfo( 0, &imageinfo );
GetItemRect( hitem, &rect, TRUE );
rect.right = rect.left - 2;
rect.left -= (imageinfo.rcImage.right + 2);
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)hitem;
pTI->lpszText = LPSTR_TEXTCALLBACK;
pTI->rect = rect;
return pTI->uId;
}
else if( nFlags & TVHT_ONITEMSTATEICON )
{
CImageList *pImg = GetImageList( TVSIL_NORMAL );
IMAGEINFO imageinfo;
pImg->GetImageInfo( 0, &imageinfo );
GetItemRect( hitem, &rect, TRUE );
rect.right = rect.left - (imageinfo.rcImage.right + 2);
pImg = GetImageList( TVSIL_STATE );
rect.left = rect.right - imageinfo.rcImage.right ;
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)hitem;
pTI->lpszText = LPSTR_TEXTCALLBACK;
pTI->rect = rect;
// return value should be different from that used for item icon return pTI->uId*2;
}
return -1;
}
Step 3: ?? TTN_NEEDTEXT
由于在增加ToolTip是使用用了 LPSTR_TEXTCALLBACK,因此ToolTip在显示
时会发送该消息来得到显示字符串。
BEGIN_MESSAGE_MAP(CTreeCtrlX, CTreeCtrl)
//{{AFX_MSG_MAP(CTreeCtrlX)
:
:
//}}AFX_MSG_MAP
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()
protected:
//{{AFX_MSG(CTreeCtrlX)
:
:
//}}AFX_MSG
afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult );
DECLARE_MESSAGE_MAP()
BOOL CTreeCtrlX::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
// need to handle both ANSI and UNICODE versions of the message TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
CString strTipText;
UINT nID = pNMHDR->idFrom;
// Do not process the message from built in tooltip
if( nID == (UINT)m_hWnd &&
(( pNMHDR->code == TTN_NEEDTEXTA && pTTTA->uFlags & TTF_IDISHWND ) || ( pNMHDR->code == TTN_NEEDTEXTW && pTTTW->uFlags & TTF_IDISHWND ) ) ) return FALSE;
// Get the mouse position
const MSG* pMessage;
CPoint pt;
pMessage = GetCurrentMessage();
ASSERT ( pMessage );
pt = pMessage->pt;
ScreenToClient( &pt );
UINT nFlags;
HTREEITEM hitem = HitTest( pt, &nFlags );
if( nFlags & TVHT_ONITEMICON )
{
int nImage, nSelImage;
GetItemImage( (HTREEITEM ) nID, nImage, nSelImage );
strTipText.Format( "Image : %d", nImage );
}
else
{
strTipText.Format( "State : %d", GetItemState( (HTREEITEM ) nID,
TVIS_STATEIMAGEMASK ) );
}
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, strTipText, 80);
else
_mbstowcsz(pTTTW->szText, strTipText, 80);
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, strTipText, 80);
else
lstrcpyn(pTTTW->szText, strTipText, 80);
#endif
*pResult = 0;
return TRUE; // message was handled
}
VC技巧六 - 工具条和状态条
在状态条上显示当前时间
执行下面五个步骤:
编辑资源。添加一个ID为ID_INDICATOR_TIME的新字符串,并将其内容设置为00:00(或者00:00:00)。状态条使用设置的初始值来计算Pane的大小。在应用程序
中可以通过调用CStatusBar::SetPaneInfo()函数来动态的改变Pane的大小。保存
资源。
编辑Mainfrm.cpp。状态条对象使用indicators[]数组中的数据并且按照其在数组
中的顺序生成状态条,在indicators[]数组的适当位置插入
ID_INDICATOR_TIME。
在CMainFrame类中加入下列的消息映射
Mainfrm.h
//{{AFX_MSG(CMainFrame)
afx_msg void OnUpdateTime(CCmdUI *pCmdUI);
//}}AFX_MSG
Mainfrm.cpp
BEGIN_MESSAGE_MAP(CMainFrame,CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_TIME,OnUpdateTime)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
由于ID_INDICATOR_TIME是一个ID而不是一个类,所以上述消息映射只能手动加入
而不能使用ClassWizard。
注意:如果你的程序还需要增加OnTimer函数(使用ClassWizard),那么你应当先
使用ClassWizard加入ON_WM_TIMER消息映射,然后再手动加入上述消息映射,如
果次序相反,上述消息映射将会被ClassWizard删除掉。
编辑Mainfrm.cpp,加入下列函数:
void CMainFrame::OnUpdateTime(CCmdUI *pCmdUI)
{//状态条时间显示函数
CTime t = CTime::GetCurrentTime();
char szTime[6];
int nHour = t.GetHour();
int nMinute = t.GetMinute();
if (nHour > 12) nHour = nHour - 12;//如要按24小时制显示,请将此行注释掉
wsprintf(szTime, "%i:%02i", nHour, nMinute);//分秒一般习惯用两位表示
//把时间写到Pane
m_wndStatusBar.SetPaneText(m_https://www.doczj.com/doc/4f10442282.html,mandToIndex
(ID_INDICATOR_TIME), LPCSTR(szTime));
pCmdUI->Enable();
}
应用程序在有空闲时间(idle time)时调用这个函数。每当应用程序清空其消息队
列时,它将发送一个WM_IDLEUPDATECMDUI消息(新的idle time)。关于idle time 的更多帮助信息,请参看CWinApp::OnIdle()函数的帮助。
到目前为止,程序还存在一个问题:当用户不操作该应用程序时,应用程序接受不到消息,也就谈不上清空消息队列,所以就不会有idle time,因此显示的时间
就不会得到刷新。我们将在第五步中解决这个问题。
在CMainFrame类OnCreate()中添加下列语句:
m_nIDTimer = ::SetTimer(NULL, 0, 1000, NULL);//发送消息,以便更新状态条时间
上述语句每秒钟发送一个消息到应用程序的消息队列,当应用程序清空其消息队列时,时间显示将被更新。在CMainFrame类析构函数中调用::KillTimer(NULL, m_nIDTimer)
更多信息
当模式对话框弹出时(例如Help的About对话框),应用程序的消息队列由对话框处
理。由于对话框的消息管理不包括idle time处理,所以OnUpdateTime函数不会被
调用。如果你要在应用中使用模式对话框,那么你的项目还要进行下面的改进(加入TimerProc函数以处理WM_TIMER消息):
编辑Mainfrm.h和Mainfrm.cpp。在CMainFrame类中加入下列函数的声明及定义:
Mainfrm.h
static void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT uIDEvent, DWORD dwTime);
Mainfrm.cpp
void CALLBACK CMainFrame::TimerProc(HWND hwnd, UINT uMsg,
UINT uIDEvent, DWORD dwTime)
{
CMainFrame *pMainWnd = (CMainFrame *)AfxGetApp()->m_pMainWnd; ASSERT(uIDEvent == pMainWnd->m_nIDTimer);
CCmdUI cui;
cui.m_nID = ID_INDICATOR_TIME;
cui.m_nIndex = 4;
cui.m_pMenu = NULL;
cui.m_pOther = &pMainWnd->m_wndStatusBar;
pMainWnd->OnUpdateTime(&cui);//调用OnUpdateTime()函数,更新时间显示
}
修改CMainFrame类OnCreate()函数中SetTimer的调用格式:
m_nIDTimer = ::SetTimer(NULL, 0, 1000, TimerProc);
实现平面工具栏的最简单方法
张圣华
在前面两篇文章中,你已经学到了如何实现类似 Word97 的工具栏。如果你不在乎工具栏上的 gripper (参见“如何实现类似 Word97 的工具栏”),则我可
以教你一种最简单的方法来实现。
在建立了工具栏之后,加上下面一句:
m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT);
则你就可以实现不带有gripper的平面工具栏了。太简单了吧。
如何使能和禁止工具条的工具提示
如果设置了CBRS_TOOLTIPS风格位,工具条将显示工具提示,要使能或者禁止工具
提示,需要设置或者清除该风格位。下例通过调用CControlBar : : GetBarStyle
和CControlBar : : SetBarStyle建立一个完成此功能的成员函数:
void CMainFrame : : EnableToolTips ( BOOL bDisplayTips )
{
ASSERT_VALID (m_wndToolBar);
DWORD dwStyle = m _wndToolBar.GetBarStyle ( ) ;
if (bDisplayTips)
dwStyle |=CBRS_TOOLTIPS ;
else
dwStyle & = ~ CBRS_TOOLTIPS ;
m_wndToolBar.SetBarStyle (dwStyle );
}
如何设置工具条标题
工具条是一个窗口,所以可以在调用CWnd : : SetWindowText来设置标题,例子如下:
int CMainFrame : : OnCreate (LPCREATESTRUCT lpCreateStruct )
{
…
// Set the caption of the toolbar .
m_wndToolBar.SetWindowText (_T "Standdard");
如何在代码中获取工具条和状态条的指针
缺省时,工作框创建状态条和工具条时将它们作为主框窗口的子窗口,状态条有
一个AFX_IDW_STATUS_BAR标识符,工具条有一个AFX_IDW_TOOLBAR标识符,下例说
明了如何通过一起调用CWnd: : GetDescendantWindow和AfxGetMainWnd来获取这
些子窗口的指针:
//Get pointer to status bar .
CStatusBar * pStatusBar =
(CStatusBar *) AfxGetMainWnd ( ) -> GetDescendantWindow
(AFX_IDW_STUTUS_BAR);
//Get pointer to toolbar .
CToolBar * pToolBar =
(CToolBar * ) AfxGetMainWnd ( ) -> GetDescendantWindow (AFX_IDW_TOOLBAR); VC技巧三 - 界面
在MFC加入"这是什么?"的帮助提示
MFC在CPropertySheet中封装了属性,但不支持标题的"这是什么?"帮助提示.函数
CPropertySheet::OnNcCreate()屏蔽了扩展风格WS_EX_CONTEXTHELP, 因此,即使
你在构造函数中加入了扩展风格,它也不能出现在窗口中.
解决方法很简单,在继承类中设置好风格位,如下所示:
BOOL CWhatsThisPropertySheet::OnNcCreate(
LPCREATESTRUCT lpCreateStruct)
(
if(!CPropertySheet::OnNcCreate(lpCreateStruct))
return FALSE;
//显式地定义此风格
//CPropertySheet默认是关
ModifyStyleEx(0, WS_EX_CONTEXTHELP);
return true;
)
精通工具条
VC++的工具条有很多特性,但有时候也会让你感到困惑. 这里有一些使它们服服贴
贴的小技巧.
要移走一个工具条项,只须将它拖到空工具条的右边,然后点击右上角的关闭按钮.
注意显示在浮动工具条菜单条上的工具条的名称,可以通过右击菜单条来关闭一
个工具条,使之挂起.反过来,激活一个挂起的工具条,右击你菜单条或另一个工具
条,然后选中你想看到的工具条的名字.
如果你想同时开关好几个工具条,不用一个一个地去右击,使用菜单上的工具, 用
户化菜单命令,会出现工具条制表顺序对话框,然后进行选择. 这个对话框也允许
挂起干扰你的工具条,或者增大看不见工具条按钮的图标.
你可以在调试时使用和编辑代码时不同的工具条布局,而二者不互相干扰.当你发
现浮动工具条停在不期望的位置时,按住Ctrl键,然后用鼠标将它移到旁边去, 当
你停下来的时候,它不再回去,即使你把它放在另一个工具条的上面.
如何改变视窗的背景颜色
Windows向窗口发送一个WM_ERASEBKGND消息通知该窗口擦除背景,可以使用ClassWizard重载该消息的缺省处理程序来擦除背景(实际是画),并返回TRUE以防止Windows擦除窗口。
//Paint area that needs to be erased.
BOOL CSampleView : : OnEraseBkgnd (CDC* pDC)
{
// Create a pruple brush.
CBrush Brush (RGB (128 , 0 , 128) );
// Select the brush into the device context .
CBrush* pOldBrush = pDC—>SelcetObject (&brush);
// Get the area that needs to be erased .
CRect reClip ;
pDC—>GetCilpBox (&rcClip);
//Paint the area.
pDC—> PatBlt (rcClip.left , rcClip.top ,
rcClip.Width ( ) , rcClip.Height ( ) , PATCOPY );
//Unselect brush out of device context .
pDC—>SelectObject (pOldBrush );
// Return nonzero to half fruther processing .
return TRUE;
}
为MFC应用程序添加全屏幕显示功能
在CMainFrame类中添加下列成员变量和成员函数(使用ClassWizard),下面是这
些变量和函数的功能说明:
成员变量:
BOOL m_bFullScreen; //全屏幕显示标志
CRect m_FullScreenWindowRect; //全屏幕显示窗口Rect
WINDOWPLACEMENT m_wpPrev; //用于保存正常视图时的窗口位置信息
CToolBar * m_wndFullScreenBar; //全屏幕显示时的浮动工具条
成员函数:
void OnMenuFullscreen(); //全屏幕显示的处理函数
void OnGetMinMaxInfo(); //捕获WM_GETMINMAXINFO消息以便允许你增加窗口大小
void OnUpdateViewFullScreen(); //更新“全屏幕显示”菜单的状态
源码
void CMainFrame::OnMenuFullscreen()
{//全屏幕显示的处理函数
RECT rectDesktop;
WINDOWPLACEMENT wpNew;
if (m_bFullScreen)
{//全屏幕显示模式
//隐藏工具条和状态条
m_wndStatusBar.ShowWindow(SW_HIDE);
m_wndToolBar.ShowWindow(SW_HIDE);
//保存正常视图时的窗口位置信息以便恢复原来状态
GetWindowPlacement (&m_wpPrev);
m_wpPrev.length = sizeof m_wpPrev;
//调整RECT为新的窗口尺寸
::GetWindowRect ( ::GetDesktopWindow(), &rectDesktop );
::AdjustWindowRectEx(&rectDesktop, GetStyle(), TRUE, GetExStyle());
//保存RECT以便OnGetMinMaxInfo()使用
m_FullScreenWindowRect = rectDesktop;
wpNew = m_wpPrev;
wpNew.showCmd = SW_SHOWNORMAL;
wpNew.rcNormalPosition = rectDesktop;
//生成新的工具条
m_wndFullScreenBar=new CToolBar;
if(!m_wndFullScreenBar->Create(this,
CBRS_SIZE_DYNAMIC|CBRS_FLOATING)
|| !m_wndFullScreenBar->LoadToolBar(IDR_FULLSCREEN)) {
TRACE0("Failed to create toolbar\n");
return; // fail to create
}
//不允许工具条停泊
m_wndFullScreenBar->EnableDocking(FALSE);
m_wndFullScreenBar->SetWindowPos(0,100,100,0,0,SWP_NOSIZE
|SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW);
m_wndFullScreenBar->SetWindowText(_T("全屏幕显示"));
FloatControlBar(m_wndFullScreenBar, CPoint(100,100));
m_bFullScreen=TRUE;
}
else
{//正常显示模式
//删除全屏幕工具条
m_wndFullScreenBar->DestroyWindow();
delete m_wndFullScreenBar;
m_bFullScreen=FALSE;
//恢复工具条和状态条
m_wndStatusBar.ShowWindow(SW_SHOWNORMAL);
m_wndToolBar.ShowWindow(SW_SHOWNORMAL);
wpNew = m_wpPrev;
}