您的当前位置:首页正文

Visual C编程技巧《好》

2020-05-29 来源:星星旅游
Visual C++编程技巧之一

1. 如何获取应用程序的实例句柄?

2. 如何通过代码获得应用程序主窗口的指针? 3.如何在程序中获得其他程序的 图标?

4.如何编程结束应用程序?如何编程控制windows的重新引导? 5.怎样加栽其他的应用程序? 6. 确定应用程序的 路径 7. 获得各种目录信息 8. 如何自定义消息

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:\emp\\1.txt,而不用加栽与 txt文件关联的应用程序,很多安装程序完成后都会打开一个窗口,来显示Readme or Faq,偶猜就是这么作的啦.

ShellExecute(NULL,NULL,_T(\"1.txt\"),NULL,_T(\"c:\\\emp\"),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处进行声明,也就是常说的\"宏映射\"

返回上页

Visual C++ 编程技巧之二

9. 如何改变窗口的图标? 10. 如何改变窗口的缺省风格? 11. 如何将窗口居中显示?

12. 如何让窗口和 MDI窗口一启动就最大化和最小化? 13. 如何使程序保持极小状态? 14. 如何限制窗口的大小? 15. 如何使窗口不可见? 16. 如何使窗口始终在最前方?

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); }

返回上页

Visual C++编程技巧之三

17、如何创建一个字回绕的CEditView 18、通用控件的显示窗口 19、移动窗口 20、重置窗口的大小

21、如何单击除了窗口标题栏以外的区域使窗口移动 22、如何改变视窗的背景颜色 23、如何改变窗口标题

24、如何防止主框窗口在其说明中显示活动的文档名

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来设置标题。记住自己设置标题时要遵循接口风格指南。

返回上页

Visual C++编程技巧之四

25、如何获取有关窗口正在处理的当前消息的信息 26、如何创建一个不规则形状的窗口 27、如何在代码中获取工具条和状态条的指针 28、如何使能和禁止工具条的工具提示 29、如何设置工具条标题

30、如何创建和使用无模式对话框 31、如何在对话框中显示一个位图 32、如何改变对话或窗体视窗的背景颜色

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 ); }

返回上页

Visual C++编程技巧之五

33、如何获取一个对话控件的指针 34、如何禁止和使能控件 35、如何改变控件的字体

36、如何在OLE控件中使用OLE_COLOR数据类型

37、在不使用通用文件打开对话的情况下如何显示一个文件列表 38、为什么旋转按钮控件看起来倒转

39 为什么旋转按钮控件不能自动地更新它下面的编辑控件 40、如何用位图显示下压按钮

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 ; }

返回上页

Visual C++编程技巧之六

41、如何一个创建三态下压按钮 42、如何动态创建控件

43、如何限制编辑框中的准许字符

44、如何改变控件的颜色

45、当向列表框中添加多个项时如何防止闪烁 46、如何向编辑控件中添加文本 47、如何访问预定义的GDI对象 48、如何获取GDI对象的属性信息

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); }

返回上页

Visual C++编程技巧之七

49、如何实现一个橡皮区矩形 50、如何更新翻转背景颜色的文本 51、如何创建一个具有特定点大小的字体 52、如何计算一个串的大小 53、如何显示旋转文本

54、如何正确显示包含标签字符的串 55、串太长时如何在其末尾显示一个省略号 56、如何快速地格式化一个CString对象

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\Norman\Oliver\")); 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);

返回上页

Visual C++编程技巧之八

57、为什么即使调用EnableMenuItem菜单项后,菜单项还处于禁止状态 58、如何给系统菜单添加一个菜单项 59、如何确定顶层菜单所占据的菜单行数 60、在用户环境中如何确定系统显示元素的颜色 61、如何查询和设置系统参数

62、如何使用一个预定义的Windows光标 63、如何确定当前屏幕分辨率

64、如何检索原先的Task Manager应用程序使用的任务列表 65、如何确定Windows和Windows系统目录 66、在哪儿创建临文件 67、如何访问桌面窗口

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\ //Get the full path of the windows system directory. :: GetSystemDirectory (szDir, MAX_PATH); TRACE (\"Windows system directory %s\\n\

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); } „ }

返回上页

Visual C++编程小技巧之九

68. VC1.5下如何跳到第一事例?

69. 为什么VC++2.0中使用文件对话框会死机? 70. 在VC++1.5中如何new一个大数组?

71. 在VC1.5中,如何得到子窗口在主窗口中的相对坐标? 72. VC1.5 如何调用进程?

73. VC++1.5编Win3.1程序能否实现全局跳转? 74. 如何在DLL用自定义窗口? 75. 如何让TOOLTIP的字体变大?

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\ 可是如何得到新进程的主窗口句柄呢?能不能截取新进程的消息呢 紧接着用 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类.一切随心所欲.*

返回上页

因篇幅问题不能全部显示,请点此查看更多更全内容