(1) 如何通过代码获得应用程序主窗口的 指针? 主窗口的 指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现。 AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED) //使程序最大化. (2) 确定应用程序的路径 Use GetModuleFileName 获得应用程序的路径,然后去掉可执行文件名。 Example: TCHAR exeFullPath[MAX_PATH] // MAX_PATH在API中定义了吧,好象是 128 GetModuleFileName(NULL,exeFullPath,MAX_PATH) (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目录: Use "GetWindowsDirectory" Windows下的system目录: Use "GetSystemDirectory" temp目录: Use "GetTempPath" 当前目录: Use "GetCurrentDirectory" 请注意前两个函数的第一个参数为目录变量名,后一个为缓冲区后两个相反. (5) 如何自定义消息 1) 手工定义消息,可以这么写 #define WM_MY_MESSAGE(WM_USER+100), MS 推荐的至少是 WM_USER+100 (2)写消息处理函数,用 WPARAM,LPARAM返回LRESULT. LRESULT CMainFrame::OnMyMessage(WPARAM wparam,LPARAM lParam) { temp目录: Use "GetTempPath" //加入你的处理函数 irectory" } (6) 如何改变窗口的图标? 向窗口发送 WM_SECTION消息。 Example: HICON hIcon=AfxGetApp() ->LoadIcon(IDI_ICON) ASSERT(hIcon) AfxGetMainWnd() ->SendMessage(WM_SECTION,TRUE,(LPARAM)hIcon) (7) 如何改变窗口的缺省风格? 重载 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) } (8) 如何将窗口居中显示? 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( ) (9) 如何让窗口和 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的风格。 (10) 如何限制窗口的大小? 也就是 FixedDialog形式。 Windows发送 WM_GETMAXMININFO消息来跟踪, 响应它,在 OnGetMAXMININFO 中写代码: (11) 如何使窗口不可见? 很简单,用SW_HIDE 隐藏窗口,可以结合 FindWindow,ShowWindow控制. (12) 如何创建一个字回绕的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 } (13) 如何使程序保持极小状态? 这么办: 在恢复程序窗体大小时,Windows会发送WM_QUERY-OPEN消息,用 ClassWizard设置成员函数 OnQueryOpen() ,add following code: Bool CMainFrame:: OnQueryOpen( ) { Return false } (14) 移动窗口 调用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) (15) 通用控件的显示窗口 MFC提供了几个CView派生的视窗类, 封装了通用控件的功能,但仍然使用工作框文档显示窗口体系结构:CEditView封装了编辑控件,CTreeView保持了树列表控件,CListView封装了列表显示窗口控件,CRichEditView可以处理多种编辑控件。 (16) 重置窗口的大小 调用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 ) (17) 如何单击除了窗口标题栏以外的区域使窗口移动 当窗口需要确定鼠标位置时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 ) ) } (18) 如何改变视窗的背景颜色 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 } (19) 如何改变窗口标题 调用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 ) } } (20) 如何防止主框窗口在其说明中显示活动的文档名 创建主框窗口和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来设置标题。记住自己设置标题时要遵循接口风格指南。 (21) 如何获取有关窗口正在处理的当前消息的信息 调用CWnd: : GetCurrentMessage可以获取一个MSG指针。例如,可以使用ClassWizard将几个菜单项处理程序映射到一个函数中,然后调用GetCurrentMessage来确定所选中的菜单项。 viod CMainFrame : : OnCommmonMenuHandler ( ) { //Display selected menu item in debug window . TRACE ("Menu item %u was selected . \n" , (22) 如何在代码中获取工具条和状态条的指针 缺省时, 工作框创建状态条和工具条时将它们作为主框窗口的子窗口,状态条有一个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) (23) 如何使能和禁止工具条的工具提示 如果设置了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 ) } (24) 如何创建一个不规则形状的窗口 可以使用新的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 thewindow . UINT nHitTest = CDialog : : OnNcHitTest (point) rerurn (nHitTest = = HTCLIENT)? HTCAPTION: nHitTest } (25) 如何获取应用程序的 实例句柄? 应用程序的实例句柄保存在CWinApp m_hInstance 中,可以这么调用AfxGetInstancdHandle获得句柄. Example: HANDLE hInstance=AfxGetInstanceHandle() (26) 如何编程结束应用程序? 这是个很简单又是编程中经常要遇到的问题. 向窗口发送 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开发人员指南"一书有比较详细的介绍,这里就不再多说乐。 (27) 如何创建和使用无模式对话框 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) Destroy ( ) //Create the modeless dialog . represents this dialog. BOOL bResult = pDialog —> Creste (IDD_IDALOG) ASSERT (bResult ) } (28) 如何防止主框窗口在其说明中显示活动的文档名 创建主框窗口和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来设置标题。记住自己设置标题时要遵循接口风格指南。 (29) 如何在代码中获取工具条和状态条的指针 缺省时, 工作框创建状态条和工具条时将它们作为主框窗口的子窗口,状态条有一个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) (30) 怎样加载其他的应用程序? 三个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) (31) 如何在代码中获取工具条和状态条的指针 缺省时, 工作框创建状态条和工具条时将它们作为主框窗口的子窗口,状态条有一个AFX_IDW_STATUS_BAR标识符,工具条有一个AFX_IDW_TOOLBAR标识符,下例说明了如何通过一起调用CWnd: : GetDescendantWindow和AfxGetMainWnd来获取这些子窗口的指针: //Get pointer to status bar . CStatusBar * pStatusBar = (CStatusBar *) AfxGetMainWnd ( ) —> GetDescendantWindow(AFX_IDW_STUTUS_BAR) (32) 如何使能和禁止工具条的工具提示 如果设置了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 ) } //Get pointer to toolbar . CToolBar * pToolBar = (CToolBar * ) AfxGetMainWnd ( ) —> GetDescendantWindow(AFX_IDW_TOOLBAR) (33) 如何设置工具条标题 工具条是一个窗口,所以可以在调用CWnd : : SetWindowText来设置标题,例子如下: int CMainFrame : : OnCreate (LPCREATESTRUCT lpCreateStruct ) { … // Set the caption of the toolbar . m_wndToolBar.SetWindowText (_T "Standdard") (34) 如何使窗口始终在最前方? 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) } (35) 如何在对话框中显示一个位图 这要归功于Win 32先进的静态控件和Microsoft的资源编辑器,在对话框中显示位图是很容易的, 只需将图形控件拖到对话中并选择适当属性即可,用户也可以显示图标、位图以及增强型元文件。 (36) 如何改变对话或窗体视窗的背景颜色 调用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 ) } (37) 如何获取一个对话控件的指针 有两种方法。其一,调用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对话。 (38) 如何禁止和使能控件 控件也是窗口,所以可以调用CWnd : : EnableWindow使能和禁止控件。 //Disable button controls . m_wndOK.EnableWindow (FALSE ) m_wndApply.EnableWindow (FALSE ) (39) 如何改变控件的字体 由于控件是也是窗口,用户可以调用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 ) … } (40) 如何在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 backgroundcolor . pdc—> FilllRect (rcBounds , &brushBack) //other drawign commands … } (41) 在不使用通用文件打开对话的情况下如何显示一个文件列表 调用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 } (42) 为什么旋转按钮控件看起来倒转 需要调用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 按钮时拷贝的数目增加。 (43) 为什么旋转按钮控件不能自动地更新它下面的编辑控件 如果使用旋转按钮的autu buddy特性, 则必须保证在对话的标记顺序中buddy窗口优先于旋转按钮控件。从Layout菜单中选择Tab Order菜单项(或者按下Crtl+D)可以设置对话的标签顺序。 (44) 如何用位图显示下压按钮 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 . 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 } (45) 如何一个创建三态下压按钮 可以使用新的BS_PUSHBUTTON 风格位和检测框以及按钮来创建一个三态下压按钮。这很容易,只需将检测框和按钮拖拉到对话中并指定属性Push—like即可。不用任何附加程序就可以成为三态下压按钮。 (46) 如何动态创建控件 分配一个控件对象的实例并调用其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 ) (47) 如何限制编辑框中的准许字符 如果用户在编辑控件中只允许接收数字,可以使用一个标准的编辑控件并指定新的创建标志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 )) } (48) 如何改变控件的颜色 有两种方法。其一,可以在父类中指定控件的颜色,或者利用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 () } 现在,控件可以自己决定如何绘画,与父窗口无关。 (49) 当向列表框中添加多个项时如何防止闪烁 调用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 () (50) 如何向编辑控件中添加文本 由于没有CEdit:: AppendText函数,用户只好自己做此项工作。调用CEdit:: SetSel移动到编辑控件末尾,然后调用CEdit:: ReplaceSel添加文本。下例是AppendText 的一种实现方法: void CMyEdit:: AppendText (LPCSTR pText) { int nLen=GetWindowTextLength () SetFocus () SetSel (nLen, nLen) ReplaceSel (pText) } (51) 如何访问预定义的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) } (52) 如何获取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) } (53) 如何实现一个橡皮区矩形 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) } (54) 如何更新翻转背景颜色的文本 调用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) } (55) 如何创建一个具有特定点大小的字体 可以指定字体逻辑单位的大小,但有时指定字体的点的大小可能会更方便一些。可以如下将字体的点转换为字体的高度: 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")) (56) 如何计算一个串的大小 函数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 } (57) 如何显示旋转文本 只要用户使用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() } } (58) 如何正确显示包含标签字符的串 调用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) } (59) 如何快速地格式化一个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) (60) 串太长时如何在其末尾显示一个省略号 调用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) } (61) 为什么即使调用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) (62) 如何给系统菜单添加一个菜单项 给系统菜单添加一个菜单项需要进行下述三个步骤: 首先,使用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的串表的入口来实现。 (63) 如何确定顶层菜单所占据的菜单行数 这可以通过简单的减法和除法来实现。首先,用户需要计算主框窗口的高度和客户区;其次,从主框窗口的高度中减去客户区、框边界以及标题的高度;最后,除以菜单栏的高度。下例成员函数是一个计算主框菜单所占据的行数的代码实现。 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) } (64) 在用户环境中如何确定系统显示元素的颜色 调用SDK函数GetSysColor可以获取一个特定显示元素的颜色。下例说明了如何在MFC函数CMainFrameWnd:: OnNcPaint中调用该函数设置窗口标题颜色。 void CMiniFrameWnd:: OnNcPaint () { … dc.SetTextColor (:: GetSysColor (m_bActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT)) … (65) 如何查询和设置系统参数 在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) (66) 如何确定当前屏幕分辨率 调用SDK函数GetSystemMetrics,该函数可以检索有关windows显示信息,诸如标题大小、边界大小以及滚动条大小等等。 //Initialize CSize object with screen size. CSize sizeScreen (GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN)) (67) 如何使用一个预定义的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) } (68) 如何检索原先的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) } } (69) 如何确定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) (70) 在哪儿创建临文件 调用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 } (71) 我怎样才能建立一个等待光标? 调 用 BeginWaitCursor 函 数 来 启 动 等 待 光 标,调 用 EndWaitCursor 函 数 来 结 束 等 待 光 标。要 注 意,二 者 都 要 调 用 app 的 成 员 函 数,如 下 所 示: AfxGetApp()->BeginWaitCursor(); // 要做的事 AfxGetApp()->EndWaitCursor(); (72) 我在MDI框架中有个 form 视窗。它有个取消按钮,我需要当用户按取消按钮时可关闭form视窗。我应该如何关闭该文档? 调 用 OnCloseDocument 函 数。 (73) 如何访问桌面窗口 静态函数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) } … (74) 什么是COLORREF? 我该怎样用它? COLORREF 是 一 个 32-bit 整 型 数 值,它 代 表 了 一 种 颜 色。你 可 以 使 用 RGB 函 数 来 初 始 化 COLORREF。例 如: COLORREF color = RGB(0, 255, 0); RGB 函 数 接 收 三 个 0-255 数 值,一 个 代 表 红 色, 一 个 代 表 绿 色, 一 个 代 表 蓝 色。在 上 面的 例 子 中, 红 色 和 蓝 色 值 都 为 0,所 以 在 该 颜 色 中 没 有 红 色 和 蓝 色。绿 色 为 最 大 值 255。所 以 该 颜 色 为 绿 色。0,0,0 为 黑 色,255,255,255 为 白 色。 另 一 种 初 始 化 COLORREF 的 方 法 如 下 所 示: CColorDialog colorDialog; COLORREF color; if( colorDialog.DoModal() == IDOK ) { color = colorDialog.GetColor(); } 这 段 代 码 使 用 了 MFC 中 的 颜 色 对 话 框,它 需 要 文 件。 (75) AppWizard所产生的STDAFX文件是干什么用的? 它 主 要 是 协 助 产 生 预 编 译 头 文 件 的。通 常 你 是 不 需 要 修 改 它 的。 (76) 我在我的程序中是了CDWordArray。我向它添加了约10,000个整数,这使得它变得非常非常慢。为什么会这么糟? CDWordArray 是 很 好 用 的,只 是 因 为 你 没 有 指 定 数 组 的最大尺寸。因 此,当 你 添 加 新 元 素 时,该 类 会 从 堆 中 重 新 分 配 空 间。不 幸 的 是,该 类 会 在 每 次 插 入 新 元 素 时 都 为 数 组 重 新 分 配 空 间。如 果 你 向 它 添 加 了 很 多 新 元 素,所 有 这 些 分 配 和 复 制 数 组 的 操 作 会 就 会 使 它 变 慢。解 决 该 问 题 的 方 法 是,你 可 以 使 用 SetSize 函 数 的 第 二 个 参 数 来 改 变 这 种 重 新 分 配 的 频 率。例 如,如 果 你 把 该 参 数 设 置 为 500,则 每 次 数 组 空 间 超 出 时 它 才 重 新 分 配 并 添 加 500 个 新 空 间,而 不 是 1 个。这 样 一 来,你 就 可 以 不 用 重 新 分 配 而 添 加 了 另 外 499 个 元 素 空 间,这 也 会 大 大 提 高 程 序 的 运 行 速 度。 (77) 我该如何改变MDI框架窗口的子窗口的大小以使在窗口以一定的大小打开? 在 视 中 的 OnInitialUpdate 函 数 中 调 用 GetParentFrame 函 数。GetParentFrame 会 返 回 一 指 向 一 保 存 有 该 视 的 框 架 窗 口 的 指 针。然 后 调 用 在 框 架 窗 口 上 调 用 MoveWindow。 (78) 在我的程序的某些部分,我可以调用 MessageBox 函数来建立一个信息对话框,例如在视类中。但是,在其它部分我却不能,如文档类中。为什么?我怎样才能在我的应用程序类中建立一个信息对话框? MessageBox 函 数 来 自 CWnd 类,所 以 你 只 能 在 从 CWnd 继 承 的 类 ( 如 CView ) 中 调 用 它。但 是,MFC 也 提 供 了 AfxMessageBox 函 数,你 可 以 在 任 何 地 方 调 用 它。 (79) 我需要在我的程序中设置全局变量,以使文档中的所有类都能访问。我应该吧它放到哪儿? 把 该 变 量 放 到 该 应 用 程 序 类 的 头 文 件 中 的 attribute 处。然 后,在 程 序 的 任 何 地 方,你 都 可 以 用 下 面 的 方 法 来 访 问 该 变 量: CMyApp *app = (CMyApp *)AfxGetApp(); app->MyGlobalVariable = ... (80) 我听说MFC可以发现内存漏洞,我怎样使用该特性? 如 果 你 在 Debug 菜 单 中 的 Go 选 项 ( 不 是 Project 菜 单 中 的 Execute 选 项 ) 来 运 行 你 的 应 用 程 序,MFC 应 该 在 程 序 终 止 时 报 告 内 存 漏 洞。如 果 没 有,那 么 试 试 运 行 MFC Tracer 工 具 程 序 ( 在 VC++ 程 序 组 中 ),并 启 动 跟 踪。然 后 返 回 应 用 程 序。 (81) 我怎样才能在我的应用程序中循环浏览已经打开的文档? 使用CDocTemplate中未公开的GetFirstDocPosition()和GetNextDoc()函数。 (82)才能在我的应用程序中循环浏览已经打开的视? 使 用 CDocument 中 未 公 开 的 GetFirstViewPosition() 和 GetNextView() 函 数。 (83)数PreCreateWindow是干什么用的? PreCreateWindow 允 许 你 在 调 用 CreateWindow 之 前 来 改 变 窗 口 属 性。 (84)该怎样防止MFC在窗口标题栏上把文档名预置成应用程序名? 在 PreCreateWindow 函 数 中 删 除 FWS_PREFIXTITLE 标 志 的 窗 口 样 式: cs.style &= ~FWS_PREFIXTITLE; (85) 我应该怎样防止MFC在窗口标题栏上添加文档名? 在 PreCreateWindow 函 数 中 删 除 FWS_ADDTOTITLE 标 志 的 窗 口 样 式: cs.style &= ~FWS_ADDTOTITLE ; (86) 我应该如何改变视窗口的大小? 因 为 视 窗 口 实 际 上 是 框 架 窗 口 的 子 窗 口,所 以 你 必 须 改 变 框 架 窗 口 的 大 小,而 不 是 改 变 视 窗 口。使 用 CView 类 中 的 GetParentFrame() 函 数 获 得 指 向 框 架 窗 口 的 指 针,然 后 调 用 MoveWindow() 函 数 来 改 变 框 架 的 大 小。这 会 使 变 尺 寸 的 视 充 满 框 架 窗 口。 (87) 我有一无模式对话框。我怎样才能在窗口退出时删除CDialog对象? 把“delete this”加 到 PostNcDestroy 中。这 主 要 用 在 需 要 自 动 删 除 对 象 的 场 合。 (88) 为什么把“delete this”放在PostNcDestroy中而不是OnNcDestroy? OnNcDestroy 只 被 已 建 立 的 窗 口 调 用。如 果 建 立 窗 口 失 败 ( 如 PreCreateWindow ),则 没 有 窗 口 处 来 发 送 WM_NCDESTROY 消 息。PostNcDestroy 是 在 对 象 窗 口 被 完 全 删 除,在 OnNcDestroy 后,甚 至 在 窗 口 建 立 失 败 之 后 调 用 的。 (89) File菜单中的MRU列表是从哪儿来的?列表中的名字放在哪儿了?我怎样才能改变列表中项目的最大值? 在 应 用 程 序 类 的 InitInstance 函 数 中 对 LoadStdProfileSettings 的 调 用 中。该 调 用 接 受 一 个 参 数 ( 在 缺 省 情 况 下 如 果 没 有 传 递 值 则 为 4 )。MRU 文 件 名 是 从 INI 文 件 中 调 用 的。如 果 你 有 带 有 ID_FILE_MRU_FILE1 的 ID 的 菜 单 选 项,它 会 为 调 入 的 MRU 列 表 所 替 换。如 果 你 改 变 传 递 给 LoadStdProfileSettings 的 数 值 ( 最 大 为 16 ),则 你 就 改 变 了 所 装 如 文 件 名 的 最 大 值。 (90) 我在菜单中添加了新的项。但是,当我选该项时,在状态栏上没有出现任何提示信息。为什么? 打 开 资 源 文 件 中 的 菜 单 模 板。打 开 新 菜 单 选 项 的 属 性 对 话 框。在 对 话 框 的 底 部 的 Prompt 编 辑 框 中 ,你 可 以 如 下 指 定 状 态 栏 上 的 提 示 信 息 和 工 具 栏 上 的 提 示 信 息 ( 如 果 你 已 经 建 立 的 工 具 栏 按 钮 ): Status bar string\nFlying tag (91) 我怎样才能在应用程序的缺省系统菜单中加上一些东西? 系 统 菜 单 与 其 它 菜 单 类 似,你 可 以 添 加 或 删 除 项 目,这 需 要 使 用 CMenu 类 的 成 员 函 数。下 面 的 代 码 在 你 的 系 统 菜 单 后 面 添 加 一 个 新 菜 单 项: CMenu *sysmenu; sysmenu = m_pMainWnd->GetSystemMenu(FALSE); sysmenu->AppendMenu(MF_STRING, 1000, "xxx"); 参 见 MFC 帮 助 文 件 中 的 CMenu 类。 (92) 我建立了一个对话框。但是当我显示该对话框时,第一个编辑框总是不能获得焦点,我必须单击它来使它获得焦点。我怎样才能使第一个编辑框在对话框打开时就获得焦点? 打 开 资 源 编 辑 器 中 的 对 话 框 模 板。在 Layout 菜单 中 选 择 Tab Order 选 项。按 你 的 需 求 单 击 对 话 框 中 的 控 制 来 重 新 排 列 这 些 控 制 的 tab 顺 序。 (93) 我怎样才能使一个窗口具有“always on top”特性? 在 调 用 OnFileNew 后,在 你 的 InitInstance 函 数 中 加 上 下 面 的 代 码: m_pMainWnd->SetWindowPos(&CWnd::wndTopMost,0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); (94) 我要为我的form view添加文档模板。我先建立了对话框模板,然后使用ClassWizard建立了基于CFormView的新类,它也是从CDocument继承来的。我还建立了相应的资源并在InitInstance中添加了新的文档模板。但是,当我试图运行该程序时,出现了Assertion信息。为什么? form 的 对 话 框 模 板 需 要 些 特 殊 设 置 以 便 可 用 于 CFromView。确 保 这 些 设 置 的 最 简 单 方 法 是 使 用 AppWizard 来 建 立 CFormView 应 用 程 序,并 查 看 AppWizard 所 建 立 的 对 话 框 模 板 所 选 择 的Styles Properties。你 会 发 现 该 对 话 框 模 板 具 有 下 列 样 式:没 有 标 题 栏、不 可 见 和“Child”。把 你 的 form view 的 对 话 框 属 性 变 成 这 样 就 可 以 了。 (95) 我在一对话框中有一列表框,我需要tabbed列表框中的项目。但是,当我处理含有tab字符(用AddString添加的)的列表项时,tab被显示成小黑块而没有展开。哪儿出错了? 在 对 话 框 模 版 中,打 开 列 表 框 的 属 性。确 保 选 择 了“Use Tabstops” 样 式。然 后,确 保 在 对 话 框 类 中 OnInitDialog 函 数 中 调 用 SetTabStops。 (96) 我建立了一个应用程序,并使用了CRecordset类。但是,当我运行该程序时,它试图要访问数据库,并给出“Internal Application Error”对话框。我应该怎样做? 通 常 情 况 下,当 你 的 程 序 中 向 数 据 库 发 送 信 息 的 SQL 语 句 出 现 问 题 时 才 出 现 该 对 话 框。例 如,参 见 下 面 的 例 子: set.m_strFilter = "(ZipCode = '27111')"; 如 果 ZipCode 列 被 定 义 为 字 符 串 时 不 会 出 现 问 题,如 果 定 义 为 long,则 会 出 现“Internal Application Error”对 话 框,这 是 由 于 类 型 不 匹 配 的 缘 故。如 果 你 删 除 27111 的 单 引 号,则 不 会 出 现 问 题。当 你 看 到“Internal Application Error”时,最 好 检 查 一 下 试 图 要 发 送 给 数 据 库 的 SQL 语 句。 (97) 我用ClassWizard建立了一个类。但是,我把名字取错了,我想把它从项目中删除,应该如何做? 在 ClassWizard 对 话 框 关 闭 后,用 文 件 管 理 器 删 除 新 类 的 H 和 CPP 文 件。然 后 打 开 ClassWizard,它 会 提 示 丢 失 了 两 个 文 件,并 询 问 你 该 如 何 做。你 可 以 选 择 从 项 目 中 删 除 这 两 个 问 的 按 钮。 (98) 当我打开应用程序中的窗口时,我要传递该窗口的矩形尺寸。该矩形指定了窗口的外围大小,但是当我调用GetClientRect时,所得到的尺寸要比所希望的值要小(因为工具栏和窗口边框的缘故)。有其它方法来计算窗口的尺寸吗? 参 见 CWnd::CalcWindowRect。 (99) 我在文档类中设置了一个整型变量。但是,当我试图把该变量写入Serialize函数中的archive文件中时,出现了类型错误。而文档中的其它变量没有问题。为什么? archive 类 只 重 载 某 些 类 型 的 >> 和 << 操 作 符。“int”类 型 没 有 在 其 中,也 许 是 因 为 int 变 量 在 Windows 3.1 与 Windows NT/95 有 所 不 同 的 缘 故 吧。“long”类 型 得 到 了 支 持,所 以 你 可 以 把 int 类 型 改 成 long 型。参 见 MFC 帮 助 文 件 中 CArchive 类。 (100) 如何控制菜单的大小? 我用MFC的CMenu生成了一个动态菜单(例如File,Edit,View...Help), 我想控制这个菜单的大小(长+高). 方法一:查找 WM_MEASUREITEM 和 MEASUREITEMSTRUCT. 方法二:查询系统::GetSystemMetric(SM_CXMENUSIZE). /* 你可以通过如下代码来获得文本的大小: (A)获得被使用的字体 */ NONCLIENTMETRICS ncm; HFONT hFontMenu; SIZE size; size.cy = size.cy = 0; memset(&ncm, 0, sizeof(NONCLIENTMETRICS)); ncm.cbSize = sizeof(NONCLIENTMETRICS); if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)) { hFontMenu = CreateFontIndirect(&ncm.lfMenuFont); /* (B) 获得菜单项的文本: */ char szText[_MAX_PATH]; pMenu->GetMenuString(0, szText, _MAX_PATH, MF_BYPOSITION); /* 然后,获得菜单项文本的高度: */ HFONT hFontOld; HDC hDC; hDC = ::GetDC(NULL); hFontOld = (HFONT) ::SelectObject(hDC, hFontMenu); GetTextExtentPoint32(hDC, szText, lstrlen(szText), &size); SelectObject(hDC, hFontOld); ::ReleaseDC(NULL, hDC); } /*此时,size.cy即为高度,size.cx为宽度,你可以给菜单加上自定义的高度和宽度,通过比较,我发现宽度为4 比较合适。*/ (101) 改变LVIS_SELECTED的状态颜色? 我想将CListCtrl项和CTreeCtrl项在LVIS_SELECTED状态时的颜色变灰. 方法一:查找函数CustomDraw,它是IE4提供的公共控制,允许有你自己的代码. 方法二:生成一个draw控件,然后在DrawItem中处理文本颜色. (102) 如何只存储文档的某一部分? 我只想存储文档的某一部分,能否象使用文件一样使用文档?(也就是有定位函数).将每个CArchive类设置为CFile类的派生类,这样你就能使用Seek等成员函数. (103) 保存工具条菜单有bug吗? 使用浮动菜单条时,SaveBarState和LoadBarState出现了问题.如果菜单是浮动的,重起应用程序时它会出现在左上角,而它固定在屏幕其它位置时,下一次启动就会出现在该位置,这是什么原因?你试试这个PToolBar->Create(this,...,ID_MYTOOLBAR); 你的工具条需要包括id,而不是象默认的工具条那样. (104) Tip of the day的bug 我创建了一个简单的mdi应用程序,使用.BSF(自定义的文档扩展名)作为它的文档我保存一个foo.bsf文档后,可以在资源管理器中双击该文件打开mdi应用程序同时打开foo.bsf文档.但当我给mdi应用程序加上a tip of the day组件之后,从资源管理器中双击foo.bsf后,就会给我一个警告:ASSERT(::IsWindow(m_hWnd)),然后mdi应用程序就死那了. 当从dde启动应用程序(例如:双击相关文档)时,"Tip of the Day"是有bug的.你可以看看函数"ShowTipAtStartup",它在"InitInstance"中调用,可以看到tip of the day作为一个模式对话框显示,在处理其它消息时它一直进行消息循环你可心修改ShowTipAtStartup使其从dde启动时不出现tip of the day. void CTipOfApp::ShowTipAtStartup(void) { // CG: This function added by 'Tip of the Day' component. CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if ( cmdInfo.m_bShowSplash && cmdInfo.m_nShellCommand != CCommandLineInf:FileDDE ) { CTipDlg dlg; if (dlg.m_bStartup) dlg.DoModal(); } } 如果还有其它bug,你可以设定cmdInfo.m_nShellCommand的过滤. (105) 如何可以让我的程序可以显示在其它的窗口上面? 让用户选择"总是在最上面"最好是在系统菜单里加入一个选项.可以通过修改WM_SYSCOMMAND消息来发送用户的选择.菜单的命令标识(id)会作为一个参数传给OnSysCommand().要定义标识(id),将如下代码加入到CMainFrame.CPP中: #define WM_ALWAYSONTOP WM_USER + 1 将"总在最上面"的菜单项加入到系统菜单中,将如下代码加入到函数CMainFrame::OnCreate()中: CMenu* pSysMenu = GetSystemMenu(FALSE); pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, WM_ALWAYSONTOP, "&Always On Top"); 使用ClassWizard,加入对WM_SYSCOMMAND消息的处理,你应该改变消息过滤器,使用系统可以处理这个消息. void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam) { switch ( nID ) { case WM_ALWAYSONTOP: if ( GetExStyle() & WS_EX_TOPMOST ) { SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); GetSystemMenu(FALSE)->CheckMenuItem(WM_ALWAYSONTOP, MF_UNCHECKED); } else { SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); GetSystemMenu(FALSE)->CheckMenuItem(WM_ALWAYSONTOP,MF_CHECKED); } break; default: CFrameWnd::OnSysCommand(nID, lParam); } } (106) 如何控制窗口框架的最大最小尺寸? 要控制一个框架的的最大最小尺寸,你需要做两件事情.在CFrameWnd的继承类中处理消息WM_GETMINMAXINFO,结构MINMAXINFO设置了整个窗口类的限制,因此记住要考虑工具条,卷动条等等的大小. // 最大最小尺寸的象素点 - 示例 #define MINX 200 #define MINY 300 #define MAXX 300 #define MAXY 400 void CMyFrameWnd::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) { CRect rectWindow; GetWindowRect(&rectWindow); CRect rectClient; GetClientRect(&rectClient); // get offset of toolbars, scrollbars, etc. int nWidthOffset = rectWindow.Width() - rectClient.Width(); int nHeightOffset = rectWindow.Height() - rectClient.Height(); lpMMI->ptMinTrackSize.x = MINX + nWidthOffset; lpMMI->ptMinTrackSize.y = MINY + nHeightOffset; lpMMI->ptMaxTrackSize.x = MAXX + nWidthOffset; lpMMI->ptMaxTrackSize.y = MAXY + nHeightOffset; } 第二步,在CFrameWnd的继承类的PreCreateWindow函数中去掉WS_MAXIMIZEBOX消息,否则在最大化时你将得不到预料的结果. BOOL CMyFrameWnd::PreCreateWindow(CREATESTRUCT& cs) { cs.style &= ~WS_MAXIMIZEBOX; return CFrameWnd::PreCreateWindow(cs); } (107) 如何改变窗口框架的颜色? MDI框架的客户区被另一个窗口的框架所覆盖.为了改变客户区的背景色,你需要重画这个客户窗口.为了做到这点,你要处理消息WM_ERASEBKND产生一个新类,从CWnd继承,姑且称之为CMDIClient.给它加上一个成员变量, #include "MDIClient.h" class CMainFrame : public CMDIFrameWnd { ... protected: CMDIClient m_wndMDIClient; } 在CMainFrame中重载CMDIFrameWnd::OnCreateClient BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { if ( CMDIFrameWnd::OnCreateClient(lpcs, pContext) ) { m_wndMDIClient.SubclassWindow(m_hWndMDIClient); return TRUE; } else return FALSE; } 然后就可以加入对消息WM_ERASEBKGND的处理了. (108) 如何将应用程序窗口置于屏幕正中? 要将你的应用程序窗口放置在屏幕正中央,只须在MainFrame的OnCreate函数中加入: CenterWindow( GetDesktopWindow() ); 1)当文档被修改时,如何在标题上加上标志'*'? 重载CDocument类的虚函数virtual SetModifiedFlag: void CTest2Doc::SetModifiedFlag(BOOL bModified) { CString strTitle = GetTitle(); CString strDirtyFlag = " *"; // note space before the '*' // so we don't break Save As dialog if (!IsModified() && bModified) { SetTitle(strTitle + strDirtyFlag); } else if ( IsModified() && !bModified ) { int nTitleLength = strTitle.GetLength(); int nDirtyLength = strDirtyFlag.GetLength(); SetTitle( strTitle.Left(nTitleLength - nDirtyLength) ); } UpdateFrameCounts(); CDocument::SetModifiedFlag(bModified); } (2)VC6.0对VC5.0的兼容性? 很不幸,vc6.0在调试模式对vc5.0不兼容,但发行模式没有问题.原因在微软改变了调试模式所用dll的格式,而保留了原文件名. 因此,不要在vc6.0中打开vc5.0的调试版本工程. (3)打印和打印机的问题? 我碰到这么一个问题:在打印方法中使用了MM_LOMETRIC模式,在LOGFONT结构中改变了字体的大小,但不知道173(或者对于屏幕而言是25)是从哪来的,它是自动的.然而当我用另外一个打印机时173并不适合.我想知道的是:我如何对所有的打印来调整这个数字. 我以前也碰到过类似的问题,我让用户改变字体(大小,颜色等等).这些改变在屏幕上看起来挺好,但是打印时太小(我的同事在程序包中加入一个放大类).原因非常简单:打印机的分辨率可能是300dpi,而屏幕的分辨率则低得多.我是这么解决的:在获得屏幕字体信息后,我获取屏幕字体的毫米级大小(使用LPtoDP,然后将模式变为MM_LOMETRIC,调用DPtoLP),接着对打印机设定了相同的模式,再调用LPtoDP.切换回原来的模式之后,我调用了DPtoLP,这样就得到了想要的字体高度和宽度. 在LOGFONT中使用这个值,并且带有其它诸如下划线,斜体等字体信息,我实现了用户的要求. (4)CRichEditCtrl滚动条的问题? 我使用了CRichEditCtrl控制来显示某个文件中的数据(将该控制设置为只读).我已经设置了ES_MULTILINE | ES_AUTOVSCROLL,但当数据内容比控制显示多的时候,滚动条并不出现,是不是因为设置了只读属性而引起了其它的问题? ES_AUTOVSCROLL | ES_AUTOHSCROLL属性只在控制是可编辑时有效.你可心使用下面的滚动条风格来使滚动条出现:WS_VSCROLL | WS_HSCROLL,但是这样一来,不管你的数据量有多大,滚动条总是会出现. (5)从数据库中读大于32k的内容? 我在从数据库中读数据时碰到了问题.当数据栏包含超过32k的内容时,我就读不出来,我试过ODBC::SQLGetData()也不行. 哪种类型的数据库?MS SQL,SYBASE... 试试设置一下大小: BOOL CGetBlobStmt::Execute(LPCTSTR stmt) { m_cbSize = 0; m_size = 0; LPBYTE lpData; lpData = (LPBYTE)GlobalLock(m_hData); m_retcode = SQLSetStmtOption(GetHandle(),SQL_MAX_LENGTH,m_dwBytesLeft); m_retcode = SQLExecDirect(GetHandle(),(UCHAR*)stmt,SQL_NTS); if (m_retcode == SQL_SUCCESS) { m_retcode = SQLFetch(GetHandle()); if (m_retcode == SQL_SUCCESS ||m_retcode == SQL_SUCCESS_WITH_INFO) { m_retcode = SQLGetData(GetHandle(),1,SQL_C_BINARY,lpData,254,&m_cbSize); while(m_retcode == SQL_SUCCESS_WITH_INFO) { lpData+= 254; m_retcode = SQLGetData(GetHandle(),1,SQL_C_BINARY,lpData,254,&m_cbSize); } GetError(); } } GlobalUnlock(m_hData); #if TESTDATA TRACE("%ld",m_size); #endif SaveFile(); return RETVALUE; } (6)如何获得CRichEditCtrl中字符的位置? 我想在CRichEditCtrl中使用右键菜单,因此想判定光标处字符的位置,请指点. 查看如下的帮助: IRichEditOleCallback::GetContextMenu EM_SETOLECALLBACK (7)如何限制mdi子框架最大化时的大小? 用ptMaxTrackSize代替prMaxSize,如下所示: void CChildFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) { // TOD Add your message handler code here and/or call default CChildFrame::OnGetMinMaxInfo(lpMMI); lpMMI->ptMaxTrackSize.x = 300; lpMMI->ptMaxTrackSize.y = 400; } (8)如何切换视口而不破坏它们? 我创建了一个带有静态分隔区的sdi应用程序,左边显示工作区,右过显示左边选取的东西.我想达到的是如果在分隔区之间进行切换,而不覆盖或破坏原来的CView对象. 以下代码是你所想要的: class CExSplitterWnd : public CSplitterWnd { // Construction public: CExSplitterWnd(); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CExSplitterWnd) //}}AFX_VIRTUAL // Implementation virtual ~CExSplitterWnd(); BOOL AttachView(CWnd* pView, int row, int col); BOOL DetachView(int row, int col); // Generated message map functions //{{AFX_MSG(CExSplitterWnd) // NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CExSplitterWnd::CExSplitterWnd() { } CExSplitterWnd::~CExSplitterWnd() { } BOOL CExSplitterWnd::AttachView(CWnd* pView, int row, int col) { //Make sure the splitter window was created if (!IsWindow(m_hWnd)) { ASSERT(0); TRACE(_T("Create the splitter window before attaching windows to panes")); return (FALSE); } //Make sure the row and col indices are within bounds if (row >= GetRowCount() || col >= GetColumnCount()) { ASSERT(0); return FALSE; } //Is the window to be attached a valid one if (pView == NULL || (!IsWindow(pView->m_hWnd))) { ASSERT(0); return FALSE; } pView->SetDlgCtrlID(IdFromRowCol(row, col)); pView->SetParent(this); pView->ShowWindow(SW_SHOW); pView->UpdateWindow(); return (TRUE); } BOOL CExSplitterWnd::DetachView(int row, int col) { //Make sure the splitter window was created if (!IsWindow(m_hWnd)) { ASSERT(0); TRACE(_T("Create the splitter window before attaching windows to panes")); return (FALSE); } //Make sure the row and col indices are //within bounds if (row >= GetRowCount() || col >= GetColumnCount()) { ASSERT(0); return FALSE; } CWnd* pWnd = GetPane(row, col); if (pWnd == NULL || (!IsWindow(pWnd->m_hWnd))) { ASSERT(0); return FALSE; } pWnd->ShowWindow(SW_HIDE); pWnd->UpdateWindow(); //Set the parent window handle to NULL so that this child window is not //destroyed when the parent (splitter) is destroyed pWnd->SetParent(NULL); return (TRUE); } (9)改变列表控制时发生闪烁现象? 我创建了一个简单的对话框,在对话框中设置了一个列表控件,这个控件占用了对话框的全部客户区.对话框是可以改变大小的,因此我要保证列表控件在对话框中维持正确的位置,在对话框的ONSize()事件中我对列表控件使用了MoveWindow(),这起到了作用,但当用户改变对话框的大小时,列表控件不停地闪烁. 要解决这个问题,在用MoveWindow之前,先用ShowWindow(SW_HIDE)隐藏列表控件,然后在MoveWindow之后用ShowWindow(SW_SHOW)来显示列表控件. (10)处理列表控件可见项的问题? 我在一个列表控件中加入了好多条目.我通过获取某个条目是否可见或最后是哪个条目来进行处理.我看了CListCtrl::GetItem()的帮助,但是没有找到如何判断一个条目是否可见的方法. 如果你只想处理可见的条目,你可以用GetTopIndex.它返回最大可见条目的索引值,然后你再用GetCountPerPage来得到在可见区域的条目数. (11)产生线程的问题? 我在使用CreateThread时碰到了问题.我想让调用的函数和被调用的函数属于同一个类,结果在我调用CreateThread时得到如下错误: error C2440: 'type cast' : cannot convert from 'unsigned long (__stdcall Cdmi::*)(void *)' to 'unsigned long (__stdcall *)(void *)' 方法一: (1)'unsigned long (__stdcall Cdmi::*)(void *)'是指向Cdmi某个成员函数的指针. (2)'unsigned long (__stdcall *)(void *)'仅仅只是一个c形式函数的指针. 编译器无法将(1)转换为(2)是因为c++成员函数取第一个(隐藏)参数"this pointer"作为成员函数,但当是一个静态的成员时则例外.可按如下方法解决. class XMyThread { public: void StartThread(void); virtual UINT ThreadFunction(void); static UINT __bogusthreadfunc(LPVOID lpparam); }; void XMyThread::StartThread() { AfxBeginThread(__bogusthreadfunc,this); } UINT XMyThread::ThreadFunction(void) { //here you do all your real work return 0; } UINT XMyThread::__bogusthreadfunc(LPVOID lpparam) { XMyThread* This = dynamic_cast(lpparam); return This->ThreadFunction(); } for the sake of clairty, I did not add StopThread and I did not save the CWinThread* returned by AfxBeginThread. If you wanted a thread that does other things, simply derive from XMyThread and override ThreadFunction() example: class XAnotherThread : public XMyThread { virtual UINT ThreadFunction(void); }; UINT XAnotherThread :: ThreadFunction(void) { //do some other work here return 0; } 方法二:Cdmi::MonitorFiles()是个静态的成员函数. (12)CFile使用了缓冲区吗? 请告诉我CFile到底有没有使用缓冲区来处理文件? CFile没有使用运行库的I/O缓冲例程,从这个意义上讲CFile并没有使用缓冲.但是有可能操作系统在处理文件时使用了缓冲区,如果你完全不需要缓冲区,你可以设置FILE_FLAG_NO_BUFFERING.CFile工作在这种模式下的唯一的方法是CFile::Attach(). (13)DAO的密码? 我创建了一个使用数据库的mfc应用程序.用类模板生成CDaoRecordset直接打开数据库(不通过ODBC),但问题是我如何打开有密码保护的数据库? 方法一:试试下面的代码: DAODBEngine* pDBEngine = AfxDaoGetEngine(); ASSERT(pDBEngine != NULL); COleVariant varUserName (strUserName, VT_BSTRT); COleVariant varPassword (strPassword, VT_BSTRT); DAO_CHECK(pDBEngine->put_DefaultUser (V_BSTR(&varUserName)); DAO_CHECK(pDBEngine->put_DefaultPassword (V_BSTR(&varPassword)); 方法二:你可以使用CDaoDatabase的Open方法来打开: MyDaoDatabase->Open("C:\MyDatabaseFile.mdb",FALSE,FALSE,";PWD=MyPassWord"); btw:不要忘了PWD=前面的;号. (14)如何知道CListBox什么时候滚动了? 每次绘制列表框都要重绘某项,通过消息WM_CTLCOLOR从父窗口获得DC颜色.因此每欠列表框的滚动你都可以用WM_CTLCOLOR来检验是否滚动. HBRUSH CParentDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { // is the control _the_ list box we're interested in? if( nCtlColor == CTLCOLOR_LISTBOX && pWnd->GetDlgCtrlID() == IDC_LIST ) { // if the top index changed, the list box has been scrolled int iTop = ((CListBox*)pWnd)->GetTopIndex(); if( iTop != m_iTopOld ) { // keeps tracking of the top index changes m_iTopOld = iTop; // process scrolling ... } } HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); return hbr; } 使用这种方法可以不必为了实现这个功能而去产生一个继承类. (15)视口的不活动性如何处理? 有什么方法使CListView成为类似WM_DIASBLED的风格,或者使它和背景色一致. 方法一:你所要做的是处理CListView的WM_SETFOCUS消息,然后在TreeView中调用SetFocus, 这样,ListView就永远不会获得焦点. afx_msg void CMyListView::OnSetFocus(CWnd* pOldWnd); { // assuming m_pwndTreeView points to the valid TreeView // on the left side m_pwndTreeView->SetFocus(); } 方法二:重载PreTranslateMessage,然后当消息为WM_LBUTTONDOWN或WM_RBUTTONDOWN时返回真即可. (16)如何使用COleClientItem的IDispatch接口? 我创建了一个如何使用COleClientItem对象,我想使用它的自动化方法.有什么方法来获得IDispatch的接口?我试过以CCmdTarget为基类的的GetIDispatch函数但却出错,我用过EnableAutomation和GetIDispatch,却什么也没得到. MSDN中有一篇关于这个的文章(TN039).如下的代码也可能是你所需要的: LPDISPATCH CMyClientItem::GetIDispatch() { ASSERT_VALID(this); ASSERT(m_lpObject != NULL); LPUNKNOWN lpUnk = m_lpObject; Run(); // must be running LPOLELINK lpOleLink = NULL; if (m_lpObject->QueryInterface(IID_IOleLink, (LPVOID FAR*)&lpOleLink) == NOERROR) { ASSERT(lpOleLink != NULL); lpUnk = NULL; if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR) { TRACE0("Warning: Link is not connected!\n"); lpOleLink->Release(); return NULL; } ASSERT(lpUnk != NULL); } LPDISPATCH lpDispatch = NULL; if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR) { TRACE0("Warning: does not support IDispatch!\n"); return NULL; } ASSERT(lpDispatch != NULL); return lpDispatch; } (17)关于用户自定义的消息使用? 我写了一个基于MFC应用程序的对话框,在这个程序中,我创建了等待网络传输数据的线程,一旦该线程接收到数据,它就传送一个用户自定义的消息到对话框,使对话框知道有数据过来.但是为何在CMyDialog::PreTranslateMessage(MSG* pMsg)中能捕捉到WM_MYCMD这个消息,却不能和OnMyCommand相映射? 将你所有自定义消息的基类设为WM_APP,而不是WM_USER. (18)在打开一个文档时退出? 我有一个mdi程序,在打开文件的处理过程中,我想判断该文档是不是应用程序需要处理的文档,因此,我检测文档中的某个数字是否符合要求,如何在发现不是该文档时出现一个错误提示,然后不打开该文档? 给文档设定某个标志,如果文档不是所要的就设定它.然后OnOpenDocument中检测,当发现标志被设定后返回FALSE. (19)在CListCtrl控件中多选择项的删除? 如何从在CListCtrl中删除多个选择项? 按如下方法处理:如果你的在CListCtrl是m_list,to_delete是个整数数组. i=3D0; POSITION pos=3Dm_list.GetFirstSelectedItemPosition(); if(pos) while(pos) to_delete[i++]=3Dm_list.GetNextSelectedItem(pos); 然后用删除保存在to_delete中的项目,用GetSelectedCount来得到已选项的个数. (20)工作线程的登录状态? 我使用循环删除了用AfxBeginThread创建的线程的好几个实例.每个线程打开一个iNET连接,打开一个URL并返回结果.我需要找出哪一个或者何时这些线程进入到登录状态. 按如下方法处理:(伪代码) // Start Threads for( unsigned u = 0; u < NUMBER_OF_THREADS; u++ ) { ThreadHandleArray[ u ] = AfxBeginThread( ...... )->m_hThread; } DWORD count = NUMBER_OF_THREADS DWORD dwWait; while( count ) { dwWait = ::WaitForMultipleObjects( count, ThreadHandleArray, FALSE, INFINITE ); if( dwWait >= WAIT_OBJECT_0 && dwWait < ( WAIT_OBJECT_0 + count ) ) { dwWait -= WAIT_OBJECT_0; // dwWait now has index to thread that completed do whatever you want to do with it // set array back up for next wait if( dwWait != ( count - 1 ) ) ThreadHandleArray[ dwWait ] = ThreadHandleArray[ count - 1 ]; count--; } } (21)如何增加视图中ActiveX控件的事件处理函数? 如果我在对话框中加入微软的网络浏览器,很容易通过类模板加入对事件的处理.但我现在在视图中用m_pBrowser=new CWebBrowser2加入了网络浏览器,我该如何对事件进行处理? 到http://www.vcdj.com(inet/章节)去看看,有一篇文章名为"Building a Webbrowser in a Afternoon".如下的代码也可能是你所需要的: #include // For AFX_EVENT def. BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { AFX_EVENT *pEvent = (AFX_EVENT *)pExtra; //If this is a control notification event. if (nCode == CN_EVENT) { // If we have information on this event. if (pEvent) { // Event DISPID is stored at pEvent->m_dispid // Event DISPPARAMS are stored at pEvent->m_pDispParams // Handle the event from here... } } return CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } (22)如何创建一个动态的Tree控件? 我想创建一个动态的tree控件,就象弹出窗口一样,但它并不象想象中那么容易. 方法一:用CreateWindow(SDK)创建风格为WS_POPUP,WS_CAPTION和WS_TICKFRAME的窗口,并作为父窗口. 方法二:创建一个包含Tree控件的对话框. (23)SDI程序开始时不打开文档? 我创建了一个SDI应用,但每次启动时它都会打开一个文档("untitled"),如何不让它打开该文档呢? 看看InitInstance函数中有没有关于OnFileNew的调用,去掉它即可. (24)List控件中整栏选择? 我在处理List控件时碰到了麻烦,我想创建一个ListView,来依据Tree控件的选择同时在ListView和ReportView中显示列表的信息.以下是相关的代码: // Set full line select ListView_SetExtendedListViewStyle(m_plstCustomers->GetSafeHwnd(), LVS_EX_FULLROWSELECT); 按如下方法处理: // -------------------- begin of snippet -------------------------------- bool CCommCtrlUtil32::ListCtrl_ModifyExtendedStyle(CListCtrl& p_rListCtrl, const DWORD p_dwStyleEx, const bool p_bAdd) { HWND t_hWnd = p_rListCtrl.GetSafeHwnd(); DWORD t_dwStyleEx = ListView_GetExtendedListViewStyle(t_hWnd); if(p_bAdd) { if(0 == (p_dwStyleEx & t_dwStyleEx)) { // add style t_dwStyleEx |= p_dwStyleEx; } } else { if(0 != (p_dwStyleEx & t_dwStyleEx)) { // remove style t_dwStyleEx &= ~p_dwStyleEx; } } ListView_SetExtendedListViewStyle(t_hWnd, t_dwStyleEx); return true; } (25)如何重载MRU文件? 我创建了一个应用程序可以载入图象文件,但当我点击FILE菜单下MRU文件列表时,却不能从磁盘载入以前曾经打开过的文件. 下面是我所能想到的解决方案: (1)在文档类中定义一个成员函数(例如:CMyDoc::Reopen)来处理重新打开这个问题,指明参数和返回值. (2)产生一个CMultiDocTemplate的继承类(如CMyDocTemplate),定义一个构造函数,取和基类相同的参数,不做任何事,只是调用基类的构造函数. (3)重载MatchDocType: CMyDocTemplate::Confidence CMyDocTemplate::MatchDocType( LPCTSTR lpszPath, CDocument *&rpDocMatch ) { Confidence match = CMultiDocTemplate::MatchDocType(lpszPath, rpDocMatch); if(yesAlreadyOpen == match) // clear enough { ASSERT_KINDOF(CMyDoc, rpDocMatch); ((CMyDoc *) rpDocMatch)->Reopen(/* your parameters */); // you can take any other actions here... } return match; } 当这个函数返回"yesAlreadyOpen"时,你的文档框架将会被激活. -------------------------------------------------- (26)CImageList控件中图象橙色被显示为黄色? 我使用了一个CImageList控件来装入位图,用于TREE控件,其它的色彩都很正常就是橙色被显示成为黄色. 你只能使用系统指定的20种颜色(橙色不包括在内);当然,你也可以用下面的方法来装载位图资源而不受颜色数的限制. HBITMAP LoadResourceBitmap(HINSTANCE hInstance, LPSTR lpString, HPALETTE FAR* lphPalette) { HRSRC hRsrc; HGLOBAL hGlobal; HBITMAP hBitmapFinal = NULL; LPBITMAPINFOHEADER lpbi; HDC hdc; int iNumColors; if (hRsrc = ::FindResource(hInstance, lpString, RT_BITMAP)) { hGlobal = ::LoadResource(hInstance, hRsrc); lpbi = (LPBITMAPINFOHEADER)LockResource(hGlobal); hdc = ::GetDC(NULL); *lphPalette = CreateDIBPalette ((LPBITMAPINFO)lpbi, &iNumColors); if (*lphPalette) { ::SelectPalette(hdc,*lphPalette,FALSE); ::RealizePalette(hdc); } hBitmapFinal = ::CreateDIBitmap(hdc, (LPBITMAPINFOHEADER)lpbi, (LONG)CBM_INIT, (LPSTR)lpbi + lpbi->biSize + iNumColors * sizeof(RGBQUAD), (LPBITMAPINFO)lpbi, DIB_RGB_COLORS ); ::ReleaseDC(NULL,hdc); // ::UnlockResource(hGlobal); // ::FreeResource(hGlobal); } return (hBitmapFinal); } // internally used by LoadResourceBitmap HPALETTE CreateDIBPalette (LPBITMAPINFO lpbmi, LPINT lpiNumColors) { LPBITMAPINFOHEADER lpbi; LPLOGPALETTE lpPal; HANDLE hLogPal; HPALETTE hPal = NULL; int i; lpbi = (LPBITMAPINFOHEADER)lpbmi; if (lpbi->biBitCount <= 8) *lpiNumColors = (1 << lpbi->biBitCount); else *lpiNumColors = 0; // No palette needed for 24 BPP DIB if (lpbi->biClrUsed > 0) *lpiNumColors = lpbi->biClrUsed; // Use biClrUsed if (*lpiNumColors) { hLogPal = GlobalAlloc (GHND, sizeof (LOGPALETTE) + sizeof (PALETTEENTRY) * (*lpiNumColors)); lpPal = (LPLOGPALETTE) GlobalLock (hLogPal); lpPal->palVersion = 0x300; lpPal->palNumEntries = *lpiNumColors; for (i = 0; i < *lpiNumColors; i++) { lpPal->pal PalEntry. peRed = lpbmi->bmiColors.rgbRed; lpPal->palPalEntry.peGreen = lpbmi->bmiColors.rgbGreen; lpPal->palPalEntry.peBlue = lpbmi->bmiColors.rgbBlue; if (i<=10 || i>=246) lpPal->palPalEntry.peFlags = PC_NOCOLLAPSE; else lpPal->palPalEntry.peFlags = 0; } hPal = CreatePalette (lpPal); GlobalUnlock (hLogPal); GlobalFree (hLogPal); } return hPal; } 该函数也重载了位图调色板,这个功能被CBitmap::LoadBitmap忽略了(它假定位图只使用20种颜色).因此要保证在DC中有SelectPalette和RealizePalette. (27)无法正确改变应用程序的图标? 我有一个基于对话框的应用程序,在初始化时我使用了AfxGetApp()->LoadIcon(IDI_BRIEFCASE)来载入自己的图标,当把程序拷贝到桌面上时,图标是我所期望的.但在资源管理器中的图标却还是MFC的图标. 资源管理器仅使用16x16的小图标,可能你在资源编辑器中只修改了32x32的标准图标.你需要重建16x16的小图标. (28)工具条状态的问题? 在应用程序中我创建了三个工具条,我想让它们在应用程序启动的时候排成一行正好在主菜单的下面,我该如何去做? 在VC CDs上有一个例子: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { //other stuff here... EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar,AFX_IDW_DOCKBAR_TOP); DockControlBarLeftOf(&m_wndListToolBar,&m_wndToolBar); return 0; } void CMainFrame::DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf) { CRect rect; DWORD dw; UINT n; // get MFC to adjust the dimensions of all docked ToolBars // so that GetWindowRect will be accurate RecalcLayout(); LeftOf->GetWindowRect(&rect); rect.OffsetRect(1,0); dw=LeftOf->GetBarStyle(); n = 0; n = (dw & CBRS_ALIGN_TOP) ? AFX_IDW_DOCKBAR_TOP :n; n = (dw & CBRS_ALIGN_BOTTOM && n==0) ? AFX_IDW_DOCKBAR_BOTTOM :n; n = (dw & CBRS_ALIGN_LEFT && n==0) ? AFX_IDW_DOCKBAR_LEFT :n; n = (dw & CBRS_ALIGN_RIGHT && n==0) ? AFX_IDW_DOCKBAR_RIGHT :n; // When we take the default parameters on rect, DockControlBar will dock // each Toolbar on a seperate line. By calculating a rectangle, we in effect // are simulating a Toolbar being dragged to that location and docked. DockControlBar(Bar,n,&rect); } (29)在SDI应用程序中使用Active控件? 我刚了解到如何在MFC应用程序中使用Active控件,文档上说只能在视图为CFormView和CDialog时使用,但要是其它的情况该怎么办呢? 你可以在你应用程序的任何地方使用Active控件,而不仅仅局限于CFormView和CDialog为视图基类的情况.DevStudio通过资源编辑器和对话框模板来使得在上述两个条件下使用Active控件更容易.因此,你也可以在任何视图中使用Active控件,条件是你直接操纵该控件,创建它并手工的布置好它的位置(这也是DevStudio为你所做的事). (30)有RichEdit控件的对话框无法正常显示? 我在对话框中放置了一个RichEdit控件,但是对话框却无法正常显示. 在你的应用程序InitInstance()中调用了::AfxInitRichEdit()吗? (31)DLL中的模板成员函数? 在一个DLL中,我在自己创建的类中使用了模板成员函数来代替预处理宏.但出现以下错误: error C2664: 'double Data::extract(double &)' : cannot convert parameter 1 from 'class CArray' to 'double &' 为什么在匹配模板定义时它要寻找一个DOUBLE参数? 我觉得你可能是在表达成员函数(内联)时出现了问题,请参照下面的示例: class AFX_EXT_CLASS Data : public CObject //This is not a template { public: Data(); Data(BYTE * buffer,int size); template Data(const CArray& array); template CArray& extract(CArray& array) { CArchive ar(&buffer, CArchive::store); ar >> array; }; double extract(double&); (...) private: CMemFile buffer; } (32)CFormView中的上下文帮助? 我想在基于CFormView类的SDI应用程序中加入真正的上下文帮助,但没有成功. 你应该重载CMyFormView类的OnHelpHitTest函数: LRESULT CMyFormView::OnHelpHitTest(WPARAM, LPARAM lParam) { LRESULT lResult = (LRESULT)0x00; CWnd* pWndChild = ChildWindowFromPoint(CPoint(lParam), CWP_ALL|CWP_SKIPINVISIBLE); if (pWndChild && ::IsWindow(pWndChild->m_hWnd)) { lResult = ::GetWindowLong(pWndChild->m_hWnd, GWL_ID); if (lResult) lResult += HID_BASE_COMMAND; } if (lResult == (LRESULT)0x00) lResult = ::GetWindowLong(m_hWnd, GWL_ID) + HID_BASE_RESOURCE; return lResult; } 然后你就可以使用平时用的帮助文件了,但你要保证有正确的前缀,请参照 TN028:Context-Sensitive Help Support. 例如: ID_SOME_MENU_ITEM_OR_COMMAND_BUTTON IDR_SOME_WINDOW_OR_DIALOG IDP_PROMPT IDW_CONTROL_THAT_IS_NOT_A_COMAND_BUTTON 你要确认你所使用的控件的ID包含在文件resource.hm中. (33)CArchive类的WriteObject函数问题? 谁知道在使用CArchive类的WriteObject函数时,如何避免将类名写入文件吗? WriteObject函数不仅写入了类名,而且还写入PID(请查看TN02),如果你只想写进一个文本文件,并且你也想用串行化,你可以使用文件指针(用GetFile)来存储字符串.或者,你可以使用CFILE类来处理这个问题,如果是文本文件,你也可以用CStdioFile类. (34)RegisterWindowMessage中的BroadcastSystemMessage如何处理? 我想用BroadcastSystemMessage来在两个进程之间通讯,我从一个进程发送了一个用RegisterWindowMessage注册过的消息,但在目的进程中却没有收到该消息. 我认为你应该在两个进程的最高级窗口中都注册该消息.请看下例: static UINT sBroadcastCommand = ::RegisterWindowMessage( _T("BroadcastCommand")); BEGIN_MESSAGE_MAP( Gui_Top_Level_MainFrame, Gui_MainFrame ) ON_REGISTERED_MESSAGE( sBroadcastCommand, onBroadcastCommand ) END_MESSAGE_MAP() LRESULT Gui_MainFrame :: onBroadcastCommand( UINT aMsg, LPARAM lParam ) { your code... } 然后发送进行应该包含: While the sending process would contain: static UINT sBroadcastCommand = ::RegisterWindowMessage( _T("BroadcastCommand")); void Someclass :: someMethod( void ) { ::PostMessage( (HWND)HWND_BROADCAST, sBroadcastCommand, 0, yourMessageId ); } (35)CListCtrl中选择变化时如何获得通知? 我在Report View中使用了一个CListCtrl(自绘制类型),我想知道什么时候选择项发生了改变. 在选择项变化时,可以使用按钮有效或失效,按如下操作: 加入LVN_ITEMCHANGED消息处理. void CYourClassNameHere::OnItemchangedEventList(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; *pResult = 0; if (pNMListView->uChanged == LVIF_STATE) { if (pNMListView->uNewState) GetDlgItem(IDC_DELETE)->EnableWindow(TRUE); else GetDlgItem(IDC_DELETE)->EnableWindow(FALSE); } } (36)如何向ATL-COM对象传送一个数组? 我想创建一个函数来向ATL-COM对象传送数组. 如下代码的方法用于ACTIVEX中,可能对ATL-COM也有启发吧. CoInitialize(NULL); CLSID m_clsid; USES_CONVERSION; ::CLSIDFromString(T2OLE("ROUNDANALOG.RoundAnlgAARCtrl.1"), &m_clsid); IDispatch FAR* pObj = (IDispatch FAR*)NULL; CString str = "UpdateControl"; BSTR bstr = str.AllocSysString(); HRESULT hr = CoCreateInstance(m_clsid, NULL, CLSCTX_ALL, IID_IDispatch, (void**)&pObj); SafeArrayAccessData(psa, (void**)&bstrArray); bstrArray[0] = str.AllocSysString(); bstrArray[1] = str.AllocSysString(); SafeArrayUnaccessData(psa); VARIANTARG* pvars = new VARIANTARG[1]; VariantInit(&pvars[0]); pvars[0].vt = VT_ARRAY|VT_BYREF|VT_BSTR; pvars[0].pparray = &psa; DISPID dispid; hr = pObj->GetIDsOfNames(IID_NULL, &bstr, 1,LOCALE_USER_DEFAULT, &dispid); DISPPARAMS disp = {pvars, &dispid, 1,1}; hr = pObj->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,DISPATCH_PROPERTYPUT,&disp,NULL, NULL, NULL); delete[] pvars; pObj->Release(); CoUninitialize(); 在你的控制中建立如下并变量参考: void CRoundAnlgAARCtrl::SaveFunc(const VARIANT FAR& var) { // TOD Add your dispatch handler code here ASSERT(var.vt == VT_ARRAY | VT_BYREF | VT_BSTR); SAFEARRAY* psa = *var.pparray; } (37)如何选择CTreeCtrl中的节点文本进行编辑? 在向CTreeCtrl中加入一项后,有什么方法可以编辑该节点的文本呢? 首先设置你的CcompTreeCtrl具有TVS_EDITLABELS属性.在设计时用控件属性来设置在运行时用GetStyle()/SetStyle()成员函数来设置.然后请看下述代码: HTREEITEM CCompTreeCtrl::AddSet() { static int setCnt =3D 1; HTREEITEM hItem; CString csSet; //create text for new note: New Set 1, New Set 2 ... csSet.Format( _T( "New Set %d" ), setCnt++ ); hItem =3D InsertItem( csSet, IMG_CLOSEDFOLDER, IMG_CLOSEDFOLDER ); if( hItem !=3D NULL ) EditLabel( hItem ); return hItem; } (38)如何改变默认的光标形状? 我试着将光标改变为其它的形状和颜色,但却没有变化. 在对话框/窗口/你需要的地方加上对WM_SETCURSOR消息的处理. BOOL MyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { // TOD Add your message handler code here and/or call default ::SetCursor(AfxGetApp()->LoadCursor(IDC_MYCURSOR)); return TRUE; //return CDialog::OnSetCursor(pWnd, nHitTest, message); } 你没有成功的原因是因为窗口类光标风格不能为NULL. (39)如何用键盘滚动分割的视口? 我的问题是当我用鼠标滚动分割窗口时,视口滚动都很正常,但用键盘时,却什么也没有发生. 在你的视图继承类中加入如下两个函数,假定该类为CScrollerView: void CScrollerView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { BOOL processed; for (unsigned int i=0;i< nRepCnt&&processed;i++) processed=KeyScroll(nChar); if (!processed) CScrollView::OnKeyDown(nChar, nRepCnt, nFlags); } BOOL CScrollerView::KeyScroll(UINT nChar) { switch (nChar) { case VK_UP: OnVScroll(SB_LINEUP,0,NULL); break; case VK_DOWN: OnVScroll(SB_LINEDOWN,0,NULL); break; case VK_LEFT: OnHScroll(SB_LINELEFT,0,NULL); break; case VK_RIGHT: OnHScroll(SB_LINERIGHT,0,NULL); break; case VK_HOME: OnHScroll(SB_LEFT,0,NULL); break; case VK_END: OnHScroll(SB_RIGHT,0,NULL); break; case VK_PRIOR: OnVScroll(SB_PAGEUP,0,NULL); break; case VK_NEXT: OnVScroll(SB_PAGEDOWN,0,NULL); break; default: return FALSE; // not for us // and let the default class // process it. } return TRUE; } (40)如何在线程中处理状态条? 在我的应用程序CWnd的继承中有指针指向状态条,用pStatusBar->SetPaneText(0,status,TRUE)在状态条上显示一些文本都很正常.但在第二个线程中调用该函数却不行,出现hwnd警告. 当你传送一个CWnd的指针到另外一个线程时,m_hWnd将为空.我的办法是用PostThreadMessage传送消息到状态条的父类,让它对状态条进行处理. (41)如何阻止WINDOWS关闭? 我有一个应用程序会不停地工作.当该程序正常运行时,该如何避免用户关掉系统?是不是该用WM_QUERYENDSESSION. 是的,在你的主框架窗口类中使用. // in the class header afx_msg BOOL OnQueryEndSession( WPARAM wReserved, LPARAM lEndReason ); // in the Message Map ON_MESSAGE( WM_QUERYENDSESSION, OnQueryEndSession ) // in the class body BOOL CMainFrame::OnQueryEndSession( WPARAM wReserved, LPARAM lEndReason ) { if( lEndReason =3D=3D ENDSESSION_LOGOFF ) { // user is logging off else // Windows is going down return( bCanExit ); } (42)如何使一个按钮Disable? 我使用下面代码来Disable一个为ID_BUTTON的按钮,为什么会没有变化. GetDlgItem(IDC_BUTTON)->EnableWindow(FALSE); CWnd类中的EnableWindow函数用来Enable或Disable一个窗口类的对象,因为CButton类继承于类CWnd,所以你可以使用来操作一个按钮.Enable一个基于窗口类的对象可以用以下代码: pWnd->EnableWindow(TRUE); Disable一个对象可用 pWnd->EnableWindow(FALSE); 其中pWnd为一个指向窗口对象的指针VC++中消息WM_ENABLE告诉窗口它正在Disable或Enable,但它并不能使一个窗口Enable或Disable. (43)怎样从MFC扩展动态链结库(DLL)中显示一个对话框? 我在过去的几天中试着在DLL中定义的函数中显示一个对话框,可是已经在DLL中定义好的对话框资源,在常规DLL调用时,我可以正常的显示出来,为什么在扩展DLL中同样的资源我却不能显示. 当你在DLL中使用资源时,有些小细节需要注意,首先,在DLL运行时,必须保存DLL的实例,可以通过AfxInitExtensionModule static AFX_EXTENSION_MODULE extensionDLL; extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) { if (dwReason == DLL_PROCESS_ATTACH) { // Extension DLL one-time initialization if (!AfxInitExtensionModule(extensionDLL, hInstance)) return false; } return(true); } 然后,每次使用DLL资源时,你必须改变资源的句柄,使其指向DLL,并保存exe的资源,以便以后正确恢复 void get_DLL_resource(void) { /* this function changes the resource handle to that of the DLL */ //这个函数改变资源句柄使其指向DLL if (resource_counter == 0) { save_hInstance = AfxGetResourceHandle(); AfxSetResourceHandle(extensionDLL.hModule); } resource_counter++; } 接着你需要其它函数来恢复资源句柄 void reset_DLL_resource(void) { /* this function restores the resource handle set by 'get_DLL_resource()' */ if (resource_counter > 0) resource_counter--; if (resource_counter == 0) AfxSetResourceHandle(save_hInstance); } 接下来一点非常重要,只要有可能就必须恢复资源句柄,否则,你将会遇到许多问题.原因是可执行文件必须重画工具条等等,比如说,如果用户移动DLL的对话框,如果资源句柄仍然为DLL的资源,程序就崩溃了,我发现最好恢复句柄的时机在对话框的OnInitDialog()中,这时对话框的模板等已经读出了. (44)想隐藏用户界面怎么办? 我编了一个小巧而有趣的工具,当用户使用时我不想让它显示出任何用户界面。听听各位有办法可将视关闭。 你可以注册一个新的窗口类型,它拥有除了WS_VISBLE属性外的任何属性,类似CFrameWnd,在PreCreateWindow方法中实现。另外,你能在OnCreate方法中通过设置m_nCmdShow为SW_HIDE来实现,具体方法如下: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // hide our app AfxGetApp()->m_nCmdShow = SW_HIDE; return 0; } (45)如何实现SDI与MDI的转换? 我想将一个编好的SDI应用程序转换为MDI,很明显要有多处的改变。 你可以这样做:建立一个继承于CMDIChidWnd的类,不防设为CChldFrm.在CWinApp中作如下变化。 InitInstance() { . ... //instead of adding CSingleDocTemplate // Add CMultiDocTemplate. pDocTemplate = new CMultiDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CSDIDoc), RUNTIME_CLASS(CChldFrm), // For Main MDI Frame change this frame window from // CFrameWnd derivative ( i.e. CMainFrame ) // to your CMDIChildWnd derived CChldFrm. RUNTIME_CLASS(CSDIView)); /// After this it is required to create the main frame window // which will contain all the child windows. Now this window is // what was initially frame window for SDI. CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; ..... } 在从CMDIFrameWnd中继承的类CMainFrame代替CFramWnd后,所有的类都将从CMDIFrame继承,而不是CFrameWnd,编译运行后你就会发现程序已经从SDI变换到MDI。 注意:在CMainFram中必须将构造函数从private改为public.否则会出错。 (46) CDC中的竖排文本? 在OnDraw成员函数中我想让文本竖直对齐,但CDC类似乎不支持该处理 方法一:如果你的竖直对齐是指旋转文本的话,下面的代码会对你有帮助:该代码检查一个Check box控制,查看文本是否需要旋转. // m_pcfYTitle is a CFont* to the selected font. // m_bTotateYTitle is a bool (==TRUE if rotated) void CPage1::OnRotateytitle() { LOGFONT lgf; m_pcfYTitle->GetLogFont(&lgf); m_bRotateYTitle= ((CButton*)GetDlgItem(IDC_ROTATEYTITLE))->GetCheck()>0; // escapement is reckoned clockwise in 1/10ths of a degree: lgf.lfEscapement=-(m_bRotateYTitle*900); m_pcfYTitle->DeleteObject(); m_pcfYTitle->CreateFontIndirect(&lgf); DrawSampleChart(); } 注意如果你从CFontDialog中选择了不同的字体,你应该自己设定LOGFONT的lfEscapement成员.将初始化后的lfEscapement值传到CFontDialog中. 方法二:还有一段代码可参考: LOGFONT LocalLogFont; strcpy(LocalLogFont.lfFaceName, TypeFace); LocalLogFont.lfWeight = fWeight; LocalLogFont.lfEscapement = Orient; LocalLogFont.lfOrientation = Orient; if (MyFont.CreateFontIndirect(&LocalLogFont)) { cMyOldFont = cdc->SelectObject(&MyFont); } (47)如何激活变灰的弹出菜单? 在设计菜单时设定为GRAYED的菜单项,如何在运行时激活它? 请看下面的示例代码: void CMyView::OnRButtonDown(UINT nFlags, CPoint point) { CScrollView::OnRButtonDown(nFlags, point); CMenu *menu, *popup; menu = new CMenu(); // load menu from resource file menu->LoadMenu( IDR_POPUPMENU ); popup = menu->GetSubMenu(0); // item 0 is DUMMY UINT nEnable; nEnable = MF_BYCOMMAND|MF_GRAYED; if( your test ) { nEnable = MF_BYCOMMAND|MF_ENABLED; } popup->EnableMenuItem( ID_YOUR_ID, nEnable ); //display menu ClientToScreen(&point); popup->TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this ); delete menu; } (48)线程消息? 如何正确地在线程之间传送消息? 下面的代码将会帮你的忙: void CThread::OnUserOpen( WPARAM wParm, LPARAM lParm ) { UNUSED( wParm ) ; UNUSED( lParm ) ; AfxMessageBox("User Open", MB_OK|MB_ICONEXCLAMATION); } 当然,也别忘了以下声明: class CThread : public CWinThread { DECLARE_DYNCREATE(CThread) protected: CThread(); // protected constructor used by dynamic creation afx_msg void OnUserOpen( WPARAM wParm, LPARAM lParm ); (49)TreeCtrl控制的显示速度太慢? 我从CTreeCtrl继承了一个TREE控制类,重载主要是为了改写每个节点的文本.我在 OnPaint函数中写了一些代码,但这严重地影响了TREE控制的滚动速度. OnPaint函数 1.可见节点,对于GetFirstVisibleItem和GetNextVisibleItem来讲,是: a.根节点;b.父节点已展开的节点;因此,"可见"意味着"没有被未展开的父节点隐藏".当节点滚动到客户外时,它对上述两个函数来讲仍是可见的. 2.当TREE的内容改变时,它默认只将变为可见的节点重绘.另外其它已经是可见的节点没有必要重绘,TREE只是滚动DC的位图而已. 上面的意思是不要绘制你不需要看的节点,那会导致速度降低.建议,测试节点矩形是否在客户区,使得只有需要绘制的节点才会被绘制. void CIndentTree::OnPaint() { CPaintDC dc(this); // device context for painting HTREEITEM hItem = NULL; DRAWITEMSTRUCT dis; CRect rc; // redraw only visible items with indentation for( hItem = GetFirstVisibleItem(); hItem; hItem = GetNextVisibleItem( hItem ) ) { if( !GetItemRect( hItem, rc, FALSE ) ) continue; if( rc.top <= dc.m_ps.rcPaint.bottom && rc.bottom > dc.m_ps.rcPaint.top &&=20 rc.left <= dc.m_ps.rcPaint.right && rc.right > dc.m_ps.rcPaint.left ) { dis.hwndItem = (HWND)hItem; dis.rcItem = rc; OnDrawItem(0, &dis, &dc); } } } OnDrawItem函数 1.删掉如下代码: IMAGEINFO* pinfo = new IMAGEINFO; ... delete pinfo; 没有必要使用动态的IMAGEINFO变量,你可以将其定义为堆栈变量. 2.GetItemState和GetItemText都是使用的GetItem,因此,你只需调用一次, 就可以从节点获得你要的所有信息. (50)关于工具条? 我需要在程序中做一个FLAT工具条,于是我加入一个变量m_wndToolBar. 在程序主体窗口的OnCreate()函数中修改工具条状态(0,TBSTYLE_FLAT). 在NT中运行正常,为什么在95中工具条变得透明? 在COMCTL32.DLL中的旧版本中有些小bug,绘画时会带来一些问题, 你可以使用一些定制代码,在http://www.codeguru.com/站点上有下载,如果你使用的是6.0版本,你也可以使用下列代码(摘自我的mainfrm.cpp文件) m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); 用CreateEx替换ModifyStyle在同一尺寸的工具条中有些不同((CreateEx 建立的略小些),试一下,如果你仍然有这个问题,检查一下COMCTL32.DLL的版本使用标准按钮替换FLAT. (51)关于线程消息? 真奇怪,OnUserOpen()函数和OnUserOpen(WPARAM, LPARAM)函数竟然不是一个函数,编译器在查到OnUserOpen(WPARAM, LPARAM)时,不会调用OnUserOpen 莫非有人在消息映象做了什么手脚? 其实,这是MFC严密的表现,处理时,通过函数指针来调用,而该指针是由发生的事件所决定的.如果句柄不正确定义的话,调用当然是非法的 (52)关于控件的焦点? 有谁能给我一个有效的方法:当一个控件失去焦点时,怎样管理才能使对话框的焦点进入到正确的控件. 我有一个可运行的程序来实现,不一定很全面,但能工作. const int WM_VALIDATE_PARAMS WM_APP + 101; void CMyDlg::OnKillfocusName() { PostMessage(WM_VALIDATE_PARAMS, ED_NAME, 0L); } void CMyDlg::OnKillfocusAddress() { PostMessage(WM_VALIDATE_PARAMS, ED_ADDRESS, 0L); } bool CMyDlg::OnValidateParams(WPARAM rcId, LPARAM) { switch( rcId ) { case ED_NAME: if( !validateName() ) m_edName.SetFocus(); break; case ED_ADDRESS: if( !validateAddress() ) m_edAddress.SetFocus(); break; default: break; } return true; } 上面的代码可以在用户使用TAB键或鼠标操纵时,使用焦点跳至下一个控制.当你想DISABLE一个控件或重设焦点时,会有些问题,特别是在Killfocus事件中。 (53)如何捕获键盘按键? 在CTabCtrl的子对话框怎样才能捕获ALT+0组合键 可以在PreTranslateMessage中截取键盘消息。 (54)怎样实现3D效果? 在对话框中怎样实现Edit和Listboxes控件的3D效果?(环境95/NT VC5.0) 1). 使用带WS_EX_CLIENTEDGE标志的::CreateWindowEx来替换::CreateWindow 或者用CWnd::CreateEx替换CWnd::Create. 2).在建立控件之后,调用ModifyStyleEx(0, WS_EX_CLIENTEDGE). (55)怎样建立客户CSocket? 我有一个客户socket想在socket中建立一个局域联接.我使用下列顺序: CSocket* m_pSocket; m_pSocket = new CSMSSocket(this); m_pSocket->Create(); m_pSocket->Bind(m_intHostPort, m_strHostIPAddress); m_pSocket->Connect(lpszAddress, nPort); 但每次Windows Socket都定向到别的端口,怎样才能定向到同一个端口(环境:95/NT VC5.0). 1).如果你想用Client Socket,你就不能在connect()之前调用bind(),因为局域端口地址由TCP/IP设置,我们不可能知道下一次将使用那一个端口,我想我们不必这做. 2).看一下Create()的帮助,里面告诉我们必须给Create()指定一个端口值, 缺省的情况为0,也就是由Window为我们选择一个端口,通过Create()将会自动捆绑. 3).我不认为你应该完成所有的工作,但想总是用一个相同的端口来连接远程机器是一个不正确的想法. 问题出在端口数/地址结合必须唯一,如果你想在Create()中指一个固定的端口数,你只能与远程机器建一个单个连接.在你所写的代码中是允许局域端口数可变化,可以打开多个连接来取得相同的地址.在侦听(listening)Socket中有许多理由使用一个固定端口,但在连接(connecting Socket中我想没有太多的必要. (56)Disable一个非模态对话框的客户区? 我在OCX(对象连接和嵌入客户控制程序)有一个非模态对话框.它有一个菜单以及工具条.现在我想Disable客户区(只是客户区,例如:设置特殊变量时显示一个等待光标,区域里的所有控制都不可以处理)但在客户区的所有控制要看上去没有变化(也就是不可以Disable) 可以这样试一下,建立一个子窗口,覆盖对话框的全部的用户区域,用WS_EX_TRANSPAPENT 透明类型,然后调用函数EnableWindow(FALSE),使用SetClassLong或者别的方法,在子窗口调用"忙"光标,这时光标就正确了,但对话框中的菜单还能正常使用.(说白了就是建立一个透明的子窗口盖住所有的用户区域,然后Disable该透明窗口,在这个窗口中设置光标为"忙") 这个方法我没有试过,但在一些老的Windows的书介绍过这种方法. (57)关于使用SetClassLong和SetCapture问题 我用SetClassLong设置对话框光标时遇到了一些问题,当我使用SetCapture捕获鼠标时, 光标形状并没有变化时,以下为原代码: void CMouseMoveSimDlg::OnLButtonDown(UINT nFlags, CPoint point) { myDragging = TRUE; myhPrevCursor = (HCURSOR)SetClassLong( m_hWnd, GCL_HCURSOR, (long)LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_SELECTCURSOR ))); SetCapture(m_hWnd); CDialog::OnLButtonDown(nFlags, point); } 如果移去SetCapture这一行,光标就会正确的设置,但它就不能正确的捕获鼠标消息.那儿出问题了(环境NT4.0 VC6.0)? 1).如果我没有记错的话,SetClassLong只影响调用它以后的建立的窗口.可以使用 SetWindowLong来改变已存在的窗口的属性.(为什么要用SetClassLong来改变光标形状, 为什么不在消息WM_SETCURSOR中替换.) 2).我也不清楚问题出在那儿,但下面的方法可以克服SetCapture带来的问题,它是从我的程序里面提出来的: void CScribbleView::OnLButtonDown(UINT nFlags, CPoint point) { ........ SetCapture(); // Capture the mouse until button up myhPrevCursor = (HCURSOR)SetClassLong( m_hWnd, GCL_HCURSOR, (long)LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CURSOR1))); SetCursor(LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CURSOR1))); ........ } void CScribbleView::OnLButtonUp(UINT nFlags, CPoint point) { if( GetCapture() != this ) return; ........ ReleaseCapture(); SetClassLong( m_hWnd, GCL_HCURSOR, (long)myhPrevCursor); return; } (58)动画控件? 我在对话框中使用一个动画控件,通常我都是用CAnimated的open成员函数,并加上avi的文件名来使用动画控件,怎样在资源文件加入一个avi文件,作为资源使用? 1).简单,将avi文件引入资源,按你的喜欢来决定是属于那一种类型的,通过ID来代替文件的名字,这样你就可以使用了. 2).在资源窗口中单击右键,在弹出的菜单中选择"Import".这时会打开文件选择框,选择所要的文件,这时系统将会询问自定义资源类型,输入avi.一个AVIS的资源组将会创立,你所选的avi文件将会出现在该组中并拥有一个ID. 3).手动在资源文件中加入一个AVI资源说明,比如: //在这手工编辑资源文件 IDR_ANIMATION AVI res\animate.avi (59)错误声明的消息? 我给一个视发送一条消息 pView->SendMessage (MY_MESSAGE, wparam, lparam); 消息声明被认为是不正确的 afx_msg void OnMyMessage(); 高手一看就知道这是一条命令错误的声明对象,正确的声明应该为: afx_msg LERSULT OnMyMessage (WPAPRAM wparam, LPARAM lparam); 因为我不使用参数,程序工作也很好,所以我不知道为什么会有这种错误,该过程处理完之后也没有任何错误的信息出现.但现在release版本中有一个奇怪的现象(debug版本中没有)程序会非正常终止,通常这现象发生在SendMessage()返回之后。为什么? 1.相信问题是出在错误的堆栈上,"thiscall"调用后就应该清除堆栈,调用者调用时将两个参数压入堆栈,但参数却没有被清除.如果你真的不需要WPARAM,LPARAM,也不需要返回值的话,你可以使用ON_MESSAGE_VOID 消息声明.在afxpriv.h中定义,是非文档的,意思就是它不会有什么提示或可能中断程序, 另外,需要注意一下线程消息,注意线程消息是可变的,它们将返回void,没有LRESULT,同样的声明. 2.如果你不使用WParam和LParam,为什么不在视中定义一个用户函数来处理自己想做的? (59)怎样模拟鼠标动作? 这是困扰我多时的一个问题,怎样才能实现模拟鼠标的动作,就是说要使一个程序实现鼠标的单击,双击,拖放等功能.我认为必须要实现相应的消息传递,但每次都不成功. 比如说,我想关闭记事本窗口,可以传送WM_lBUTTONDOWN和WM_LBUTTONUP(X,Y值为记事本的右上角关闭按钮的位置)给记事本窗口,但窗口并没有关闭.当然,我也知道关闭一个窗口可以通过传送WM_QUIT或WM_CLOSE来实现,但鼠标的消息为什么会丢失? 请教各位大师,怎样模式模拟实现鼠标的动作,或者给我一些怎样发送消息来关闭窗口的建议(不是WM_CLOSE或WM_QUIT) 1).试一下window hooks,你可以使用SetWindowsHookEx和JournalPlayback来处理鼠标事件. 2).你可以使用文档中的SendInput(),它能实现模拟键盘或鼠标事件.如果你使用NT,那也可以用老的函数像mouse_event(),keyb_event等,在Win98中,SendInput()一样可以使用. 3).抱歉不能给你一个满意的回答,你可以在网站http://www.microsoft.com/enable/dev/tooldev.htm 中找到一篇关于模拟输入的文章. 4).在NT中可以使用mouse_event()传递事件,文档上说这种方法已经过时了,那么你可以用 SendInput()替换,但找不到关于此函数的使用说明,所以我依然使用mouse_event,没有任何问题. (60)改变对话框标题字体? 怎样改变对话框标题文件的字体,改变资源中对话框属性中的字体,将改变所有的控件的字体, 却没有改变标题,但我只想改变标题字体,不改基余控件的属性.是不是我错过一些明显的选项. 通过查找一些MFC代码,我发现有一个CDialog模块,里面调用了一引起字体方法,但该对话框不是公用的,我相信它不会给我任何帮助. 1).就我所知,对话框的标题字体和其它的窗口标题一样,它可以通过系统--显示器--属性--外观来设置,如果自己想这样做,我想你应该取得WM_NCPAINT句柄自己来画出非用户区域(包括标题在内),我从未做这样做过,可能是个错误的方向. 2).如果你是在CView继承的,那你可以在构造函数中看见如下代码: if( !my_CFont .CreatePointFont( 180,"Helvetica",NULL ) ) return false; GetEditCtrl().SetFont( &my_CFont ,true ) 接下来如果你想改变在对话框中的一个CEdit控件字体时,可以使用以下代码: if( !my_CFont .CreatePointFont( 180,"Helvetica",NULL ) ) return false; ( GetDlgItem (ID_ANY_CEDIT) ) ->SetFont( &my_CFont ); (61)怎样知道CWinThread对象的状态? 怎样才能知道一个线程是在运行还是已经终止? 可以利用线程句柄所指的::GetExitCodeThread()函数,如果线程已经结束, 它将返回一个退出代码,如果还在运行,则返回一个STILL_ACTIVE.不过在之此前,先将 CWinThread成员对象m_bAutoDelete设置为FALSE.另外对象在线程结束时会自动检测到. (62)如何调整控件对话框条的大小? 我想让用户能够在控制条出现时控制它的大小,在所有的例子中,在控件浮动时,改变尺寸还可以,但在工具条停靠在框架上时就无法调整其大小,该怎样实现? 1)也许你错过了一些注意点,我用的是codeguru站点上下载的CCoolDialogBar类, 在工具条停靠时也可以重新改变其大小. 2)我开发了一个应用程序,它的界面跟你所说的差不多,让我试着解释一下我是怎样做的. 1.从CDialogBar类中继承一个类,名为CMyBar; 2.在CMyBar中增加一个成员变量,int m_iWidth; 3.在CMyBar中的OnPaint和OnNcPaint中画出工具条(grab bar); 4.拖动工具条时在鼠标事件时绘出轨迹; 5.释放鼠标时,计算CMyBar新宽度.可以通过取得当前轨迹位置,使m_iWidth等于新的宽度; 6.(重要)GetDockingFrame()->RecalcLayout(); 7.在CMybar中增加一个成员方法CalcDynamicLayout; 8.在CalcDynamicLayout中,当工具条停靠时,通过计算m_iWidth返回值. 当然,这只是一个很简单的方法,你可以做得比这更好. 3)可以试一下VC6.0中的CReBar类 (63)如何顶端显示CStatic类文字? 我正写一个小的应用程序,我想显示一串文本(CStatic)并且无论别的应用程序运行时是否覆盖,这些文字总会在最上面显示. 1)用CreateEx来建立一个WS_POPUP窗口,使这个窗口总在最上面(always on top) 然后在该窗口中实现文字显示. 2)建立窗口时用SetWindowPos()函数,用&wndTopMost作为第一个参数,这样就可以完成你想做的了. (64)消息句柄出了什么事? 我在CParentView中为WM_LBUTTONDOWN定义一个句柄,但我建个新的CChildView, 句柄得不到处理. 1)仔细看一下你ChildView文件中的MESSAGE_MAP,可能在第两个参数匹配 BEGIN_MESSAGE_MAP(Child,Parent)中有着错误的基类.如果你是用向导生成器, 那么你很容易就会发生这种事情. 2)检查一下消息映象宏中的类名和父类名是否正确,比如BEGIN_MESSAGE_MAP (CChildView,CParentView). 如果你用自己的消息句柄手工代替了向导所做的,确信你的改动是正确的, 一个错误的参数或者加了一个"const"将会改变消息映象而不会被正确调用. 3)我猜想你一定是用类向导生成器来建立你的CChildView,而且在基类的选择中一定是选了CView,自己动手在消息映象中把它修改过来. (65)树形控件为何闪烁? 我从CTreeCtrl中继承了一个类,以缩进的格式显示节点,现在我碰上些问题,当树被重画两次之后(一次为缺省,另一次为对齐文本时)点选节点树就会闪烁. 1)试一下LockWindowUpdate()API函数。 2)试一下加入TVS——HASBUTTONS标志, ModifyStyleEx(TVS_HASBUTTONS, 0); ....//drawing ModifyStyleEx(0, TVS_HASBUTTONS); 如果它不再闪烁,那么在将其定义为自画属性,用PreCreateWindow()中加入CS——OWNDC。 (66)怎样才能关闭树形控件中的滚动条? 我想关闭树形控件的滚动条,但它依然显示出来,怎样才能隐藏它? 1)在建立时加入TVS_NOSCROLL,注意此时你就不可以用键盘来实现翻页,这种类型需要comct32.dll4.71版本以上才可以,并且要在commctrl.h中定义如#define TVS_NOSCROLL 0x2000. 2)值得这样试一下 ModifyStyle(WS_VSCROLL,0),将这段代码放在建立之后,显示之前。 (67)如何建立一个带滚动条的窗口? 我想建立一个带滚动的子窗口,但我没有用向导生成器。 如果你让你的窗口有一个滚动条,你必须首先初始化。如下 SCROLLINFO si; si.cbSize = sizeof( SCROLLINFO ); si.fMask = SIF_PAGE | SIF_RANGE; si.nMin = 0; si.nMax = 100; si.nPage = 10; SetScrollInfo( SB_HORZ, &si ); si.nMin = 0; si.nMax = 50; si.nPage = 5; SetScrollInfo( SB_VERT, &si ); 如果程序运行时你的窗口内容已经改变或者窗口被改变大小而重画时,你必须重新设置滚动条。在MFC中包含类CScrollView,它已内建滚动条。 (68)如何实现对话框的拖放? 我有一个对话框程序,想让它实现拖放。但无论用OnDrag或OnDrop等等,所有的的消息都发送给CView类而不是CDialog类,为什么? 你应该使用COleDropTarget类,试一下这些: class CMyOleDropTarget: public COleDropTarget { protected: virtual DROPEFFECT OnDragEnter( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point ) { TRACE( "DRAG Enter\n" ); return DROPEFFECT_MOVE; }; virtual DROPEFFECT OnDragOver( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point ) { TRACE( "DRAG Over\n" ); return DROPEFFECT_MOVE; }; }; CMyOleDropTarget DropTarget; BOOL CDlgDlg::OnInitDialog() { CDialog::OnInitDialog(); DropTarget.Register( this ); 不要忘记调用AfxOleInit() BOOL CDlgApp::InitInstance() { AfxEnableControlContainer(); AfxOleInit(); } (69)TrackMouseEvent()怎么了 我使用TrackMouseEvent()函数来跟踪鼠标是否已经离开我的窗口,但在MFC中,如果我使用 ::TrackMouseEvent()系统告诉我没有定义,为什么? 1).请使用_TrackMouseEvent 2).在commctrl.h显示为_TrackMouseEvent(),请注意下划线. 3).可能TrackMouseEvent()不支持Win98(在NT中工作得非常好),建议你结合WM_MOUSEMOVE消息和 SetCapture()函数,当鼠标移出窗口时你依然可以控制. (70)奇怪的组合框控件 我有一个对话框程序,里面只有几个下拉式给合框.但当鼠标箭头移动到组合框的上下按钮时,会变成"6"或"9",一会儿又恢复到原状,这是为什么? 1)也许是你的操作系统有问题,不防重新起动一次也许就行了(概率非常小8%-())你也可以试一下系统清除工具,如果这事情经常发生,可能你真的需要重装一下95或NT,这也是个好的建议,每隔半年左右可以重装一下系统. 2).我猜想可能是comctl32.dll文件被破坏了. 3).这个问题的原因很有可能是系统的资源不够,你可以试着关闭一些程序、减少屏幕的分辨率来增加一些系统资源。 (71)关于使用MS SANS SERIF字体 我看过好多关于创建对话框、组合框等等使用MS SANS SERIF的例子,自己也做过多次。如: m_font.CreatePointFont (80, _T("MS Sans Serif")); 或 m_font.Create (-8, ....., _T("MS Sans Serif")); 那么想问一下:1)该字体是否在所有的版本中都能实现(包括国际版本) 2)在控制面板上有没有更好的字体代替“SYSTEM”字体?如果有人这样做了,那又是怎样设置字体大小等相关设置的?我希望有一个彻底的方法来选择组合框等的字体。 1)有件事情我做过,在我所有的程序界面中都改变了字体.消息框来显示用户选择的字体. 菜单,工具条以及其他控件的字体都随用户意愿改变.但在对话框中最好还是用对话框编辑器, 其基本字体都是MS SANS SERIF,所以我也以这种字体来作为所有的用户界面. 以下为我所做的代码: // here's the font I use: SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); m_fntUI.CreateFontIndirect(&ncm.lfMessageFont); // here's the code to change the font for a wnd and all it's children, and resize the controls appropriately void ChangeDialogFont(CWnd* pWnd, CFont* pFont, int nFlag) { CRect windowRect; // grab old and new text metrics TEXTMETRIC tmOld, tmNew; CDC * pDC = pWnd->GetDC(); CFont * pSavedFont = pDC->SelectObject(pWnd->GetFont()); pDC->GetTextMetrics(&tmOld); pDC->SelectObject(pFont); pDC->GetTextMetrics(&tmNew); pDC->SelectObject(pSavedFont); pWnd->ReleaseDC(pDC); long oldHeight = tmOld.tmHeight+tmOld.tmExternalLeading; long newHeight = tmNew.tmHeight+tmNew.tmExternalLeading; if (nFlag != CDF_NONE) { // calculate new dialog window rectangle CRect clientRect, newClientRect, newWindowRect; pWnd->GetWindowRect(windowRect); pWnd->GetClientRect(clientRect); long xDiff = windowRect.Width() - clientRect.Width(); long yDiff = windowRect.Height() - clientRect.Height(); newClientRect.left = newClientRect.top = 0; newClientRect.right = clientRect.right * tmNew.tmAveCharWidth / tmOld.tmAveCharWidth; newClientRect.bottom = clientRect.bottom * newHeight / oldHeight; if (nFlag == CDF_TOPLEFT) // resize with origin at top/left of window { newWindowRect.left = windowRect.left; newWindowRect.top = windowRect.top; newWindowRect.right = windowRect.left + newClientRect.right + xDiff; newWindowRect.bottom = windowRect.top + newClientRect.bottom + yDiff; } else if (nFlag == CDF_CENTER) // resize with origin at center of window { newWindowRect.left = windowRect.left - (newClientRect.right - clientRect.right)/2; newWindowRect.top = windowRect.top - (newClientRect.bottom - clientRect.bottom)/2; newWindowRect.right = newWindowRect.left + newClientRect.right + xDiff; newWindowRect.bottom = newWindowRect.top + newClientRect.bottom + yDiff; } pWnd->MoveWindow(newWindowRect); } pWnd->SetFont(pFont); // iterate through and move all child windows and change their font. CWnd* pChildWnd = pWnd->GetWindow(GW_CHILD); while (pChildWnd) { pChildWnd->SetFont(pFont); pChildWnd->GetWindowRect(windowRect); CString strClass; ::GetClassName(pChildWnd->m_hWnd, strClass.GetBufferSetLength(32), 31); strClass.MakeUpper(); if(strClass==_T("COMBOBOX")) { CRect rect; pChildWnd->SendMessage(CB_GETDROPPEDCONTROLRECT,0,(LPARAM) &rect); windowRect.right = rect.right; windowRect.bottom = rect.bottom; } pWnd->ScreenToClient(windowRect); windowRect.left = windowRect.left * tmNew.tmAveCharWidth / tmOld.tmAveCharWidth; windowRect.right = windowRect.right * tmNew.tmAveCharWidth / tmOld.tmAveCharWidth; windowRect.top = windowRect.top * newHeight / oldHeight; windowRect.bottom = windowRect.bottom * newHeight / oldHeight; pChildWnd->MoveWindow(windowRect); pChildWnd = pChildWnd->GetWindow(GW_HWNDNEXT); } } (72)为什么DLL在字符串表中找不到字符串 我用向导生成器中的"Use MFC in a Shared DLL"选项建立一个DLL,在字符串表资源中加一个字符串,当我使用csMyString.LoadString( IDS_MY_STRING ) csMyString 是空的,为什么会这样? 1)MFC是由AfxGetResourceHandle调用资源的.所以,如果你想在你的DLL中读出资源应该使用 AfxSetResourceHandle.你也可以在LoadLibrary的返回值中得到它,如果不想调用该DLL时也可以使用DLLMain函数的hInstance参数. 2)试一下在你函数打头处使用AFX_MANAGE_STATE(AfxGetStaticModuleState()) (事实上每个被外部DLL调用的每一个函数都会使用它) 3)我记得先前的列表讲过这个问题,试一下以下两种方法: 如果你是用LoadLibrary()来调用DLL的,它会返回一个句柄,你可以在 AfxSetResourceHandle()中使用它.如: hinstnew = Loadbrary(...); ... hinstOld = AfxGetResourceHandle(); AfxSetResourceHandle(hinstnew); LoadString(IDS_MY_STRING); AfxSetResourceHandle(hinstOld); // remember to set this back, // or your night won't be nice. 如果你不是用LoadLibrary来调用DLL又该怎样办呢?你可以使用 GetModule("You DLL Name")来取得用户句柄,剩下的就好办了. (73)关于复选框的文本颜色 有谁知道怎样才能改变复选框中的文本选项的颜色? 1)你有没有试过在控件中使用OnCtlColor,它将在重画任何控件之前被调用,所以你可以有机会来改变文本选项的颜色。 2)为什么你一定要用PreDrawItem()?你是想在里面做一些特定的代码?我认为DrawItem() 也能处理。在调用重画函数之前取得索引号并改变颜色。 (74)系列化与版本的问题 我需要使用系列化来读取我的文件,为了保证文件能在各个版本中都能实现,我作了尽可能的努力,为什么会不成功. 答:下面的代码是我过去使用过的,希望能对你有所帮助 // Use this macro to fix the versioning problem in the MFC // Place it at the beginning of your CMyObject::Serialize implementation - // it will guarantee that the correct version of the class is written to // and read from the archive // // Usage: SERIALIZE_VERSION(CMyObject) #define SERIALIZE_VERSION(this_class) ar.SerializeClass(this_class::GetRuntimeClass()); // For classes which cannot use IMPLEMENT_SERIAL (such as abstract // base classes). This guarantees the object can have [Read/Write][Class/Object] // called on it by placing a schema number in it. It also puts it in the // list of known class names (AFX_CLASSINIT). // Note: this is almost the same as IMPLEMENT_SERIAL_ABC // in "MFC Internals", but this version uses AFX_CLASSINIT, // with the result that it works! #define DECLARE_DYNAMIC_SERIAL(class_name) DECLARE_SERIAL(class_name) #define IMPLEMENT_DYNAMIC_SERIAL(class_name, base_class_name, wSchema) _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, NULL) static const AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) { pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); return ar; } 或者也可以这样实现: CMySerialRootDerivedClass::Serialize(CArchive& ar) { //CMySerialRoot::Serialize(ar); // <- do not call this here if (ar.IsStoring()) { ... store derived stuff here } else { int nVersion = ar.GetObjectSchema(); switch(nVersion) { case 1: ... load derived version 1 stuff here break; case 2: ... load derived version 2 stuff here break; default: // report unknown version of // this object break; } } // serialize the base class version information // -> then serialize the base class ar.SerializeClass(RUNTIME_CLASS(CMySerialRoot)); CMySerialRoot::Serialize( ar ); } (75)在一个控件内检测并使用ON_COMMAND消息 有一个控件(继承CWnd)在CRormView.可不可以将它的ID在ON_COMMAND消息中发出,如果用pCtrl->OnCommand(ID_VIEW_ZOOMIN,..), 编译器会报告参数不匹配,该怎么办? 1)为什么不用pCtrl->Post/SendMessage (WM_COMMAND, ID_VIEW_ZOOMIN) 2)通过重载CYourFormView::OnCmdMsg就可以.如: BOOL CYourFormView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { return pCtrl->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo) || CFormView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } 3)使用WM_COMMAND消息,看一下关于WM_COMMAND和CWnd::PostMessage()的帮助. DWORD wParam; HIWORD(wParam) = wNotifyCode; // notification code LOWORD(wParam) = ID_VIEW_ZOOMIN; pCtrl->PostMessage(WM_COMMAND,(WPARAM)wParam, pCtrl->m_hWnd); 4)能够这样做,但不是象你们做法,你们必须得到控件的句柄或CWnd指针然后在句柄中使用::SendMessage() or ::PostMessage();在CWnd中使用 CWnd::SendMessage() or CWnd::PostMessage() 试一下这个. CMyCtrl *pCtrl; /* call GetDlgItem() from an instance of your form view */ pCtrl = ( CMyCtrl * )GetDlgItem( IDC_MYCONTROL ); if( pCtrl != NULL && ::IsWindow( pCtrl->GetSafeHwnd( ) ) pCtrl->SendMessage( WM_COMMAND, /*wParam*/, /*lParam*/ ); // see WM_COMMAND description on help/MSDN for a detailed explanation of // {W|L}PARAM (76)为何MDI程序中有子窗口打开时主应用程序不能关. 我在MDI程序中增加了一个CRichEditView文档模板,在子窗口视中我增加了下面一些代码. StartReport (void) { CReportFrame *rpt; CReportDoc *rptDoc; // First get the right document template POSITION pPos = theApp.GetFirstDocTemplatePosition(); theApp.GetNextDocTemplate ( pPos ); theApp.GetNextDocTemplate ( pPos ); CDocTemplate *pTemplate = theApp.GetNextDocTemplate ( pPos ); // Verify validity ASSERT(pTemplate != NULL); ASSERT_KINDOF(CDocTemplate, pTemplate); // Create the frame rptDoc = new CReportDoc; rpt = (CReportFrame*)pTemplate->CreateNewFrame ( rptDoc, NULL ); pTemplate->InitialUpdateFrame (rpt, rptDoc); // Get access to the display area CReportView *rptView = static_cast(rpt->GetActiveView()); CRichEditCtrl &rptCtrl = rptView->GetRichEditCtrl(); } CReportFrame继承于CMDIChildWnd CReportDoc继承于CRichEditDoc CReportView继承于from CRichEditView 如果我关闭程序前不关闭新建的视,调试器将认为程序依然在运行(程序管理器中依然存在) 我需要用调试菜单中的stop debugging来关闭程序;如果我手工关闭该视,程序将会正常关闭.如果有什么不同的话,在手工关闭新的视之前程序会询问是否保存. 那么怎样我才能关闭程序呢? 1)我也碰上过对话框,窗口不能自动关闭的情况,这主要是因为继承的对象不正确所造成的。通常应该在主程序中设置AfxGetMainWnd(). 你的程序让我搞糊涂了,一连使用了多个GetNextDocTemplate(pPos),在这些文档指针是NULL时通常会引起一些循环.在你的文档模板中是否已经精心算好了数目?这样可能会产生些bugs 我建议找出当前的文档模板用CDocTemplate::CreateNewDocument()来代替你的"new CReportDoc" 2) 记住一个公共规则,关闭程序前要关闭所有的视. (77)滚动视中LPtoDP失败 在WINDOWS98/95中,当你给光标指针位置大于32767或者小于-21768函数CDC::LPtoDP 将失败,程序工作在NT上但在95/98中用滚动视工作时却出现了问题. LPtoDP是在下面函数中被调用的: SetScrollSizes(MM_HIMETRIC, sizeTotal); 函数是在CScrollView中调用的.我使用的是HIMETRIC映射方式,在我想将A4扩大150%时这个问题就会出现。怎样才能解决这个问题? 1)在95中确实存在这样的问题,95中的GDI不是32位的.当我们开发一个程序有编辑矢量图象时手动而不是由LPtoDP()函数来完成转换.(在NT中也存在同样的问题) 2)简言之,CScrollView或CWnd之所以32位参数会失败是因为95/98并不是真正的32 位操作系统,里面仍然包含16位代码.比如Scrollbars还是只接受16位的值来调整范围. NT是一个真正的32位操作系统,就没有这些困惑. 在95中不得不面对类似的滚动大文档的问题时,我们只能另外写些代码来实现滚动的实际位置,当它超出-32K或+32K时,你也必须在你的应用中做些映射. 作为一个有关的注意点(可能你已经碰上过这个问题)如果在MFC处理滚动消息时,如: void CSomeWnd::OnVScroll (UINT nSBCode,UINT nPos, CScrollBar *pscrollBar) 中的 nPos参数只有16位长.克服这个限制可以使用SCROOLINFO结构运行::GetScrollInfo.SCROLLINFO 结构中的nTrackPos是一个真正的32位。 (78)ODBC许可问题 我有个程序想通过ODBC来使用一个MS Access数据库,但是却碰上了错误,系统显示 "Records can't be read; no read permission on table SESSION".(记录不能读, 表单不允许读) )首先我假设access数据库有一个缺省的用户为"admin",可以这样完成"ODBC;UID=admin". 然后,当你继承CRecordset类时你就不必带参数打开,但下面的方法可能更好些: Open(CRecordset::dynaset, NULL,CRecordset::useBookmarks | CRecordset::skipDeletedRecords) 当然你必须提供DSN表示连接名字的数据库在ODBC之下. (79)怪异的字体 我们有一个MFC应用程序,主窗口是在客户区域内画些文本和图形. 我们希望能在客户区域内显示文本,在不需要时则擦除.所以我们先得到一个DC(CClientDC), 然后设置字体和文本颜色就开始写文本,在擦除时,我们用同样的字体,同样的地方用背景色重写文本. 这种方法绝大部分情况下都工作得很好,但偶尔文本并不能完全擦除,有些像素点依然可见. 好象在写文本时比通常略微胖了些,就象用粗体一样.字体是在写文本时使用的,以后也没有进行过任何的调整. 下面是我们使用的写与擦除的函数. void CSign::DrawSignName(CDC* pDC) { int OldBkMode; // select the appropriate font CFont* pOldFont = (CFont*) pDC->SelectObject(pSignNameFont); OldBkMode = pDC->SetBkMode(TRANSPARENT); // determine the colour of the text if (IsSignNameVisible()) pDC->SetTextColor(aColours[SIGN_NAME_COLOUR]); else pDC->SetTextColor(aColours[DEVICE_INVISIBLE_COLOUR]); // draw the text pDC->TextOut(m_pointNameCoords.x, m_pointNameCoords.y, m_strName); // restore the previously used font and background mode pDC->SelectObject(pOldFont); pDC->SetBkMode(OldBkMode); } // DrawSignName 函数是在消息句柄中调用的,而参数中的DC是这样建立的: CClientDC dc(AfxGetMainWnd()). 字体是在程序初始化时建立的: pSignNameFont = new CFont; pSignNameFont->CreateFont(10,5,0,0,150, FALSE,FALSE,0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Helvetica"); 是不是一次使用两个指向同一个客户窗口的DC有问题?程序中的DrawSignName()被多个消息句柄调用。 1)加入以下代码: { m_strName.Empty(); Invalidate(); UpdateWindow(); more stuff;;; } 上面代码会产生一个WM_ERASEBKGND消息,将会用背景色填满窗口,然后再调用OnDraw(),这时只要将字符串置空即可。 2)我不清楚为什么程序不能正常工作,但我有个主意(它会更快些)可以在显示文本的地方用一个背景色的矩形画一下即可。我也不清楚为什么你们为什么要用透明文本,它将会给图形系统带来大量的工作。字体之所以有这种情况,是否你们安装了文本输出的图形保真软件?它会给你们带来困惑的。 3)你只想简单的用一个指针来保存一个指向DC的GDI对象,并试图再次调用它时期望它能指向正确的对象。恕我直言,这不是正确的方法(我不知道是否这是显示不正常的唯一原因)将它转化为一个Windows句柄才是正确的: // // Creating: // pSignNameFont = new CFont; pSignNameFont->CreateFont(10,5,0,0,150, FALSE,FALSE,0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Helvetica"); // Now converting into a windows handle m_hSNFont = (HFONT) pSignNameFont->GetSafeHandle(); 直接保存一个对象是不安全的。 (80)自画列表框样例 很久以前,有人散发关于自画列表框控件代码,而自画列表框外观就象一个标准列表框,在那时我就有个想法想把程序员开发的所有自画控件的代码惧收集起来,这样程序员们就可以使用现存的代码了。 我想问一下在1996年关于MFC站点那儿有才能关于列表框或其它控件的代码? 1)自画列表框代码如下,看看是不是你所想要的。 Header file class CCustomListBox : public CListBox { public: // Operations DECLARE_DYNCREATE(CCustomListBox) int AddLBItem(LPSTR); void HandleSelectionState(LPDRAWITEMSTRUCT lpdis); void HandleFocusState(LPDRAWITEMSTRUCT lpdis); virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS); }; cpp file IMPLEMENT_DYNCREATE(CCustomListBox, CListBox) int CCustomListBox::AddLBItem(LPSTR itemStr) { AddString((LPCSTR)itemStr); return 0; } void CCustomListBox::DrawItem(LPDRAWITEMSTRUCT lpDIS) { CDC* pDC = CDC::FromHandle(lpDIS->hDC); if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))) { pDC->InvertRect(&lpDIS->rcItem); pDC->DrawFocusRect(&lpDIS->rcItem); } if (!(lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & ODA_SELECT)) { pDC->InvertRect(&lpDIS->rcItem); pDC->DrawFocusRect(&lpDIS->rcItem); } } void CCustomListBox::HandleSelectionState(LPDRAWITEMSTRUCT lpdis) { // Ordinarily could check for "if (lpdis->itemState & ODS_SELECTED)" // and do drawing for selected state, "else" draw non-selected state. // But second call to InvertRect restores rectangle to original // state, so will just call function whether selected or unselected. ::InvertRect (lpdis->hDC, (LPRECT)&lpdis->rcItem); } void CCustomListBox::HandleFocusState(LPDRAWITEMSTRUCT lpdis) { // Ordinarily would check for "if (lpdis->itemState & ODS_FOCUS)" // and do drawing for focus state, "else" draw non-focus state. // But second call to DrawFocusRect restores rectangle to original // state, so will just call function whether focus or non-focus. // New to Windows 3.0, this function draws a black dashed-rect // border on the border of the specified rectangle ::DrawFocusRect( lpdis->hDC, (LPRECT) &lpdis->rcItem ); } 2)http://toronto.planeteer.com/~zalmoxe/ (81)CWnd::GetMenu()的问题 我有个程序用下面代码: CWnd *pWnd = CWnd::GeForegroundWindow(); if (pWnd == NULL) return FALSE; CMenu *pMenu = pWnd->GetMenu(); if (pMenu == NULL) return FALSE; for (int i = 0; i < pMenu->GetMenuItemCount; i++) { pMenu->GetMenuItemID(...); pMenu->GetMenuString(...); } 上述代码工作除了在IE窗口外,别的窗口工作都很正常,请问怎样才能在IE窗口中正常使用,如果不是用这种方法,那又该用什么方法? IE有一个定义菜单,是用自定义系列控件中的弹出菜单。所以你就不能再使用枚举这种方法了,试一下处理WM_INITMENUPOPUP或WM_INITMENU。在VC的CD中有类似的例子(关于剪切与复制)你得到消息句柄时就可以列出所有的菜单项。上面的代码之所不工作可能是因为微软的自画菜单项的保存菜单项用了不同的格式,想要明白菜单和画标是否是自画的,你可以用这种方法测试lpmii->fType & MFT_OWNERDRAW.Ipmii是一个菜单结构,返回得到的菜单项信息。lpmii->dwTypeData 返回(菜单)项目的类型,如果dwTypeData返回的值没有什么用的话还有一个机会,lpmii->dwItemData将指向一个(程序)开始时的菜单项中的字符串结构。以上方法比较好,因为现在好多程序都使用自定义菜单。 (82)用MFC制作弹出窗口 我正在试着用MFC来制作弹出窗口,我看过一些关于建立弹出窗口的文章,它们是使用 CWnd对象的。但在文档,视窗结构中是怎样实现的? 你可以建立一个非模态对话框(使用Create函数),你可以在任何建立窗口,子窗口等。如果你一定要在文档、视窗结构中实现,你也可以用CCreateContest类。下面是建立MDI窗口的例子: { LPCTSTR lpszClassName = NULL; CCreateContext cContext; cContext.m_pNewViewClass = RUNTIME_CLASS ( CMyView ) DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX; // TOD Add your specialized code here and/or call the base class if ( CMDIChildWnd::Create(lpszClassName, lpszWindowName, dwStyle, pParentWnd->rectDefault, pParentWnd, &cContext) ) { InitialUpdateFrame ( NULL, TRUE ); CScrollView *pView = ( CScrollView* ) GetActiveView(); if ( pView ) pView->ResizeParentToFit ( FALSE ); return TRUE; } else return FALSE; } CCreateContext有一个成员为m_pCurrentDoc,你可以用它来将一个文档分配到相应的窗口上. (83)怎样取消一个弹出式菜单 我有一个应用程序不显示窗口(建立窗口时使用了SW_HIDE参数),它只在任务条显示一个图标,我是这样做的: NOTIFYICONDATA tnid; tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = m_hWnd; tnid.uID = 1; tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnid.uCallbackMessage = MYWM_NOTIFYICON; tnid.hIcon = AfxGetApp()->LoadIcon( IDI_ICON1 ); lstrcpyn(tnid.szTip, "Giroimag Image Mail Exchange", strlen("Giroimag Image Mail Exchange")+1); Shell_NotifyIcon(NIM_ADD, &tnid); 当我点击任务条时,程序会显示一个弹出菜单: CMenu m_Menu; m_Menu.CreatePopupMenu(); m_Menu.AppendMenu( MF_STRING, IDM_ABOUT, "Op&1" ); m_Menu.AppendMenu( MF_SEPARATOR, 0 ); m_Menu.AppendMenu( MF_STRING, IDM_CONFIG, "Op&2" ); m_Menu.AppendMenu( MF_STRING, IDM_STATUS, ""Op&3" ); m_Menu.AppendMenu( MF_SEPARATOR, 0 ); m_Menu.AppendMenu( MF_STRING, IDM_SEND, "Op&4" ); m_Menu.AppendMenu( MF_STRING, IDM_RECEIVE, "Op&5" ); m_Menu.AppendMenu( MF_SEPARATOR, 0 ); m_Menu.AppendMenu( MF_STRING, IDM_CLOSE, "Op&6" ); POINT p; GetCursorPos( & p ); m_Menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, this ); 到这为止,程序运行很正常,问题在于如果我不选择任何菜单该怎样取消它?我以为按ESC或者在菜单外面点击就可以取消,但事实并不是这样。我也试过用WIN32API中的TrackPopupMenuEx函数但没有用,到底我该怎么做? 1)最简单的方法在消息映象中加"Cancel Menu"命令即可。 2)尽管你的主窗口不可见,但在你可以在调用m_Menu.TrackPopupMenu();时将其置为最前。 3)在你弹出菜单之前,设置你的窗口为最前窗口,调用下面的代码,问题就会迎刃而解。 POINT p; GetCursorPos( & p ); // Increase the thread priority by invoking SetForegroundWindow. SetForegroundWindow(); m_Menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, this ); 4)调用TrackPopupMenu()之前,你必须先调用SetForegroundWindow( m_hWnd ),然后调用PostMessage( m_hWnd, WM_NULL, 0, 0 ): POINT point; GetCursorPos( &point ); SetForegroundWindow( m_hWnd ); TrackPopupMenu( hPopup, TPM_RIGHTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, 0, m_hWnd, 0 ); PostMessage( m_hWnd, WM_NULL, 0, 0 ); |