Visual C++编程技巧

 
Visual C++ 编程技巧
整理 Ackarlix
 
1. 如何获取应用程序的实例句柄 ?
应用程序的 实例句柄保存在 CWinAppIm_hInstance , 可以这么调用
AfxGetInstancdHandle 获得句柄 .
Example: HANDLE hInstance=AfxGetInstanceHandle();
2. 如何通过代码获得应用程序主窗口的指针 ?
主窗口的 指针保存在 CWinThread::m_pMainWnd , 调用 AfxGetMainWnd 实现。
AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED); // 使程序最大化 .
3. 如何在程序中获得其他程序的图标 ?
两种方法 :
(1) SDK 函数 SHGetFileInfo 或使用 ExtractIcon 获得图标资源的 handle,
(2) SDK 函数 SHGetFileInfo 获得有关文件的 很多信息 , 如大小图标 , 属性 , 类型等 .
Example(1): 在程序窗口左上角显示 NotePad 图标 .
void CSampleView: OnDraw(CDC * pDC)
{
if( :: SHGetFileInfo(_T("c://pwin95//notepad.exe"),0,
&stFileInfo,sizeof(stFileInfo),SHGFI_ICON))
{
pDC ->DrawIcon(10,10,stFileInfo.hIcon);
}
}
Example(2): 同样功能 ,Use ExtractIcon Function
void CSampleView:: OnDraw(CDC *pDC)
{
HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T
("NotePad.exe"),0);
if (hIcon &&hIcon!=(HICON)-1)
pDC->DrawIcon(10,10,hIcon);
}
说明 : 获得 notepad.exe 的路径正规上来说用 GetWindowsDirectory 函数得到 , 如果是调用 win95 下的画笔,应该用访问注册表的方法获得其路径,要作成一个比较考究的程序,考虑应该全面点 .
4. 如何编程结束应用程序 ? 如何编程控制 windows 的重新引导 ?
这是个很简单又是编程中经常要遇到的问题 .
第一问 , 向窗口发送 WM_CLOSE 消息 , 调用 CWnd::OnClose 成员函数 . 允许对用户提示
是否保存修改过的数据 .
Example: AfxGetMainWindow()->SendMessage(WM_CLOSE);
还可以创建一个自定义的函数 Terminate Window
void Terminate Window(LPCSTR pCaption)
{
CWnd *pWnd=Cwnd::FindWindow(NULL,pCaption);
if (pWnd)
pWnd ->SendMessage(WM_CLOSE);
}
说明 : FindWindow 函数不是提倡的做法,因为它无法处理标题栏自动改变,比如我们要检测 Notepad 是不是已运行而事先不知道 Notepad 的标题栏 , 这时 FindWindow 就无能为力了,可以通过枚举 windows 任务列表的办法来实现。 在 机械出版社 "Windows 95 API 开发人员指南 " 一书有比较详细的介绍 , 这里就不再多说乐。
第二问 ,Use ExitWindowsEx Function 函数控制系统是重新引导,还是重启 windows. 前面已经有人讲过乐,就不再提了。
5. 怎样加栽其他的应用程序 ?
我记得这好象是出场频度很高的问题。
三个 SDK 函数 winexec, shellexecute,createprocess 可以使用。
WinExec 最简单,两个参数,前一个指定路径,后一个指定显示方式 . 后一个参数值得说一下,比如泥用 SW_SHOWMAXMIZED 方式去加栽一个无最大化按钮的 程序,呵呵就是 Neterm,calc 等等,就不会出现正常的 窗体,但是已经被加到任务列表里了。 ShellExecute WinExex 灵活一点,可以指定工作目录 , 下面的 Example 就是直接打开 c:/temp/1.txt, 而不用加栽与 txt 文件关联的应用程序 , 很多安装程序完成后都会打开一个窗口,来显示 Readme or Faq, 偶猜就是这么作的啦 .
ShellExecute(NULL,NULL,_T("1.txt"),NULL,_T("c://temp"),SW_SHOWMAXMIZED);
CreateProcess 最复杂,一共有十个参数,不过大部分都可以用 NULL 代替,它可以指定进程的安全属性,继承信息,类的优先级等等 . 来看个很简单的 Example:
STARTUPINFO stinfo; // 启动窗口的信息
PROCESSINFO procinfo; // 进程的信息
CreateProcess(NULL,_T("notepad.exe"),NULL,NULL.FALSE, NORMAL_PRIORITY_
CLASS,NULL,NULL, &stinfo,&procinfo);
6. 确定应用程序的路径
前些天好象有人问过这个问题 .
Use GetModuleFileName 获得应用程序的路径,然后去掉可执行文件名。
Example:
TCHAR exeFullPath[MAX_PATH]; // MAX_PATH API 中定义了吧,好象是 128
GetModuleFileName(NULL,exeFullPath,MAX_PATH)
7. 获得各种目录信息
Windows 目录 : Use "GetWindowsDirectory
Windows 下的 system 目录 : Use "GetSystemDirectory"
temp 目录 : Use "GetTempPath "
当前目录 : Use "GetCurrentDirectory"
请注意前两个函数的第一个参数为 目录变量名,后一个为缓冲区 ; 后两个相反 .
8. 如何自定义消息
也有人问过的,其实不难。
(1) 手工定义消息,可以这么写 #define WM_MY_MESSAGE(WM_USER+100),
MS 推荐的至少是 WM_USER+100;
(2) 写消息处理函数 , WPARAM,LPARAM 返回 LRESULT.
LRESULT CMainFrame::OnMyMessage(WPARAM wparam,LPARAM lParam)
{
// 加入你的处理函数
}
(3) 在类的 AFX_MSG 处进行声明,也就是常说的 " 宏映射 "
9. 如何改变窗口的图标 ?
向窗口发送 WM_SECTION 消息。
Example:
HICON hIcon=AfxGetApp() ->LoadIcon(IDI_ICON);
ASSERT(hIcon);
AfxGetMainWnd() ->SendMessage(WM_SECTION,TRUE,(LPARAM) hIcon);
10. 如何改变窗口的缺省风格 ?
重栽 CWnd:: PreCreateWindow 并修改 CREATESTRUCT 结构来指定窗口风格和其他
创建信息 .
Example: Delete "Max" Button and Set Original Window's Position and Size
BOOL CMainFrame:: PreCreateWindow (CREATESTRUCT &cs)
{
cs.style &=~WS_MAXINIZEMOX;
cs.x=cs.y=0;
cs.cx=GetSystemMetrics(SM_CXSCREEN/2);
cs.cy=GetSystemMetrics(SM_CYSCREEN/2);
return CMDIFramewnd ::PreCreateWindow(cs);
}
11. 如何将窗口居中显示 ?
Easy, Call Function CWnd:: Center Windows
Example(1): Center Window( ); //Relative to it's parent
// Relative to Screen
Example(2): Center Window(CWnd:: GetDesktopWindow( ));
//Relative to Application's MainWindow
AfxGetMainWnd( ) -> Center Window( );
12. 如何让窗口和 MDI 窗口一启动就最大化和最小化 ?
先说窗口。
InitStance 函数中设定 m_nCmdShow 的 取值 .
m_nCmdShow=SW_SHOWMAXMIZED ; // 最大化
m_nCmdShow=SW_SHOWMINMIZED ; // 最小化
m_nCmdShow=SW_SHOWNORMAL ; // 正常方式
MDI 窗口 :
如果是创建新的应用程序 , 可以用 MFC AppWizard Advanced 按钮并在
MDI 子窗口风格组中检测最大化或最小化 ; 还可以重载 MDI Window
PreCreateWindow 函数,设置 WS_MAXMIZE or WS_MINMIZE;
如果从 CMDIChildWnd 派生 , 调用 OnInitialUpdate 函数中的 CWnd::Show
Window 来指定 MDI Child Window 的 风格。
13. 如何使程序保持极小状态 ?
很有意思的 问题
这么办 : 在恢复程序窗体大小时, Windows 会发送 WM_QUERY-OPEN 消息,
ClassWizard 设置成员函数 OnQueryOpen() ,add following code:
Bool CMainFrame:: OnQueryOpen( )
{
Return false;
}
14. 如何限制窗口的大小 ?
也就是 FixedDialog 形式。 Windows 发送 WM_GETMAXMININFO 消息来跟踪 ,
响应它 , OnGetMAXMININFO 中写代码 :
15. 如何使窗口不可见?
很简单 , SW_HIDE 隐藏窗口,可以结合 FindWindow,ShowWindow 控制 .
16. 如何使窗口始终在最前方 ?
两种途径 .
BringWindowToTop(Handle);
SetWindowPos 函数,指定窗口的 最顶风格 , WS_EX_TOPMOST 扩展窗口的 风格
Example:
void ToggleTopMost( CWnd *pWnd)
{
ASSERT_VALID(pWnd);
pWnd ->SetWindowPos(pWnd-> GetStyle( ) &WS_EX_TOPMOST)?
&wndNoTopMOST: &wndTopMost,0,0,0,0,SSP_NOSIZE|WSP_NOMOVE);
}
17 、如何创建一个字回绕的 CEditView
重载 CWnd : : PreCreateWindow 和修改 CREATESTRUCT 结构,关闭 CEditView 对象的 ES_AUTOHSCROLL WS_HSCROLL 风格位, 由于 CEditView : : PreCreateWindow 显示设置 cs. style ,调用基类函数后要修改 cs . style
BOOL CSampleEDitView : : PreCreateWindow (CREATESTRUCT&cs)
{
//First call basse class function .
BOOL bResutl =CEditView : : PreCreateWindow (cs) ;
// Now specify the new window style .
cs.style &= ~ (ES_AUTOHSCROLL WS_HSCROLL);
return bResult ;
}
18 、通用控件的显示窗口
MFC 提供了几个 CView 派生的视窗类, 封装了通用控件的功能,但仍然使用工
作框文档显示窗口体系结构: CEditView 封装了编辑控件, CTreeView 保持了树列表
控件, CListView 封装了列表显示窗口控件, CRichEditView 可以处理多种编辑控件。
19 、移动窗口
调用 CWnd : : SetWindowPos 并指定 SWP_NOSIZE 标志。目的位置与父窗口
有关(顶层窗口与屏幕有关)。调用 CWnd : : MoveWindow 时必须要指定窗口
的大小。
//Move window to positoin 100 , 100 of its parent window .
SetWindowPos (NULL, 100 , 100 , 0 , 0 , SWP_NOSIZE SWP_NOAORDER);
20 、重置窗口的大小
调用 CWnd: : SetWindowPos 并指定 SWP_NOMOVE 标志, 也可调用
CWnd : : MoveWindow 但必须指定窗口的位置。
// Get the size of the window .
Crect reWindow ;
GetWindowRect (reWindow );
//Make the window twice as wide and twice as tall .
SetWindowPos (NULL , 0 , 0 , reWindow . Width ( ) *2,
reWindow . Height () * 2,
SWP_NOMOVE SWP_NOZORDER );
21 、如何单击除了窗口标题栏以外的区域使窗口移动
当窗口需要确定鼠标位置时 Windows 向窗口发送 WM_NCHITTEST 信息,可以处理
该信息使 Windows 认为鼠标在窗口标题上。对于对话框和基于对话的应用程序,可
以使用 ClassWizard 处理该信息并调用基类函数, 如果函数返回 HTCLIENT 则表明
鼠标在客房区域,返回 HTCAPTION 表明鼠标在 Windows 的标题栏中。
UINT CSampleDialog : : OnNcHitTest (Cpoint point )
{
UINT nHitTest =Cdialog: : OnNcHitTest (point );
return (nHitTest = =HTCLIENT)? HTCAPTION : nHitTest ;
}
上述技术有两点不利之处, 其一是在窗口的客户区域双击时, 窗口将极大;
其二, 它不适合包含几个视窗的主框窗口。还有一种方法,当用户按下鼠标左键
使主框窗口认为鼠标在其窗口标题上,使用 ClassWizard 在视窗中处理 WM_LBUTTODOWN
信息并向主框窗口发送一个 WM_NCLBUTTONDOWN 信息和一个单击测试 HTCAPTION
void CSampleView : : OnLButtonDown (UINT nFlags , Cpoint point )
{
CView : : OnLButtonDow (nFlags , pont );
//Fool frame window into thinking somene clicked on
its caption bar .
GetParentFrame ( ) > PostMessage (
WM_NCLBUTTONDOWN , HTCAPTION , MAKELPARAM (poitn .x , point .y) );
}
该技术也适用于对话框和基于对的应用程序,只是不必调用 CWnd : : GetParentFrame
void CSampleDialog : : OnLbuttonDown (UINT nFlags, Cpoint point )
{
Cdialog : : OnLButtonDow (nFlags, goint );
//Fool dialog into thinking simeone clicked on its caption bar .
PostMessage (WM_NCLBUTTONDOWN , HTCAPTION , MAKELPARM (point.x , point. y ) )
}
22 、如何改变视窗的背景颜色
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;
}
23 、如何改变窗口标题
调用 CWnd : : SetWindowText 可以改变任何窗口(包括控件)的标题。
//Set title for application's main frame window .
AfxGetMainWnd ( ) > SetWindowText (_T("Application title") );
//Set title for View's MDI child frame window .
GetParentFrame ( ) > SetWindowText ("_T ("MDI Child Frame new title") );
//Set title for dialog's push button control.
GetDigitem (IDC_BUTTON) > SetWindowText (_T ("Button new title ") );
如果需要经常修改窗口的标题(注:控件也是窗口),应该考虑使用半文档化
的函数 AfxSetWindowText 。该函数在 AFXPRIV.H 中说明,在 WINUTIL.CPP 中实现,在
联机帮助中找不到它,它在 AFXPRIV.H 中半文档化, 在以后发行的 MFC 中将文档化。
AfxSetWindowText 的实现如下:
voik AFXAPI AfxSetWindowText (HWND hWndCtrl , LPCTSTR IpszNew )
{
itn nNewLen= Istrlen (Ipaznew);
TCHAR szOld [256];
//fast check to see if text really changes (reduces flash in the controls )
if (nNewLen >_contof (szOld) ||
: : GetWindowText (hWndCrtl , szOld , _countof (szOld) !=nNewLen ||
Istrcmp (szOld , IpszNew )! = 0
{
//change it
: : SetWindowText (hWndCtrl , IpszNew );
}
}
24 、如何防止主框窗口在其说明中显示活动的文档名
创建主框窗口和 MDI 子窗口进通常具有 FWS_ADDTOTITLE 风格位, 如果不希望在
说明中自动添加文档名, 必须禁止该风格位, 可以使用 ClassWizard 重置
CWnd: : PreCreateWindow 并关闭 FWS_ADDTOTITLE 风格。
BOOL CMainFrame : : PreCreateWindow (CREATESTRUCT&cs)
{
//Turn off FWS_ADDTOTITLE in main frame .
cs.styel & = ~FWS_ADDTOTITLE ;  
return CMDIFrameWnd : : PreCreateWindow (cs );
}
关闭 MDI 子窗口的 FWS _ADDTOTITLE 风格将创建一个具有空标题的窗口,可以调
CWnd: : SetWindowText 来设置标题。记住自己设置标题时要遵循接口风格指南。
25 、如何获取有关窗口正在处理的当前消息的信息
调用 CWnd: : GetCurrentMessage 可以获取一个 MSG 指针。例如,可以使用
ClassWizard 将几个菜单项处理程序映射到一个函数中,然后调用 GetCurrentMessage
来确定所选中的菜单项。
viod CMainFrame : : OnCommmonMenuHandler ( )
{
//Display selected menu item in debug window .
TRACE ("Menu item %u was selected . /n" ,
GetCruuentMessage ( ) > wParam );
}
26 、如何创建一个不规则形状的窗口
可以使用新的 SDK 函数 SetWindowRgn 。该函数将绘画和鼠标消息限定在窗口的一
个指定的区域,实际上使窗口成为指定的不规则形状。
使用 AppWizard 创建一个基于对的应用程序并使用资源编辑器从主对话资源中删
除所在的缺省控件、标题以及边界。
给对话类增加一个 CRgn 数据成员,以后要使用该数据成员建立窗口区域。
Class CRoundDlg : public CDialog
{
private :
Crgn m_rgn : // window region
} ;
修改 OnInitDialog 函数建立一个椭圆区域并调用 SetWindowRgn 将该区域分配给
窗口:
BOOL CRoundDlg : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( ) ;
//Get size of dialog .
CRect rcDialog ;
GetClientRect (rcDialog );
// Create region and assign to window .
m_rgn . CreateEllipticRgn (0 , 0 , rcDialog.Width ( ) , rcDialog .Height ( ) );
SetWindowRgn (GetSafeHwnd ( ) , (HRGN) m_ rgn , TRUE );
return TRUE ;
}
通过建立区域和调用 SetWindowRgn ,已经建立一个不规则形状的窗口,下面的例
子程序是修改 OnPaint 函数使窗口形状看起来象一个球形体。
voik CRoundDlg : : OnPaint ( )
{
CPaintDC de (this) ; // device context for painting .
//draw ellipse with out any border
dc. SelecStockObject (NULL_PEN);
//get the RGB colour components of the sphere color
COLORREF color= RGB( 0 , 0 , 255);
BYTE byRed =GetRValue (color);
BYTE byGreen = GetGValue (color);
BYTE byBlue = GetBValue (color);
// get the size of the view window
Crect rect ;
GetClientRect (rect);
// get minimun number of units
int nUnits =min (rect.right , rect.bottom );
//calculate he horiaontal and vertical step size
float fltStepHorz = (float) rect.right /nUnits ;
float fltStepVert = (float) rect.bottom /nUnits ;
int nEllipse = nUnits/3; // calculate how many to draw
int nIndex ; // current ellipse that is being draw
CBrush brush ; // bursh used for ellipse fill color
CBrush *pBrushOld; // previous brush that was selected into dc
//draw ellipse , gradually moving towards upper-right corner
for (nIndex = 0 ; nIndes < + nEllipse ; nIndes ++)
{
//creat solid brush
brush . CreatSolidBrush (RGB ( ( (nIndex *byRed ) /nEllipse ).
( ( nIndex * byGreen ) /nEllipse ), ( (nIndex * byBlue) /nEllipse ) ) );
//select brush into dc
pBrushOld= dc .SelectObject (&brhsh);
//draw ellipse
dc .Ellipse ( (int) fltStepHorz * 2, (int) fltStepVert * nIndex ,
rect. right -( (int) fltStepHorz * nIndex )+ 1,
rect . bottom -( (int) fltStepVert * (nIndex *2) ) +1) ;
//delete the brush
brush.DelecteObject ( );
}
}
最后,处理 WM_NCHITTEST 消息,使当击打窗口的任何位置时能移动窗口。
UINT CRoundDlg : : OnNchitTest (Cpoint point )
{
//Let user move window by clickign anywhere on the window .
UINT nHitTest = CDialog : : OnNcHitTest (point) ;
rerurn (nHitTest = = HTCLIENT)? HTCAPTION: nHitTest ;
}
27 、如何在代码中获取工具条和状态条的指针
缺省时, 工作框创建状态条和工具条时将它们作为主框窗口的子窗口,状态条
有一个 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);
28 、如何使能和禁止工具条的工具提示
如果设置了 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 );
}
29 、如何设置工具条标题
工具条是一个窗口,所以可以在调用 CWnd : : SetWindowText 来设置标题,例子如下:
int CMainFrame : : OnCreate (LPCREATESTRUCT lpCreateStruct )
{
// Set the caption of the toolbar .
m_wndToolBar.SetWindowText (_T "Standdard");
30 、如何创建和使用无模式对话框
MFC 将模式和无模式对话封装在同一个类中,但是使用无模式对话需要几
个对话需要几个额处的步骤。首先,使用资源编辑器建立对话资源并使用
ClassWizard 创建一个 CDialog 的派生类。模式和无模式对话的中止是不一样的:
模式对话通过调用 CDialog : : EndDialog 来中止,无模式对话则是调用
CWnd: : DestroyWindow 来中止的,函数 CDialog : : OnOK CDialog : : OnCancel
调用 EndDialog , 所以需要调用 DestroyWindow 并重置无模式对话的函数。
void CSampleDialog : : OnOK ( )
{
// Retrieve and validate dialog data .
if (! UpdateData (TRUE) )
{
// the UpdateData rountine will set focus to correct item
TRACEO (" UpdateData failed during dialog termination ./n") ;
return ;
}
//Call DestroyWindow instead of EndDialog .
DestroyWindow ( ) ;
}
void CSampleDialog : : OnCancel ( )
{
//Call DestroyWindow instead of EndDialog .
DestroyWindow ( ) ;
}
其次,需要正确删除表示对话的 C++ 对象。对于模式对来说,这很容易,需要创建函数返回后即可删除 C++ 对象;无模式对话不是同步的,创建函数调用后立即返回,因而用户不知道何时删除 C++ 对象。撤销窗口时工作框调用 CWnd : : PostNcDestroy ,可以重置该函数并执行清除操作,诸如删除 this 指针。
void CSampleDialog : : PostNcDestroy ( )
{
// Declete the C++ object that represents this dialog .
delete this ;
}
最后,要创建无模式对话。可以调用 CDialog : : DoModal 创建一个模式对放, 要创建一个无模式对话则要调用 CDialog: : Create 。下面的例子说明了应用程序是如何创建无模式对话的:
void CMainFrame : : OnSampleDialog ( )
{
//Allocate a modeless dialog object .
CSampleDilog * pDialog =new CSampleDialog ;
ASSERT_VALID (pDialog) ;
//Create the modeless dialog .
BOOL bResult = pDialog > Creste (IDD_IDALOG) ;
ASSERT (bResult ) ;
}
31 、如何在对话框中显示一个位图
这要归功于 Win 32 先进的静态控件和 Microsoft 的资源编辑器, 在对话框中显示位图是很容易的,只需将图形控件拖到对话中并选择适当属性即可,用户也可以显示图标、位图以及增强型元文件。
32 、如何改变对话或窗体视窗的背景颜色
调用 CWinApp : : SetDialogBkColor 可以改变所有应用程序的背景颜色。第一个参数指定了背景颜色,第二个参数指定了文本颜色。下例将应用程序对话设置为蓝色背景和黄色文本。
BOOL CSampleApp : : InitInstance ( )
{
//use blue dialog with yellow text .
SetDialogBkColor (RGB (0, 0, 255 ), RGB ( 255 , 255 , 0 ) ) ;
}
需要重画对话(或对话的子控件)时, Windows 向对话发送消息 WM_CTLCOLOR ,通常用户可以让 Windows 选择绘画背景的刷子,也可重置该消息指定刷子。下例说明了创建一个红色背景对话的步骤。
首先,给对话基类增加一人成员变量 CBursh :
class CMyFormView : public CFormView
{
private :
CBrush m_ brush ; // background brush
} ;
其次, 在类的构造函数中将刷子初始化为所需要的背景颜色。
CMyFormView : : CMyFormView ( )
{
// Initialize background brush .
m_brush .CreateSolidBrush (RGB ( 0, 0, 255 ) )
}
最后,使用 ClassWizard 处理 WM_CTLCOLOR 消息并返回一个用来绘画对话背景的刷子句柄。注意:由于当重画对话控件时也要调用该函数,所以要检测 nCtlColor 参量。
HBRUSH CMyFormView : : OnCtlColor (CDC* pDC , CWnd*pWnd , UINT nCtlColor )
{
// Determine if drawing a dialog box . If we are , return +handle to
//our own background brush . Otherwise let windows handle it .
if (nCtlColor = = CTLCOLOR _ DLG )
return (HBRUSH) m_brush .GetSafeHandle ( ) ;
return CFormView : : OnCtlColor (pDC, pWnd , nCtlColor );
}
33 、如何获取一个对话控件的指针
有两种方法。其一,调用 CWnd: : GetDlgItem ,获取一个 CWnd* 指针调用成员函数。下例调用 GetDlgItem ,将返回值传给一个 CSpinButtonCtrl* 以便调用 CSpinButtonCtrl : : SetPos 函数:
BOOL CSampleDialog : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( ) ;
//Get pointer to spin button .
CSpinButtonCtrl * pSpin - ( CSpinButtonCtrl *) GetDlgItem (IDC_SPIN) ;
ASSERT _ VALID (pSpin) ;
//Set spin button's default position .
pSpin > SetPos (10) ;
return TRUE ;
}
其二, 可以使用 ClassWizard 将控件和成员变量联系起来。在 ClassWizard 中简单地选择 Member Variables 标签,然后选择 Add Variable 按钮。如果在对话资源编辑器中,按下 Ctrl 键并双击控件即可转到 Add Member Variable 对话。
34 、如何禁止和使能控件
控件也是窗口,所以可以调用 CWnd : : EnableWindow 使能和禁止控件。
//Disable button controls .
m_wndOK.EnableWindow (FALSE ) ;
m_wndApply.EnableWindow (FALSE ) ;
35 、如何改变控件的字体
由于控件是也是窗口,用户可以调用 CWnd: : SetFont 指定新字体。该函数用一个 Cfont 指针,要保证在控件撤消之前不能撤消字体对象。下例将下压按钮的字体改为 8 Arial 字体:
//Declare font object in class declaration (.H file ).
private :
Cfont m_font ;
// Set font in class implementation (.Cpp file ). Note m_wndButton is a
//member variable added by ClassWizard.DDX routines hook the member
//variable to a dialog button contrlo.
BOOL CSampleDialog : : OnInitDialog ( )
{
//Create an 8-point Arial font
m_font . CreateFont (MulDiv (8 , -pDC > GetDeviceCaps (LOGPIXELSY) , 72).
0 , 0 , 0 , FW_NORMAL , 0 , 0, 0, ANSI_CHARSER, OUT_STROKE_PRECIS ,
CLIP_STROKE _PRECIS , DRAFT _QUALITY
VARIABLE_PITCH FF_SWISS, _T ("Arial") );
//Set font for push button .
m_wndButton . SetFont (&m _font );
}
36 、如何在 OLE 控件中使用 OLE_COLOR 数据类型
诸如 COleControl : : GetFortColor COleControl : : GetBackColor 等函数返回 OLE _COLOR 数据类型的颜色,而 GDI 对象诸如笔和刷子使用的是 COLORREF 数据类型,调用 COleControl : : TranslateColor 可以很容易地将 OLE_COLOR 类型改为 COLORREF 类型。下例创建了一个当前背景颜色的刷子:
void CSampleControl : : OnDraw (CDC* pdc
const Crect& rcBounds , const Crect& rcInvalid )
{
//Create a brush of the cuttent background color .
CBrush brushBack (TranslateColor (GetBackColor ( ) ) );
//Paint the background using the current background color .
pdc > FilllRect (rcBounds , &brushBack) ;
//other drawign commands
}
37 、在不使用通用文件打开对话的情况下如何显示一个文件列表
调用 CWnd: : DlgDirList 或者 CWnd: : DlgDirListComboBox Windows 将自动地向列表框或组合框填充可用的驱动器名或者指定目录中的文件,下例将 Windows 目录中的文件填充在组合框中:
BOOL CSampleDig : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( )
TCHAR szPath [MAX_PATH] = {"c://windows"} ;
int nReslt = DlgDirListComboBox (szPath , IDC_COMBO , IDC_CURIDIR,
DDL_READWRITE DDL_READONLY DDL_HIDDEN
DDL_SYSTEM DDL_ARCHIVE ) ;
return TRUE ;
}
38 、为什么旋转按钮控件看起来倒转
需要调用 CSpinCtrl : : SetRange 设置旋转按钮控件的范围,旋转按钮控件的缺省上限为 0 ,缺省下限为 100 ,这意味着增加时旋转按控件的值由 100 变为 0 。下例将旋转按钮控件的范围设置为 0 100
BOOL CAboutDlg : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( )
//set the lower and upper limit of the spin button
m_wndSpin . SetRange ( 0 ,100 ) ;
return TRUE ;
}
Visual C++ 4.0 Print 对话中的 Copise 旋转按钮控件也有同样的问题:按下 Up 按钮时拷贝的数目减少,而按下 Down 按钮时拷贝的数目增加。
39 为什么旋转按钮控件不能自动地更新它下面的编辑控件
如果使用旋转按钮的 autu buddy 特性, 则必须保证在对话的标记顺序中 buddy 窗口优先于旋转按钮控件。从 Layout 菜单中选择 Tab Order 菜单项(或者按下 Crtl+D )可以设置对话的标签顺序。
40 、如何用位图显示下压按钮
Windows 95 按钮有几处新的创建风格,尤其是 BS_BITMAP BS_ICON ,要想具有位图按钮,创建按钮和调用 CButton : : SetBitmap CButton : : SetIcon 时要指定 BS_BITMAP BS_ICON 风格。
首先,设置按钮的图标属性。
然后,当对话初始化时调用 CButton: : SetIcon 。注意:下例用图标代替位图,使用位图时要小心,因为不知道背景所有的颜色——并非每个人都使用浅灰色。
BOOL CSampleDlg : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( ) ;
//set the images for the push buttons .
m_wndButton1.SetIcon (AfxGetApp ( ) > LoadIcon (IDI _ IPTION1) )
m_wndButton2.SetIcon (AfxGetApp ( ) > LoadIcon (IDI _ IPTION2) )
m_wndButton3.SetIcon (AfxGetApp ( ) > LoadIcon (IDI _ IPTION3) )
return TRUE ;
}
41 、如何一个创建三态下压按钮
可以使用新的 BS_PUSHBUTTON 风格位和检测框以及按钮来创建一个三态下压按钮。这很容易,只需将检测框和按钮拖拉到对话中并指定属性 Push like 即可。不用任何附加程序就可以成为三态下压按钮。
42 、如何动态创建控件
分配一个控件对象的实例并调用其 Create 成员函数。开发者最容易忽略两件事:忘记指定 WS_VISBLE 标签和在栈中分配控件对象。下例动态地创建一个下压按钮控件:
//In class declaration (.H file ).
private :
CButton* m _pButton ;
//In class implementation (.cpp file ) .
m_pButton =new CButton ;
ASSERT_VALID (m_pButton);
m_pButton >Create (_T ("Button Title ") , WS_CHILD WS_VISIBLE BS_PUSHBUTTON.
Crect ( 0, 0, 100 , 24) , this , IDC _MYBUTTON )
43 、如何限制编辑框中的准许字符
如果用户在编辑控件中只允许接收数字,可以使用一个标准的编辑控件并指定新的创建标志 ES_NUMBERS, 它是 Windows 95 新增加的标志,该标志限制 编辑控件只按收数字字符。如果用户需要复杂的编辑控件,可以使用 Microsoft 的屏蔽编辑控件,它是一个很有用的 OLE 定制控件。
如果希望不使用 OLE 定制控件自己处理字符,可以派生一个 CEdit 类并处理 WM_CHAR 消息,然后从编辑控件中过滤出特定的字符。首先,使用 ClassWizard 建立一个 CEdit 的派生类,其次,在对话类中指定一个成员变量将编辑控件分类在 OnInitdialog 中调用 CWnd: : SubclassDlgItem .
//In your dialog class declaration (.H file )
private :
CMyEdit m_wndEdit ; // Instance of your new edit control .
//In you dialog class implementation (.CPP file )
BOOL CSampleDialog : : OnInitDialog ( )
{
//Subclass the edit lontrod .
m_wndEdit .SubclassDlgItem (IDC_EDIT,this );
}
使用 ClassWizard 处理 WM_CHAR 消息,计算 nChar 参量并决定所执行的操作,用户可以确定是否修改、传送字符。下例说明了如何显示字母字符,如果字符是字母字符,则调用 CWnd ; OnChar ,否则不调用 OnChar.
//Only display alphabetic dharacters .
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UITN nFlags )
{
//Determine if nChar is an alphabetic character .
if (: : IsCharAlpha ( ( TCHAR) nChar ) )
CEdit : : OnChar (nChar, nRepCnt , nFlags );
}
如果要修改字符,则不能仅仅简单地用修改过的 nChar 调用 CEdit : : OnChar ,然后 CEdit: : OnChar 调用 CWnd: : Default 获取原来的 wParam lParam 的值 ,这样是不行的。要修改一个字符,需要首先修改 nChar ,然后用修改过的 nChar 调用 CWnd: : DefWindowProc 。下例说明了如何将字符转变为大写:
//Make all characters uppercase
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UINT nFlags )
{
//Make sure character is uppercase .
if (: : IsCharAlpha ( .( TCHAR) nChar)
nChar=: : CharUpper (nChar ) ;
//Bypass default OnChar processing and directly call
//default window proc.
DefWindProc (WM_CHAR, nChar , MAKELPARAM (nRepCnt , nFlags )) ;
}
44 、如何改变控件的颜色
有两种方法。其一,可以在父类中指定控件的颜色,或者利用 MFC4.0 新的消息反射在控件类中指定颜色。当控件需要重新着色时,工作框调用父窗口(通常是对话框)的 CWnd: : OnCrtlColor, 可以在父窗口类中重置该函数并指定控件的新的绘画属性。例如,下述代码将对话中的所有编辑控件文本颜色改为红色:
HBRUSH CAboutDig : : OnCtlColor (CDC * pDCM , CWnd * pWnd , UINT nCtlColor)
{
HBRUSH hbr = CDialog : : OnCtlColor (pDC, pWnd , nCtlColor );
//Draw red text for all edit controls .
if (nCtlColor= = CTLCOLOR_EDIT )
pDC > SetTextColor (RGB (255 , 0 , 0 , ) ) ;
return hbr ;
}
然而,由于每个父窗口必须处理通知消息并指定每个控件的绘画属性,所以,这种方法不是完全的面向对象的方法。控件处理该消息并指定绘画属性更合情合理。消息反射允许用户这样做。通知消息首先发送给父窗口,如果父窗口没有处理则发送给控件。创建一个定制彩色列表框控件必须遵循下述步骤。
首先,使用 ClassWizard 创建一个 CListBox 的派生类并为该类添加下述数据成员。
class CMyListBox ; publilc CListBox
{
private;
COLORREF m_clrFor ; // foreground color
COLORREF m_clrBack ; //background color
Cbrush m_brush ; //background brush
} ;
其次,在类的构造函数中,初始化数据中。
CMyListBox : : CMyListBox ()
{
//Initialize data members .
m_clrFore =RGB (255 , 255 , 0) ; // yellow text
m_clrBack=RGB (0 , 0 , 255) ; // blue background
m_brush . CreateSolidBrush (m _clrBack );
}
最后,使用 ClassWizard 处理反射的 WM_CTLCOLOR(=WM_CTLCOLOR) 消息并指定新的绘画属性。
HBRUSH CMyListBox : : CtlColor (CDC* pDC, UINT nCtlColor )
{
pDC >SetTextColor (m_clrFore);
pDC >SetBkColor (m_clrBack);
return (HBRUSH) m_brush.GetSafeHandle ()
}
现在,控件可以自己决定如何绘画,与父窗口无关。
45 、当向列表框中添加多个项时如何防止闪烁
调用 CWnd::SetRedraw 清除重画标志可以禁止 CListBox (或者窗口)重画。当向列表框添加几个项时,用户可以清除重画标志,然后添加项,最后恢复重画标志。为确保重画列表框的新项,调用 SetRedraw (TRUE) 之后调用 CWnd::Invalidate
//Disable redrawing.
pListBox->SetRedraw (FALSE);
//Fill in the list box gere
//Enable drwing and make sure list box is redrawn.
pListBox->SetRedraw (TRUE);
pListBox->Invalidate ();
46 、如何向编辑控件中添加文本
由于没有 CEdit:: AppendText 函数,用户只好自己做此项工作。调用 CEdit:: SetSel 移动到编辑控件末尾,然后调用 CEdit:: ReplaceSel 添加文本。下例是 AppendText 的一种实现方法:
void CMyEdit:: AppendText (LPCSTR pText)
{
int nLen=GetWindowTextLength ();
SetFocus ();
SetSel (nLen, nLen);
ReplaceSel (pText);
}
47 、如何访问预定义的 GDI 对象
可以通过调用 CDC:: SlectStockObject 使用 Windows 的几个预定义的对象,诸如刷子、笔以及字体。下例使用了 Windows 预定义的笔和刷子 GDI 对象在视窗中画一个椭圆。
//Draw ellipse using stock black pen and gray brush.
void CSampleView:: OnDraw (CDC* pDC)
{
//Determine size of view.
CRect rcView;
GetClientRect (rcView);
//Use stock black pen and stock gray brush to draw ellipse.
pDC->SelectStockObject (BLACK_PEN);
pDC->SelectStockObject (GRAY_BRUSH)
//Draw the ellipse.
pDC->Ellipse (reView);
}
也可以调用新的 SDK 函数 GetSysColorBrush 获取一个系统颜色刷子,下例用背景色在视窗中画一个椭圆:
void CsampleView:: OnDraw (CDC* pDC)
{
//Determine size of view.
CRect rcView;
GetClientRect (rcView);
//Use background color for tooltips brush.
CBrush * pOrgBrush=pDC->SelectObject (
CBrush::FromHandle (::GetSysColorBrush (COLOR_INFOBK)));
//Draw the ellipse.
pDC->Ellipse (rcView);
//Restore original brush.
pDC->SelectObject (pOrgBrush);
}
48 、如何获取 GDI 对象的属性信息
可以调用 GDIObject:: GetObject 。这个函数将指定图表设备的消息写入到缓冲区。下例创建了几个有用的辅助函数。
//Determine if font is bold.
BOOL IsFontBold (const CFont&font)
{
LOGFONT stFont;
font.GetObject (sizeof (LOGFONT), &stFont);
return (stFont.lfBold)? TRUE: FALSE;
}
//Return the size of a bitmap.
CSize GetBitmapSize (const CBitmap&bitmap)
{
BITMAP stBitmap;
bitmap.GetObject (sizeof (BITMAP), &stBitmap);
return CSize (stBitmap.bmWidth, stBitmap. bmHeight);
}
//Create a pen with the same color as a brush.
BOOL CreatePenFromBrush (Cpen&pen, cost Cbrush&brush)
{
LOGBRUSH stBrush;
brush.Getobject (sizeof (LOGBRUSH), &stBrush);
return pen. Createpen (PS_SOLID, 0, stBrush.ibColor);
}
49 、如何实现一个橡皮区矩形
CRectTracker 是一个很有用的类,可以通过调用 CRectTracker:: TrackRubberBand 响应 WM_LBUTTONDOWN 消息来创建一个橡皮区矩形。下例表明使用 CRectTracker 移动和重置视窗中的蓝色椭圆的大小是很容易的事情。
首先,在文件档中声明一个 CRectTracker 数据成员:
class CSampleView : Public CView
{
public :
CrectTracker m_tracker;
};
其次,在文档类的构造函数中初始化 CRectTracker 对象:
CSampleDoc:: CSampleDOC ()
{
//Initialize tracker position, size and style.
m_tracker.m_rect.SetRect (0, 0, 10, 10);
m_tracker.m_nStyle=CRectTracker:: resizeInside |
CRectTracker:: dottedLine;
}
然后,在 OnDraw 函数中画椭圆和踪迹矩形:
void CSampleView:: OnDraw (CDC* pDC)
{
CSampleDoc* pDoc=GetDocument ();
ASSERT_VALID (pDoc);
//Select blue brush into device context.
CBrush brush (RGB (0, 0, 255));
CBrush* pOldBrush=pDC->SelectObject (&brush);
//draw ellipse in tracking rectangle.
Crect rcEllipse;
pDoc->m_tracker.GetTrueRect (rcEllipse);
pDC->Ellipse (rcEllipse);
//Draw tracking rectangle.
pDoc->m_tracker.Draw (pDC);
//Select blue brush out of device context.
pDC->Selectobject (pOldBrush);
}
最后,使用 ClassWizard 处理 WM_LBUTTONDOWN 消息,并增加下述代码。该段代码根据鼠标击键情况可以拖放、移动或者重置椭圆的大小。
void CSampleView::OnLButtonDown (UINT nFlags, CPoint point)
{
//Get pointer to document.
CSampleDoc* pDoc=GetDocument ();
ASSERT_VALID (pDoc);
//If clicked on ellipse, drag or resize it. Otherwise create a
//rubber-band rectangle nd create a new ellipse.
BOOL bResult=pDoc->m_tracker.HitTest (point)!=
CRectTracker::hitNothing;
//Tracker rectangle changed so update views.
if (bResult)
{
pDoc->m_tracker.Track (this,point,TRue);
pDoc->SetModifiedFlag ();
pDoc->UpdateAllViews (NULL);
}
else
pDoc->m-tracker.TrackRubberBand (this,point,TRUE);
CView:: onLButtonDown (nFlags,point);
}
50 、如何更新翻转背景颜色的文本
调用 CDC:: SetBkmode 并传送 OPAQUE 用当前的背景颜色填充背景,或者调用 CDC::SetBkMode 并传送 TRANSPAARENT 使背景保持不变,这两种方法都可以设置背景模式。下例设置背景模式为 TRANSPARENT ,可以两次更新串,用花色带黑阴影更新文本。黑色串在红色串之后,但由于设置了背景模式仍然可见。
void CSampleView:: OnDraw (CDC* pDC)
{
//Determint size of view.
CRect rcView;
GetClientRect (rcVieew);
//Create sample string to display.
CString str (_T ("Awesome Shadow Text ... "));
//Set the background mode to transparent.
pDC->SetBKMode (TRANSPARENT);
//Draw black shadow text.
rcView.OffsetRect (1, 1);
pDc->SetTextColor (RGB (0, 0, 0));
pDC->DrawText (str, str.GetLength (), rcView,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//Draw red text.
rcView.OffsetRect (-1,-1);
pDc->SetTextColor (RGB (255, 0, 0));
pDC->DrawText (str, str.GetLength (), rcView,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
51 、如何创建一个具有特定点大小的字体
可以指定字体逻辑单位的大小,但有时指定字体的点的大小可能会更方便一些。可以如下将字体的点转换为字体的高度:
int nHeigth=mulDiv (nPointSize, -dc.GetDeviceCaps (LOGPIXELSY), 72);
下例创建了一个 8 点的 Apial 字体:
CClientDC dc (AqfxGetMainWnd ());
m_font. CreateFont (MulDiv (8, -dc.GetDeviceCaps (LOGPIXELSY),
72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,
OUT_STROKE_PRECIS,CLIP_STROKE_PRECIS,DRAFT_QUALITY,
VARIABLE_PITCH | FF-SWISS,_T ("Arial"));
52 、如何计算一个串的大小
函数 CDC:: Det text Extent 根据当前选择的字体计算一个串的高度和宽度。如果使用的不是系统字体而是其他字体,则在调用 GetTextExtent 之前将字体选进设备上下文中是很重要的,否则计算高度和宽度时将依据系统字体,由此得出的结果当然是不正确的。下述样板程序当改变下压按钮的标题时动态调整按钮的大小,按钮的大小由按钮的字体和标题的大小而定。响应消息 WM_SETTEXT 时调用 OnSetText ,该消息使用 ON_MESSAE 宏指令定义的用户自定义消息。
LRESULT CMyButton:: OnSettext (WPARAM wParam, LPARAM lParam)
{
//Pass message to window procedure.
LRESULT bResult=CallWindowProc (*GetSuperWndProcAddr (),
m_hWnd, GetCurrentMessage () ->message,wParam,lParam);
//Get title of push button.
CString strTitle;
GetWindowText (strTitle);
//Select current font into device context.
CDC* pDC=GetDc ();
CFont*pFont=GetFont ();
CFont*pOldFont=pDC->SelectObject (pFont);
//Calculate size of title.
CSize size=pDC->GetTextExent (strTitle,strTitle.GetLength ());
//Adjust the button's size based on its title.
//Add a 5-pixel border around the button.
SetWindowPos (NULL, 0, 0, size.cx+10, size.cy+10,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
//Clean up.
pDC->SelectFont (pOldFont);
ReleaseDC (pDC);
return bResult;
}
53 、如何显示旋转文本
只要用户使用 TrueType 或者 GDI 笔或字体就可以显示旋转文本 ( 有些硬件设备也支持旋转光栅字体)。 LOGFONT 结构中的 ifEscapement 成员指定了文本行和 x 轴的角度,角度的单位是十分之一度而不是度,例如, ifEscapement 450 表示字体旋转 45 度。为确保所有的字体沿坐标系统的同一方向旋转,一定要设置 ifEscapement 成员的 CLIP_LH_ANGLES 位,否则,有些字体可能反向旋转。下例使用了 14 Arial 字体每间隔 15 度画一个串。
void CSampleView:: OnDraw (CDC* pDC)
{
//Determine the size of the window.
CRect rcClient;
GetClientRect (rcClient);
//Create sample string.
CString str (_T ("Wheeee ... I am rotating!"));
//Draw transparent, red text.
pDC->SetBkMode (TRANSPARENT);
pDC->SetTextColor (RGB (255,0,0));
CFont font; //font object
LOGFONT stFont; //font definition
//Set font attributes that will not change.
memset (&stFont, 0, sizeof (LOGFONT));
stFont.ifheight=MulDiv (14, -pDC->GetDeviceCaps (LOGPIXELSY), 72);
stFont.ifWeight=FW_NORMAL;
stFont.ifClipPrecision=LCIP_LH_ANGLES;
strcpy (stFont.lfFaceName, "Arial");
//Draw text at 15degree intervals.
for (int nAngle=0; nAngle<3600; nAngle+=150)
{
//Specify new angle.
stFont.lfEscapement=nAngle;
//Create and select font into dc.
font.CreateFontIndirect (&stfont);
CFont* pOldFont=pDC->SelectObject (&font);
//Draw the text.
pDC->SelectObject (pOldFont);
font.DelectObjext ();
}
}
54 、如何正确显示包含标签字符的串
调用 GDI 文本绘画函数时需要展开标签字符,这可以通过调用 CDC:: TabbedTextOut 或者 CDC:: DrawText 并指定 DT_EXPANDTABS 标志来完成。 TabbedTextOut 函数允许指定标签位的数组,下例指定每 20 设备单位展开一个标签:
void CSampleView:: OnDraw (CDC* pDC)
{
CTestDoc* pDoc=GetDocument ();
ASSERT_VALID (pDoC);
CString str;
str.Format (_T ("Cathy/tNorman/tOliver"));
int nTabStop=20; //tabs are every 20 pixels
pDC->TabbedtextOut (10, 10, str, 1, &nTabStop, 10);
}
55 、串太长时如何在其末尾显示一个省略号
调用 CDC:: DrawText 并指定 DT_END_ELLIPSIS 标志,这样就可以用小略号取代串末尾的字符使其适合于指定的边界矩形。如果要显示路径信息,指定 DT_END_ELLIPSIS 标志并省略号取代串中间的字符。
void CSampleView:: OnDraw (CDC* pDC)
{
CTestDoc* pDoc=GetDocument ();
ASSERT_VALID (pDoc);
//Add ellpsis to end of string if it does not fit
pDC->Drawtext (CString ("This is a long string"),
CRect (10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);
//Add ellpsis to middle of string if it does not fit
pDC->DrawText (AfxgetApp () ->m_pszhelpfilePath,
CRect (10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);
}
56 、如何快速地格式化一个 CString 对象
调用 CString:: Format ,该函数和 printf 函数具有相同的参数,下例说明了如何使用 Format 函数:
//Get size of window.
CRect rcWindow;
GetWindowRect (rcWindow);
//Format message string.
CString strMessage;
strMessage.Format (_T ("Window Size (%d, %d)"),
rcWindow.Width (), rcWindow.Height ());
//Display the message.
MessageBox (strmessage);
57 、为什么即使调用 EnableMenuItem 菜单项后,菜单项还处于禁止状态
需要将 CFrameWnd:: m_bAutomenuEnable 设置为 FALSE ,如果该数据成员为 TRUE (缺省值),工作框将自动地禁止没有 ON_UPDATE_COMMAND_UI 或者 ON_COMMAND 的菜单项。
//Disable MFC from automatically disabling menu items.
m_bAuoMenuEnable=FALSE;
//Now enable the menu item.
CMenu* pMenu=GetMenu ();
ASSERT_VALID (pMenu);
pMenu->EnableMenuItem (ID_MENU_ITEM,MF_BYCOMMAND | MF_ENABLED);
58 、如何给系统菜单添加一个菜单项
给系统菜单添加一个菜单项需要进行下述三个步骤:
首先,使用 Resource Symbols 对话(在 View 菜单中选择 Resource Symbols ...
可以显示该对话)定义菜单项 ID ,该 ID 应大于 0x0F 而小于 0xF000
其次,调用 CWnd::GetSystemMenu 获取系统菜单的指针并调用 CWnd:: Appendmenu 将菜单项添加到菜单中。下例给系统菜单添加两个新的菜单项:
int CMainFrame:: OnCreate (LPCREATESTRUCT lpCreateStruct)
{
//Make sure system menu item is in the right range.
ASSERT (IDM_MYSYSITEM &0xFFF0)==IDM_MYSYSITEM);
ASSERT (IDM-MYSYSITEM<0xF000);
//Get pointer to system menu.
CMenu* pSysmenu=GetSystemmenu (FALSE);
ASSERT_VALID (pSysMenu);
//Add a separator and our menu item to system menu.
CString StrMenuItem (_T ("New menu item"));
pSysMenu->Appendmenu (MF_SEPARATOR);
pSysMenu->AppendMenu (MF_STRING, IDM_MYSYSITEM, strMenuitem);
}
现在,选择系统菜单项时用户应进行检测。使用 ClassWizard 处理
WM_SYSCOMMAND 消息并检测用户菜单的 nID 参数:
void CMainFrame:: OnSysCommand (UINT nID,LPARAM lParam)
{
//Determine if our system menu item was selected.
if ( (nID & 0xFFF0)==IDM_MYSYSITEM)
{
//TODO-process system menu item
}
else
CMDIFrameWnd:: OnSysCommand (nID, lParam);
}
最后,一个设计良好的 UI 应用程序应当在系统菜单项加亮时在状态条显示一个帮助信息,这可以通过增加一个包含系统菜单基 ID 的串表的入口来实现。
59 、如何确定顶层菜单所占据的菜单行数
这可以通过简单的减法和除法来实现。首先,用户需要计算主框窗口的高度和客户区;其次,从主框窗口的高度中减去客户区、框边界以及标题的高度;最后,除以菜单栏的高度。下例成员函数是一个计算主框菜单所占据的行数的代码实现。
int CMainFrame:: GetMenuRows ()
{
CRect rcFrame,rcClient;
GetWindowRect (rcFrame);
GetClientRect (rcClient);
return (rcFrame.Height () -rcClient.Height ()-
:: GetSystemMetrics (SM_CYCAPTION) -
(:: getSystemMetrics (SM_CYFRAME) *2)) /
:: GetSystemMetrics (SM_CYMENU);
}
60 、在用户环境中如何确定系统显示元素的颜色
调用 SDK 函数 GetSysColor 可以获取一个特定显示元素的颜色。下例说明了如何在 MFC 函数 CMainFrameWnd:: OnNcPaint 中调用该函数设置窗口标题颜色。
void CMiniFrameWnd:: OnNcPaint ()
{
dc.SetTextColor (:: GetSysColor (m_bActive ?
COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
}
61 、如何查询和设置系统参数
Windows 3.1 SDK 中介绍过 SDK 函数 SystemParametersInfo ,调用该函数可以查询和设置系统参数,诸如按键的重复速率设置、鼠标双击延迟时间、图标字体以及桌面覆盖位图等等。
//Create a font that is used for icon titles.
LOGFONT stFont;
:: SystemParametersInfo (SPIF_GETICONTITLELOGFONT,
sizeof (LOGFONT), &stFont, SPIF_SENDWININICHANGE);
m_font.CreateFontIndirect (&stFont);
//Change the wallpaper to leaves.bmp.
:: SystemParametersInfo (SPI_SETDESKWALLPAPER, 0,
_T (" forest.bmp"), SPIF_UPDATEINIFILE);
62 、如何使用一个预定义的 Windows 光标
调用 CWinApp:: LoadStandardCursor 并传送光标标识符。
BOOL CSampleDialog:: OnSetCursor (CWnd* pWnd, UINT nHitTest, UINT message)
{
//Display wait cursor if busy.
if (m_bBusy)
{
SetCursor (AfxGetApp () ->LoadStandardCursor (IDC_WAIT));
return TRUE;
}
return CDialog:: OnSetCursor (pWnd. nHitTest,message);
}
63 、如何确定当前屏幕分辨率
调用 SDK 函数 GetSystemMetrics ,该函数可以检索有关 windows 显示信息,诸如标题大小、边界大小以及滚动条大小等等。
//Initialize CSize object with screen size.
CSize sizeScreen (GetSystemMetrics (SM_CXSCREEN),
GetSystemMetrics (SM_CYSCREEN));
64 、如何检索原先的 Task Manager 应用程序使用的任务列表
原先的 Task Manager 应用程序显示顶层窗口的列表。为了显示该列表,窗口
必须可见、包含一个标题以及不能被其他窗口拥有。调用 CWnd:: GetWindow 可以
检索顶层窗口的列表,调用 IsWindowVisible GetWindowTextLength 以及 GetOwner
可以确定窗口是否应该在列表中。下例将把 TaskManager 窗口的标题填充到列表中。
void GetTadkList (CListBox&list)
{
CString strCaption; //Caption of window.
list.ResetContent (); //Clear list box.
//Get first Window in window list.
ASSERT_VALID (AfxGetMainWnd ());
CWnd* pWnd=AfxGetMainWnd () ->GetWindow (GW_HWNDFIRST);
//Walk window list.
while (pWnd)
{
// I window visible, has a caption, and does not have an owner?
if (pWnd ->IsWindowVisible () &&
pWnd ->GetWindowTextLength () &&! pWnd ->GetOwner ())
{
//Add caption o window to list box.
pWnd ->GetWindowText (strCaption);
list.AddString (strCaption);
}
//Get next window in window list.
pWnd=pWnd->GetWindow (GW_HWNDNEXT);
}
}
65 、如何确定 Windows Windows 系统目录
有两个 SDK 函数可以完成该功能。 GetWindowsDirectory GetSystemDirectory ,下例说明了如何使用这两个函数:
TCHAR szDir [MAX_PATH];
//Get the full path of the windows directory.
:: GetWindowsDirectory (szDir, MAX_PATH);
TRACE ("Windows directory %s/n", szDir);
//Get the full path of the windows system directory.
:: GetSystemDirectory (szDir, MAX_PATH);
TRACE ("Windows system directory %s/n", szDir);
66 、在哪儿创建临文件
调用 SDK 函数 GetTemPath 可以确定临时文件的目录,该函数首先为临时路径检测 TMP 环境变量:如果没有指定 TMP ,检测 TMP 环境变量,然后返回到当前目录。下例说明了如何创建一个临时文件。
//get unique temporary file.
CString strFile;
GetUniqueTempName (strFile);
TRY
{
//Create file and write data.Note that file is closed
//in the destructor of the CFile object.
CFile file (strFile,CFile:: modeCreate | CFile:: modeWrite);
//write data
}
CATCH (CFileException, e)
{
//error opening file
}
END_CATCH
Void GetuniqueTempName (CString& strTempName)
{
//Get the temporary files directory.
TCHAR szTempPath [MAX_PATH];
DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath);
ASSERT (dwResult);
//Create a unique temporary file.
TCHAR szTempFile [MAX_PATH];
UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempfile);
ASSERT (nResult);
strTempName=szTempFile;
}
67 、如何访问桌面窗口
静态函数 CWnd:: GetDesktopWindow 返回桌面窗口的指针。下例说明了 MFC 函数 CFrameWnd::BeginModalStae 是如何使用该函数进入内部窗口列表的。
void CFrameWnd::BeginModalState ()
{
//first count all windows that need to be disabled
UINT nCount=0;
HWND hWnd=:: GetWindow (:: GetDesktopWindow (), GW_CHILD);
while (hWnd!=NULL)
{
if (:: IsWindowEnabled (hwnd) &&
CWnd::FromHandlePermanent (hWnd)!=NULL &&
AfxIsDescendant (pParent->m_hWnd, hWnd) &&
:: SendMessage (hWnd, WM_DISABLEMODAL, 0, 0)==0)
{
++nCount;
}
hWnd=:: GetWindow (hWnd, GW_HWNDNEXT);
}
}
68.VC1.5 下如何跳到第一事例 ?
各位大虾 : CWinApp::InitInstance() 中可知是第二事例 , 但如何在 return FALSE;
之前 FindWindow() 到第一事例呢 ? 主要困难是 :
1). 主窗口类名可否得到 ( 主窗口 ==MainFrame ? 若是 , 怎知 MainFrame 类名 ?
2).Title 因含有文档名 , 是一不确定串 .
1. 类名是动态的,所以应该不易得到。(不知是否如此?)
类名不是动态的,如果不作处理, MFC 总是使用 AfxFrameOrView 为类名,
只有 MainFrame, View 等等才会用 AfxFrameOrView,
我刚用 spy 看了我的一个程序,主窗口的 Class Afx:b:13ce:6:3be7
至于如何注册自己定义的类,我还没有学会,望高手指教。
AfxRegisterWndClass()
2. 我用的方法是只查 Title 中前半部分和你的要求串一致即可,忽略后面的文档 ?
FindWindow() 函数需要给定窗口类名和窗口标题( Title ),不知你是怎样做到只查前半部分的?
EnumWindows() 一个窗口一个窗口查,不能用 FindWindow()
这里我有另外一个问题:
找到第一事例的窗口后,如何激活它,使它成为当前窗口?
极小状态时,才能把它激活出来,否则也是没有动静,不知何故?
if (IsIconic(hwndMain))
ShowWindow(hwndMain, SW_RESTORE);
else
SetActiveWindow(hwndMain);
 
69. 关于 VC++2.0 中使用文件对话框的求助
VC++2.0 中使用了标准文件对话框以后 , 编译联接都能正常通过,而且使用 Ctrl+F5 执行 EXE 文件也完全正常 , 但是一旦用 F5 调试进入,使用该对话框的代码时 ,Win95 就会异常中止 , 请问各位高手 , 为什么 ?
文件对话框,改变了执行程序的当前路径,从而干扰了调试系统。如果是为了调试,可以使文件对话框定位在当前目录,并且运行时不要在其中改变目录。
 
70. 如何用 VC2.0 编写能 Edit 超过 64K 的类
我要用 VC2.0 编写一个 Editor, 要求能编辑超过 64K 的文本 . 我的 Edit 类从 CEdit 类派生出来 , 但编辑超过 64K 的文本就显示不出来 . 原知道在 VC1.5 CEdit 类只能编辑小于 64K 的文本 , 不知 VC2.0 是否也一样 ?
.
自己从 CWnd 类派生,管理内存、输入、显示等(其实不累)。至于从 CEdit 中派生,我也没有头绪。
VC2.0 会有这事?我没试过,但我不相信。 VC2.0 是运行在 Win95 WindowsNT 上的开发工具,是 Win32 的开发工具。 Windows 95 NT 对应用程序在内存上没有段的限制,应用程序最多可利用高达 4G 的内存 ( 包括系统占用 ) 。你所说的限制大概是 VC1.5 的限制吧。盖因 VC1.5 运行于 Win16 上,每个段不能超过 64K
的确如此, VC2.0 有两个例子 samples/mfc/multipad samples/mfc/superpad 也是如此。不信你看看。
 
71. VC++1.5 中如何 new 一个大数组
我想在用 VC++1.5 编程时, new 一个 [512][512] 的大数组,却总是不行.
哪位专家可以指点一下.
选用 Larger and Huger 模式的库编译
我用过 Large 模式,好象不行呀!我用 Huge 模式,头文件编译时就出错了!
VC++ 1.5 毕竟是 16 , 512*512 是多少?做链表好了否则就用 VC++2.0, 32 位就不该有这问题了
这样的做法并不十分好 , 要分配如此大的内存最好用全局的内存分配函数 . 直接用指针操作 , 效率也会提高 .
MFC 类不行吗 ? CObArray
请用 GlobalAllocPtr GlobalFreePtr 这一对函数,如下:
DWORD dwSize ;;
hpsStr =(char huge *)GlobalAllocPtr( GMEM_MOVEABLE , dwSize ) ;
...
GlobalFreePtr( hpsStr ) ;
不管多大数组,都保证没有问题。
VC1.5 中,如何得到子窗口在主窗口中的相对坐标?
比较笨的方法是先 ClientToScreen 转成屏幕坐标 , 然后 ScreenToClient 转成主窗口坐标 , 不知有没有更方便的方法 .
MapWindowPoints(hwndChild, hwndParent, (POINT FAR *)lprc, 2)lprc 设成子窗口内的坐标 (0,0, width, height)
 
72.VC1.5 如何调用进程 ?
我用 VC1.52 Windows 程序,中间想执行一个其他 Windows 应用程序,并且让我的程序挂起等应用程序执行完后再继续,请问如何实现?
exec( ) spawn( ) Windows 不兼容,所以它们肯定不行了.
WinExec()
挂起的一个简单方法是得到新进程的主窗口句柄,然后进入死循环,直到此句柄变为无效。
不知道还有什么更漂亮一些的手法。
请问,怎么得到进程的主窗口句柄呢?
是呀,我也正想问呢.例如我执行的是 PSpice for Windows. WinExec()
可以如下 :
int nWinRun = WinExec("wpspice test.cir", SW_SHOWNORMAL);
可是如何得到新进程的主窗口句柄呢?能不能截取新进程的消息呢
紧接着用 GetActiveWindow() 就可以 .
当然还是在 win3.1 下才有用 ....
win95 下由于有输入的局部化问题 , 所以只能得到本线程的 Activate Window Focus Window, 对于其他情况返回都是 NULL. 这问题还是不知道怎么办 ,alas...
所 以 最 根 本 的 办 法 是 如 何 读 写 Windows 消 息 总 库 .
哪 位 高 手 看 得 懂 SPY 的 源 程 序 ( C++ Delphi 之 类 的 Sample 中 有 ).
Use "FindWindow()". according to window title, get window handle.
then use Window Declassing method. that is write your own procedure
to process the message you want to capture. "CallWindowProc() is
executed before DefWindowProc()". (sorry can not type chinese now.)
我在 Vc 4.0 下用 _exec, _spawn, 以及 WinExec 都没问题的!
 
73.VC++1.5 Win3.1 程序能否实现全局跳转 ?
setjmp 即可,和 DOS 下一样。
Windows API 中的 Catch() Throw() 可能会更好些。不过我也不知道和 setjmp 有些什么区别。
setjmp 恐怕不行 , 它好象只能用来编写 C 程序,而不能用来编写 C++ 程序 !
我的感觉是,只要是代码段,可以不受函数和应用的限制,随便乱跳的。其实是数据段,也可以跳过去的。换句话说,在 WINDOW 中,可以跳到任何地方,只要我乐意。
 
74. DLL 用自定义窗口
我在 DLL 中用如下窗口,
class CMyWin:public CWnd
{
public:
CMyWin() ;
} ;
CMyWin::CMyWin()
{
if( !CreateEx( WS_EX_TOPMOST ,
AfxRegisterWndClass( CS_SAVEBITS ) ,
"mywin" , WS_POPUP ,
0 , 0 , 100 , 100 , NULL , NULL ) )
AfxMessageBox( "CreateEx error" ) ;
}
请问有谁知道是什么原因?谁在 DLL 中做过类似工作,望赐教。 ror
我记得 AfxRegisterWndClass 好象应该在 PreCreateWindow 中调用 ,
在构造函数中用这个函数似乎有问题 , 你再试试吧 !
C++ DLL 可以吗 ? 好象名字分裂回有些负作用吧 ?
MFC C ++是可以做传统的 DLL 的。只要将回调函数定义成如下格式就行了:
#ifdef __cplusplus
extern "C" {
#endif
void __export FAR PASCAL fun() ;
#ifdef __cplusplus
}
#endif
其它与普通 MFC 程序一样。当然,在例程类的定义上也有点特别的地方
我如果在正常的执行程序中这样用 CreateEx 就没有任何问题,而在 DLL
就有问题。并且这问题也只发生在 WIN3.1 上,在 WIN95 上却可以正常执行。
-
75. 如何让 TOOLTIP 的字体变大
前几天有个虫虫问如何让 vc4.0 Tooltip 的字体变大 , 可现在文章没有了 . 不过现在我
有个方法 : Windows 95 Desktop 属性中有 desktop scheme 中有 Tooltip 一项 , 可以该变
TOOLTIP 的字体和大小 . 不过这样做所有程序的 Tooltip 都变大了 . 这可能不是你的本意 .
Tooltip Windows 95 Common Control, Windows
95 内部实现 ,VC4.0 CTooltip 类并不实现 Tips 的显示 , 所以要实现针对某个程序的
Tooltip 的特殊显示 , 有一定难度 . 我以前曾经试图实现 Tooltip 的多行显示 , 做到了当 Toolt.
显示时触发我的函数 , 消失时触发我的函数 , 但在显示时却出 GP 错误 . 现在我认为最简
单的方法是从头自己实现 Tooltip . 一切随心所欲 .*
 
 
 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值