本文实例总结了MFC程序设计常用技巧。分享给大家供大家参考。具体如下:
1.属性页的添加:
创建对话框的类,该类要从CpropertyPage继承;然后在要添加该对话框为属性页的类(头文件)里创建CpropertySheet类的一个对象m_tabsheet和新创建的对话框类的对象m_skatch;最后,在.cpp文件里的OnInitDialog()之类的函数里实现如下代码:
m_tabsheet.Create(this, WS_CHILD | WS_VISIBLE, 0);
//使选项卡的按钮在下面
if(m_tabsheet.m_hWnd)
m_tabsheet.ShowWindow(SW_MAXIMIZE);//显示选项卡
//加入标签,标签名由各个子对话框的标题栏决定
m_tabsheet.AddPage(&m_skatch);
//用Create来创建一个属性页
m_tabsheet.Create(this, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT);
RECT rect;
m_tabsheet.GetWindowRect(&rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
//调整属性页的大小和位置
m_tabsheet.SetWindowPos(NULL, 225, 225, width-82, height,SWP_NOACTIVATE);
属性页的添加完成。如果要添加多个属性页,则只需要增加多个对象,如下:
m_tabsheet.AddPage(&m_skatch1);
m_tabsheet.AddPage(&m_skatch2);
. . . . . .
2.List Control中标题栏(Column)的添加:
创建一个List Control,其ID为IDC_LIST,在其Styles属性项下的View项里选择Report、Align项里选择Top、Sort项里选择None.
然后在该List所在对话框的类(头文件)里创建ClistCtrl的一个对象m_list然后在.cpp文件的OnInitDialog()之类的函数里实现如下代码:
CString strname[3];
strname[0]="Screen Name";
strname[1]="Form ID";
strname[2]="Category Path";
for(int i=0;i<3;i++)
{
m_List.InsertColumn(i,strname[i],LVCFMT_LEFT,130);
}
在这之前也要将List Control的ID与ClistCtrl的对象m_list在DoDataExchange(CDataExchange* pDX)函数里绑定,如下:
DDX_Control(pDX, IDC_LIST, m_List);
3.ToolBar和StatusBar中控件的添加:
方法⑴.只能在ToolBar里创建控件:首先,在ToolBar中创建一个Button,其ID为ID_TOOL_COMBO(我们要将创建的控件放在该Button的位置上).
其次,新创建一个类CMainToolBar,要从CToolBar继承(创建过程大概如下:选择工程/增加到工程/新的类;也可以选择工程的根,然后点击右键,选择新的类;或者CTL+W,选择增加类/新的类 --- 然后在class type里选择Generic Class,在Name栏里输入新类的名字,Base class里输入CToolBar),创建成功后在该类里创建要增加的控件的对象,如:
CComboBox m_wndMyCombo;
CStatic m_wndCategory, m_wndCategoryPath;
CButton m_wndOpenButton;
Cedit m_wndEdit;
然后在构造函数里初始化如:
m_wndMyCombo.m_hWnd = NULL;
m_wndCategory.m_hWnd = NULL;
m_wndCategoryPath.m_hWnd = NULL;
m_wndOpenButton.m_hWnd = NULL;
m_wndEdit.m_hWnd = NULL;
接着在CMainframe的头文件里创建CMainToolBar的一个对象m_wndToolBar,最后在.cpp文件的OnCreate函数的最后实现如下:
int index = 0;
CRect rect; // 可定义在头文件当中
// ComboBox
{
//找到指定的工具项
while(m_wndToolBar.GetItemID(index)!=ID_TOOL_COMBO)
index++;
//设置指定工具项的宽度并获取新的区域 120是宽度
m_wndToolBar.SetButtonInfo(index, ID_TOOL_COMBO, TBBS_SEPARATOR, 120);
m_wndToolBar.GetItemRect(index, &rect);
//设置位置
rect.top+=1;
rect.bottom += 200;
// 创建并显示控件
if(!m_wndToolBar.m_wndMyCombo.Create(WS_CHILD|WS_VISIBLE| CBS_AUTOHSCROLL|
CBS_DROPDOWNLIST | CBS_HASSTRINGS , rect, &m_wndToolBar, ID_TOOL_COMBO))
{
TRACE0("Failed to create combo-box/n");
return FALSE;
}
m_wndToolBar.m_wndMyCombo.ShowWindow(SW_SHOW);
//填充内容
m_wndToolBar.m_wndMyCombo.AddString("25%");
m_wndToolBar.m_wndMyCombo.AddString("50%");
m_wndToolBar.m_wndMyCombo.AddString("75%");
//选择默认项
m_wndToolBar.m_wndMyCombo.SetCurSel(0);
//获取到内容并MSGBOX显示出来
CString strContent;
m_wndToolBar.m_wndMyCombo.GetWindowText(strContent);
index = 0;
}
其他控件都类似创建(只需要注意一下各自的Create函数的参数即可)。方法⑵.这种方法创建不太容易控制:直接在CMainframe的头文件中创建要增加的控件的对象,如CButton的对象m_wndAboutButton,然后创建CToolBar或者CstatusBar的对象,如:CstatusBar的对象_wndStatusBar;再增加几个函数如下:
Protected:
virtual void RecalcLayout(BOOL bNotify = TRUE);
afx_msg void CMainFrame::OnViewStatusBar();
接着在.cpp文件中将StatusBar的ID和OnViewStatusBar 函数绑定在一起,如下所示:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
// {{AFX_MSG_MAP(CMainFrame)
ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
ON_WM_CREATE()
// }}AFX_MSG_MAP
END_MESSAGE_MAP()
然后Create函数的最后(返回值之前)实现如下代码:CRect rc;
VERIFY(m_wndAboutButton.Create(_T("MyAbout"),
WS_VISIBLE,rc,this,ID_APP_ABOUT));
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
再在RecalcLayout函数里实现:
CRect rc;
if (m_wndStatusBar.m_hWnd)
{
m_wndStatusBar.GetWindowRect(&rc);
ScreenToClient(&rc);
rc.right -= 50;
m_wndStatusBar.SetWindowPos(NULL,rc.left,rc.top,rc.Width(),rc.Height(),
SWP_NOZORDER);
rc.left = rc.right;
rc.right += 50;
m_wndAboutButton.SetWindowPos(NULL,rc.left,rc.top,rc.Width(),rc.Height(),
SWP_NOZORDER);
}
最后在OnViewStatusBar()里实现:
BOOL bShow = m_wndStatusBar.GetStyle() & WS_VISIBLE;
m_wndAboutButton.SetWindowPos(NULL, 0, 0, 0, 0,
SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|
(bShow ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
ToolBar中的创建与此相同,只需更改一下句柄即可。4.通过Control创建的控件,对其属性的动态控制:
在对话框类的头文件里创建所要改变属性的控件的对象,如要改变一个Button(其ID为IDC_MyButton)的属性,则需创建Cbutton的对象m_button。然后在.cpp中的DoDataExchange函数里将Button的ID和创建的对象绑定在一起:
//{{AFX_DATA_MAP(CPrintDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
DDX_Control(pDX, IDC_MyButton, m_button);
//}}AFX_DATA_MAP
然后可以在该函数的最后进行初始化:
m_button.EnableWindow(FALSE);
到这里已经实现了改变属性。如果要动态改变其属性,可如下实现(通过两个Button的点击改变起属性):
// RadioAll Button的点击响应函数
void CPrintDlg::OnRadioAll()
{
// TODO: Add your control notification handler code here
m_button.EnableWindow(TRUE);
}
// RadioSelect Button的点击响应函数
void CPrintDlg::OnRadioSelect()
{
// TODO: Add your control notification handler code here
m_button.EnableWindow(FALSE);
}
也可以通过一个Check Button的点击来改变,在其点击响应函数里实现:
m_button.EnableWindow(!m_button.IsWindowEnabled());
其余控件属性的改变都如此。5.窗口的分割与停靠:
一、新建一个类CMySplitter,基类为CSplitterWnd
二、重载该类的OnMouseMove函数:
void CMySplitter::OnMouseMove(UINT nFlags, CPoint point)
{
// 限制切分条的运动范围。
if(point.x<228||point.x>600)
{
CWnd::OnMouseMove(nFlags, point);
}
else
{
CSplitterWnd::OnMouseMove(nFlags, point);
}
}
三、 然后就可以跟一般的窗口分割那样去做了,if(point.x<228||point.x>600)这里的范围可以随你去设置了 ^_^,够简单吧。四、切分窗口
在MaiFram.h建立切分条对象:
protected:
CMySplitter m_wndSplitter; //切分窗口对象
//在MaiFram.cpp中实现窗口切分:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,CCreateContext* pContext)
{
// 创建拆分器窗口
if (!m_wndSplitter.CreateStatic(this, 1, 2))
return FALSE;
if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CLeftView),CSize(228,100), pContext) ||!m_wndSplitter.CreateView(0,1, RUNTIME_CLASS(CDataEditView), CSize(100, 100), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
return TRUE;
}
6. ①怎样在程序开始的时候让它最大化?
②vc++做出来的exe文件在窗体的右上方是没有最大化和最小化按钮的,怎样实现这一功能?
③如何在显示窗口时,使最大化按钮变灰?
①在App类里的C…App::InitInstance()中把m_pMainWnd->ShowWindow(SW_SHOW)改成m_pMainWnd->ShowWindow(SW_MAXIMIZE);
②在CreateWidnow时用WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX 风格.
③ 第一种方法:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
// disable the maxmini box
cs.style &= ~WS_MAXIMIZEBOX;
return TRUE;
}
第二种方法:
CMenu *pMenu=AfxGetApp()->m_pMainWnd->GetSystemMenu(FALSE);
int x=pMenu->GetMenuItemCount( );
UINT pID=pMenu->GetMenuItemID(x-1);
pMenu->EnableMenuItem(pID, MF_DISABLED);
第三种方法:
ModifyStyle(WS_MAXIMIZEBOX, 0);
这个函数也可以是最大化按钮失效!
并且可以在程序中动态的改变窗口的风格7. 更改属性页标题
void CProSheet::SetPageTitle(int nPage, int nImage, CString strTitle)
{
TC_ITEM item;
//item.mask = TCIF_TEXT|TCIF_IMAGE; //设置图标+文字
item.mask = TCIF_IMAGE; //只设置图标
item.iImage = nImage;
// item.pszText = strTitle.GetBuffer(0); //设置文字
GetTabControl ()->SetItem (nPage, &item);
//要设置文字时就将上面2行有注释符的代码前的注释符去掉
}
8. 创建动态菜单
void CMainFrame::OnSelectState(NMTOOLBAR* pnmtb, LRESULT *plr)
{
CMenu menu;
if(!menu.CreateMenu())
return;
menu.AppendMenu(MF_STRING,0,"开始");
menu.AppendMenu(MF_STRING,0,"结束");
CRect rc;
m_wndToolBar.SendMessage(TB_GETRECT, pnmtb->iItem, (LPARAM)&rc);
m_wndToolBar.ClientToScreen(&rc);
menu.TrackMenu( TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
rc.left, rc.bottom, this, &rc);
//menu.DestroyMenu();
menu.detach();
}
9.关于打印
1.要打印哪个视就
((CMainFrame*)AfxGetMainWnd())->m_wndSplitter.SetActivePane(...)
//要打印的那个视对应的Pane
2.有一个单文档工程,文档窗口被切分:左视图由CTreeView 的派生类管理,右视图由CListView 的派生类CMyListView(其为风格为LVS_REPORT)管理,我想为右视图添加打印和打印预览,我在MyListView.cpp中添加了
ON_COMMAND(ID_FILE_PRINT,CListView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CListView::OnFilePrintPreview)还有
BOOL CMyListView::OnPreparePrinting(CPrintInfo* pInfo)
{
// TODO: call DoPreparePrinting to invoke the Print dialog box
// return CListView::OnPreparePrinting(pInfo);
pInfo->SetMaxPage(2);
BOOL bret=DoPreparePrinting(pInfo);
pInfo->m_nNumPreviewPages=2;
return bret;
}
3. 下面是从MSDN中摘出来的一段,是用来改变消息路由的。用了这段代码之后,CView中的消息(菜单,控件,子窗口)将先被CMyShape类来处理。不知道你要的是不是这样的效果。
// This example illustrates extending the framework's standard command
// route from the view to objects managed by the view. This example
// is from an object-oriented drawing application, similar to the
// DRAWCLI sample application, which draws and edits "shapes".
BOOL CMyView::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo)
{
// Extend the framework's command route from the view to
// the application-specific CMyShape that is currently selected
// in the view. m_pActiveShape is NULL if no shape object
// is currently selected in the view.
if ((m_pActiveShape != NULL)
&& m_pActiveShape->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// If the object(s) in the extended command route don't handle
// the command, then let the base class OnCmdMsg handle it.
return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
// The command handler for ID_SHAPE_COLOR (menu command to change
// the color of the currently selected shape) was added to
// the message map of CMyShape (note, not CMyView) using ClassWizard.
// The menu item will be automatically enabled or disabled, depending
// on whether a CMyShape is currently selected in the view, that is,
// depending on whether CMyView::m_pActiveView is NULL. It is not
// necessary to implement an ON_UPDATE_COMMAND_UI handler to enable
// or disable the menu item.
(CMyShape, CCmdTarget)
//{{AFX_MSG_MAP(CMyShape)
ON_COMMAND(ID_SHAPE_COLOR, OnShapeColor)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
如果你只是想调用OnFilePrint( )函数,可以试一试下面的代码,就和调用其它类中的函数一样。
CMDIFrameWnd *pFrame =
(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
// Get the active MDI child window.
CMDIChildWnd *pChild =
(CMDIChildWnd *) pFrame->GetActiveFrame();
// or CMDIChildWnd *pChild = pFrame->MDIGetActive();
// Get the active view attached to the active MDI child
// window.
CMyView *pView = (CMyView *) pChild->GetActiveView();
pView->OnFilePrint( );
4.
void CMyReportView::OnFileOpen()
{
char Filter[] = "Crystal Report files(*.rpt)|*.rpt|All files(*.*)|*.*||";
CRect rect;
CFileDialog OpenDlg(TRUE,0,0,OFN_HIDEREADONLY|OFN_FILEMUSTEXIST,(LPCTSTR)Filter,NULL);
if(OpenDlg.DoModal()!=IDOK) ///显示文件对话框
return;
CString m_fName=OpenDlg.GetPathName(); ///取得文件名
if(m_CrystalReport)
m_CrystalReport.DestroyWindow();
GetClientRect(rect);
///////////////////创建控件///////////////
if (!m_CrystalReport.Create(AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),WS_CHILD|WS_VISIBLE,rect,this,IDC_CRYSTALREPORT1))
{
AfxMessageBox("控件创建失败!");
return ;
}
m_CrystalReport.SetWindowParentHandle((long)(this->m_hWnd));///设置父窗口
m_CrystalReport.SetWindowBorderStyle(0); ///设置为没有边框
m_CrystalReport.SetWindowLeft(0); ///左空间
m_CrystalReport.SetWindowTop(0); ///顶部空间
m_CrystalReport.SetWindowControls(FALSE); ///不显示工具条
m_CrystalReport.SetReportFileName(m_fName); ///设置报表文件
m_CrystalReport.SetWindowWidth(rect.Width()); ///设置窗口宽度
m_CrystalReport.SetWindowHeight(rect.Height()); ///设置窗口高度
m_CrystalReport.SetFormulas(0, "Company=/"VC知识库/""); ///将报表中的Company变量的值设置为VC知识库
m_CrystalReport.SetDestination(0); ///设置输出对象是屏幕
m_CrystalReport.PrintReport(); ///显示报表
}
void CMyReportView::OnFilePrint()
{
if(m_CrystalReport && m_CrystalReport.GetReportFileName() != "")
{
m_CrystalReport.SetDestination(1); ///设置输出对象是打印机
m_CrystalReport.PrintReport(); ///打印
}
10. Scroll创建一个基于CScrollview的SDI Project(在第6步中选CScrollview)
若你已创建了,这步可以省略。
然后:
改为如
void CTestView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal;
// TODO: calculate the total size of this view
sizeTotal.cx = 1024; //改这两个
sizeTotal.cy = 768; //
SetScrollSizes(MM_TEXT, sizeTotal);
}
11. 修改主窗口风格AppWizard生成的应用程序框架的主窗口具有缺省的窗口风格,比如在窗口标题条中自动添加文档名、窗口是叠加型的、可改变窗口大小等。要修改窗口的缺省风格,需要重载CWnd::PreCreateWindow(CREATESTRUCT& cs)函数,并在其中修改CREATESTRUCT型参数cs。
CWnd::PreCreateWindow 函数先于窗口创建函数执行。如果该函数被重载,则窗口创建函数将使用CWnd::PreCreateWindow 函数返回的CREATESTRUCT cs参数所定义的窗口风格来创建窗口;否则使用预定义的窗口风格。
CREATESTRUCT结构定义了创建函数创建窗口所用的初始参数,其定义如下:
typedef struct tagCREATESTRUCT {
LPVOID lpCreateParams; // 创建窗口的基本参数
HANDLE hInstance; // 拥有将创建的窗口的模块实例句柄
HMENU hMenu; // 新窗口的菜单句柄
HWND hwndParent; // 新窗口的父窗口句柄
int cy; // 新窗口的高度
int cx; // 新窗口的宽度
int y; // 新窗口的左上角Y坐标
int x; // 新窗口的左上角X坐标
LONG style; // 新窗口的风格
LPCSTR lpszName; // 新窗口的名称
LPCSTR lpszClass; // 新窗口的窗口类名
DWORD dwExStyle; // 新窗口的扩展参数
} CREATESTRUCT;
CREATESTRUCT结构的style域定义了窗口的风格。比如,缺省的MDI主窗口的风格中就包括FWS_ADDTOTITLE(在标题条中显示当前的工作文档名)、FWS_PREFIXTITLE(把文档名放在程序标题的前面)、WS_THICKFRAME(窗口具有可缩放的边框)等风格。由于多种风格参数由逻辑或(“|”)组合在一起的,因此添加某种风格,就只需用“|”把对应的参数加到CREATESTRUCT结构的style域中;删除已有的风格,则需用“&”连接CREATESTRUCT结构的style域与该风格的逻辑非值。CREATESTRUCT结构的x、y、cx、cy域分别定义了窗口的初始位置和大小,因此,在CWnd::PreCreateWindow 函数中给它们赋值,将能定义窗口的初始显示位置和大小。
下例中的代码将主框窗口的大小将固定为1/4屏幕,标题条中仅显示窗口名,不显示文档名。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
// 修改主窗风格
cs.style &= ~FWS_ADDTOTITLE; //去除标题条中的文档名
cs.style &= ~WS_THICKFRAME; //去除可改变大小的边框
cs.style |= WS_DLGFRAME; //增加不能改变大小的边框
// 确定主窗的大小和初始位置
int cxScreen = ::GetSystemMetrics(SM_CXSCREEN);//获得屏幕宽
int cyScreen = ::GetSystemMetrics(SM_CYSCREEN); //获得屏幕高
cs.x = 0; // 主窗位于左上角
cs.y = 0;
cs.cx = cxScreen/2; // 主窗宽为1/2屏幕宽
cs.cy = cxScreen/2; // 主窗高为1/2屏幕高
return CMDIFrameWnd::PreCreateWindow(cs);
}
12. 控制滚动条
BOOL CDiagramShowView::PreTranslateMessage(MSG* pMsg)
{
CFileTreeDoc* pDoc = (CFileTreeDoc*)GetDocument();
CPoint point = GetScrollPosition();
if(pMsg->message == WM_KEYDOWN)
{
switch(pMsg->wParam)
{
case VK_LEFT:
if( point.x > 10)
{
EndPoint.x = EndPoint.x - 10;
EndPoint.y = EndPoint.y;
}
else
{
EndPoint.x = 0;
EndPoint.y = EndPoint.y;
}
ScrollToPosition(EndPoint);
InvalidateRect(NULL,TRUE);
break;
case VK_RIGHT:
if( point.x < pDoc->intDiagramColumnCount * pDoc->intColumnWidth - 10 )
{
EndPoint.x = EndPoint.x + 10;
EndPoint.y = EndPoint.y;
}
else
{
EndPoint.y = pDoc->intDiagramColumnCount * pDoc->intColumnWidth;
EndPoint.x = EndPoint.x;
}
ScrollToPosition(EndPoint);
InvalidateRect(NULL,TRUE);
break;
case VK_UP:
if( point.y > 10)
{
EndPoint.y = EndPoint.y - 10;
EndPoint.x = EndPoint.x;
}
else
{
EndPoint.y = 0;
EndPoint.x = EndPoint.x;
}
ScrollToPosition(EndPoint);
InvalidateRect(NULL,TRUE);
break;
case VK_DOWN:
if( point.y < pDoc->intDiagramRowCount * pDoc->intRowHeight - 10 )
{
EndPoint.y = EndPoint.y + 10;
EndPoint.x = EndPoint.x;
}
else
{
EndPoint.y = pDoc->intDiagramRowCount * pDoc->intRowHeight;
EndPoint.x = EndPoint.x;
}
ScrollToPosition(EndPoint);
InvalidateRect(NULL,TRUE);
break;
default:
break;
}
}
return FALSE;
}
// 通过正负号判断是向上还是向下滚动
if(zDelta==120)
向上滚动
if(zDelta==-120)
向下滚动
BOOL CDiagramShowView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
CFileTreeDoc* pDoc = (CFileTreeDoc*)GetDocument();
CPoint point = GetScrollPosition();
if(zDelta==120)
{
if( point.y >= 20 )
{
EndPoint.x = point.x;
EndPoint.y = point.y;
EndPoint.x = EndPoint.x;
EndPoint.y = EndPoint.y - 20;
}
else
{
EndPoint.x = EndPoint.x;
EndPoint.y = 0;
}
}
if(zDelta==-120)
{
if( point.y <= pDoc->intDiagramRowCount * pDoc->intRowHeight - 20 )
{
EndPoint.x = point.x;
EndPoint.y = point.y;
EndPoint.x = EndPoint.x;
EndPoint.y = EndPoint.y + 20;
}
else
{
EndPoint.x = EndPoint.x;
EndPoint.y = EndPoint.y;
}
}
ScrollToPosition(EndPoint);
InvalidateRect(NULL,TRUE);
return CScrollView::OnMouseWheel(nFlags, zDelta, pt);
}
13. 属性页处理通知消息CPropertyPageImpl有一个消息映射处理WM_NOTIFY。如果通知代码是PSN_*的值,OnNotify()就会调用相应的通知处理函数。这使用了编译阶段虚函数机制,从而使得派生类可以很容易的重载这些处理函数。
由于WTL 3和WTL 7设计的改变,从而存在两套不同的通知处理机制。在WTL 3中通知处理函数返回的值与PSN_*消息的返回值不同,例如,WTL 3是这样处理PSN_WIZFINISH的:
case PSN_WIZFINISH:
lResult = !pT->OnWizardFinish();
break;
OnWizardFinish()期望返回TRUE结束向导,FALSE阻止关闭向导。这个方法很简陋,但是IE5的通用控件对PSN_WIZFINISH处理的返回值添加了新解释,他返回需要获得焦点的窗口的句柄。WTL 3的程序将不能使用这个特性,因为它对所有非0的返回值都做相同的处理。在WTL 7中,OnNotify() 没有改变 PSN_* 消息的返回值,处理函数返回任何文档中规定的合法数值和正确的行为。当然,为了向前兼容,WTL 3 仍然使用当前默认的工作方式,要使用WTL 7的消息处理方式,你必须在中including atldlgs.h一行之前添加一行定义:
#define _WTL_NEW_PAGE_NOTIFY_HANDLERS
编写新的代码没有理由不使用WTL 7的消息处理函数,所以这里就不介绍WTL 3的消息处理方式。CPropertyPageImpl 为所有消息提供了默认的通知消息处理函数,你可以重载与你的程序有关的消息处理函数完成特殊的操作。默认的消息处理函数和相应的行为如下:
int OnSetActive() - 允许页面成为激活状态
BOOL OnKillActive() - 允许页面成为非激活状态
int OnApply() - 返回 PSNRET_NOERROR 表示应用操作成功完成
void OnReset() - 无相应的动作
BOOL OnQueryCancel() - 允许取消操作
int OnWizardBack() - 返回到前一个页面
int OnWizardNext() - 进行到下一个页面
INT_PTR OnWizardFinish() - 允许向导结束
void OnHelp() - 无相应的动作
BOOL OnGetObject(LPNMOBJECTNOTIFY lpObjectNotify) - 无相应的动作
int OnTranslateAccelerator(LPMSG lpMsg) - 返回 PSNRET_NOERROR 表示消息没有被处理
HWND OnQueryInitialFocus(HWND hWndFocus) - 返回 NULL 表示将按Tab Order顺序的第一个控件设为焦点状态
14.使工具条上的按钮点击一次为按下,再点击才弹起
bCheck=m_RtfEditToolBar.GetToolBarCtrl().IsButtonChecked(ID_TB_BOLD);
m_RtfEditToolBar.GetToolBarCtrl().CheckButton(ID_TB_BOLD, !bCheck);
15. VC中基于 Windows 的精确定时
在工业生产控制系统中,有许多需要定时完成的操作,如定时显示当前时间,定时刷新屏幕上的进度条,上位 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的实时控制系统和数据采集系统中,就更需要精确定时操作。
众所周知,Windows 是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的。 这样就带来了一些问题,如一旦计算机的CPU被某个进程占用,或系统资源紧张时,发送到消息队列 中的消息就暂时被挂起,得不到实时处理。因此,不能简单地通过Windows消息引发一个对定时要求 严格的事件。另外,由于在Windows中已经封装了计算机底层硬件的访问,所以,要想通过直接利用 访问硬件来完成精确定时,也比较困难。所以在实际应用时,应针对具体定时精度的要求,采取相适 应的定时方法。VC中提供了很多关于时间操作的函数,利用它们控制程序能够精确地完成定时和计时操作。本文详细介绍了 VC中基于Windows的精确定时的七种方式.
方式一:VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时 间隔,如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数 OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。这种定时方法非常 简单,可以实现一定的定时功能,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,最小 计时精度仅为30ms,CPU占用低,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响 应,往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的Timer1。
方式二:VC中使用sleep()函数实现延时,它的单位是ms,如延时2秒,用sleep(2000)。精度非常 低,最小计时精度仅为30ms,用sleep函数的不利处在于延时期间不能处理其他的消息,如果时间太 长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer2。
方式三:利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3和Timer3_1。以下是实现2秒的延时代码:
COleDateTime start_time = COleDateTime::GetCurrentTime();
COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time;
while(end_time.GetTotalSeconds()< 2) //实现延时2秒
{
MSG msg;
GetMessage(&msg,NULL,0,0);
TranslateMessage(&msg);
DispatchMessage(&msg);
//以上四行是实现在延时或定时期间能处理其他的消息,
//虽然这样可以降低CPU的占有率,
//但降低了延时或定时精度,实际应用中可以去掉。
end_time = COleDateTime::GetCurrentTime()-start_time;
}//这样在延时的时候我们也能够处理其他的消息。
方式四:在精度要求较高的情况下,VC中可以利用GetTickCount()函数,该函数的返回值是 DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高,在较 短的定时中其计时误差为15ms,在较长的定时中其计时误差较低,如果定时时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer4和Timer4_1。下列代码可以实现50ms的精确定时:
DWORD dwStart = GetTickCount();
DWORD dwEnd = dwStart;
do
{
dwEnd = GetTickCount()-dwStart;
}while(dwEnd <50);
为使GetTickCount()函数在延时或定时期间能处理其他的消息,可以把代码改为:
DWORD dwStart = GetTickCount();
DWORD dwEnd = dwStart;
do
{
MSG msg;
GetMessage(&msg,NULL,0,0);
TranslateMessage(&msg);
DispatchMessage(&msg);
dwEnd = GetTickCount()-dwStart;
}while(dwEnd <50);
虽然这样可以降低CPU的占有率,并在延时或定时期间也能处理其他的消息,但降低了延时或定时精度。方式五:与GetTickCount()函数类似的多媒体定时器函数DWORD timeGetTime(void),该函数定时精 度为ms级,返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底 层API持,利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一 个事件、函数或过程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须将 Winmm.lib 和 Mmsystem.h 添加到工程中,否则在编译时提示DWORD timeGetTime(void)函数未定义。由于使用该 函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5和Timer5_1。
方式六:使用多媒体定时器timeSetEvent()函数,该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的Timer6和Timer6_1。函数的原型如下:
MMRESULT timeSetEvent( UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent )
该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回NULL。函数的参数说明如下:
uDelay:以毫秒指定事件的周期。
Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc:指向一个回调函数。
DwUser:存放用户提供的回调数据。
FuEvent:指定定时器事件类型:
TIME_ONESHOT:uDelay毫秒后只产生一次事件
TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。
具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数 中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。
方式七:对于精确度要求更高的定时操作,则应该使用QueryPerformanceFrequency()和 QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7、Timer7_1、Timer7_2、Timer7_3。
QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);
数据类型ARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构, 其具体用法根据编译器是否支持64位而定。该类型的定义如下:
typedef union _LARGE_INTEGER
{
struct
{
DWORD LowPart ;// 4字节整型数
LONG HighPart;// 4字节整型数
};
LONGLONG QuadPart ;// 8字节整型数
}LARGE_INTEGER ;
在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率, 然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差及时钟频率,计算出事件经 历的精确时间。下列代码实现1ms的精确定时:
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 获得初始值
do
{
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
}while(dfTim<0.001);
其定时误差不超过1微秒,精度与CPU等机器配置有关。 下面的程序用来测试函数Sleep(100)的精确持续时间:
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 获得初始值
Sleep(100);
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
由于Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。下列代码实现1微秒的精确定时:
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 获得初始值
do
{
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
}while(dfTim<0.000001);
其定时误差一般不超过0.5微秒,精度与CPU等机器配置有关。16. 为对话框中的控件增加提示的简单方法
我学VC从VC知识库中得到不少好处,相来都是一些热心朋友们的帮助,在此表示感谢!本工程可分五步:
1、建一个基于对话框的程序TipTest,在CTipTestDlg中增加成员变量:
CToolTipCtrl m_tip[2],CWnd *m_pSub[2];
2、在CTipTestDlg::OnInitDialog()函数中增加如下代码:
pSub[0] = GetDlgItem(IDC_RADIO1); //得到单选按钮的指针
pSub[1] = GetDlgItem(IDC_BUTTON1);
m_tip[0].Create(pSub[0],TTS_ALWAYSTIP); //创建CToolTipCtrl
m_tip[0].AddTool(pSub[0]); //将CToolTipCtrl与相应的控件对应起来
m_tip[1].Create(pSub[1],TTS_ALWAYSTIP);
m_tip[1].AddTool(pSub[1]);
m_tip[0].SetTipTextColor(RGB(0,0,255)); //设定文字的颜色
m_tip[0].SetDelayTime(150); //设定提示文字在控件上停留的时间
3、重载CTipTestDlg::PreTranslateMessage(MSG* pMsg)函数,增加如下代码:
if(m_tip[0].m_hWnd!=NULL)
m_tip[0].RelayEvent(pMsg); //如果m_tip[0]句柄不为空,就从主窗口中捕获消息,如WM_MOUSEMOVE,WM_LBUTTONDOWN等消息
if(m_tip[1].m_hWnd!=NULL)
m_tip[1].RelayEvent(pMsg);
4、捕获主窗口的WM_MOUSEMOVE消息,在CTipTestDlg::OnMouseMove(UINT nFlags, CPoint point)函数中增加如下代码:
m_tip[0].UpdateTipText("VC知识库欢迎你!",pSub[0]);//鼠标在相应的控件上移动时显示提示文字
m_tip[1].UpdateTipText("http://vckbase.com",pSub[1]);
17.删除文件夹
// 删除文件夹及其所有内容
void CBaseDoc::RemoveFolder(const CString &strPathName)
{
CString path = strPathName;
if (path.Right(1) != _T("//"))
path += _T("//");
path += _T("*.*");
CFileFind ff;
BOOL res = ff.FindFile(path);
while (res)
{
res = ff.FindNextFile();
// 是文件时直接删除
if (!ff.IsDots() && !ff.IsDirectory())
DeleteFile(ff.GetFilePath());
else if (ff.IsDots())
continue;
else if (ff.IsDirectory())
{
path = ff.GetFilePath();
// 是目录时继续递归,删除该目录下的文件
RemoveFolder(path);
::RemoveDirectory(path);
}
}
}
18.消息映射有对话框A,B
从A中发消息给B然后B处理。
准备工作,先定义消息,如下
#define WM_B_NOTIFY WM_USER + 300
首先,必须将B的对话框句柄传送给A,暂时叫m_hWndB;
在A的发送消息的地方这样写:
::SendMessage( m_hWndB,WM_B_NOTIFY,TRUE,NULL );
这样A中的处理就完了,下面说B 中的
首先定义消息处理函数,如下
void B::ModiNotify( WPARAM wParam, LPARAM lParam )
{
MessageBox("小样,我就不信,搞不定你!");
}
然后加消息隐射,如下:
BEGIN_MESSAGE_MAP(CB, CDialog)
//{{AFX_MSG_MAP(CRPServerDlg)
ON_MESSAGE( WM_B_NOTIFY,ModiNotify )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
19.给从CWnd派生的窗口添加滚动条
ModifyStyle(0,WS_VSCROLL);
20. SetWindowPos函数功能:该函数改变一个子窗口,弹出式窗口式顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。
函数原型:BOOL SetWindowPos(HWN hWnd,HWND hWndlnsertAfter,int X,int Y,int cx,int cy,UNIT.Flags);
参数:
hWnd:窗口句柄。
hWndlnsertAfter:在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄,或下列值之一:
HWND_BOTTOM:将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶级位置,并且被置在其他窗口的底部。
HWND_DOTTOPMOST:将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后)。如果窗口己经是非顶层窗口则该标志不起作用。
HWND_TOP:将窗口置于Z序的顶部。
HWND_TOPMOST:将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。
查g看该参数的使用方法,请看说明部分。
x:以客户坐标指定窗口新位置的左边界。
Y:以客户坐标指定窗口新位置的顶边界。
cx:以像素指定窗口的新的宽度。
cy:以像素指定窗口的新的高度。
uFlags:窗口尺寸和定位的标志。该参数可以是下列值的组合:
SWP_ASNCWINDOWPOS:如果调用进程不拥有窗口,系统会向拥有窗口的线程发出需求。这就防止调用线程在其他线程处理需求的时候发生死锁。
SWP_DEFERERASE:防止产生WM_SYNCPAINT消息。
SWP_DRAWFRAME:在窗口周围画一个边框(定义在窗口类描述中)。
SWP_FRAMECHANGED:给窗口发送WM_NCCALCSIZE消息,即使窗口尺寸没有改变也会发送该消息。如果未指定这个标志,只有在改变了窗口尺寸时才发送WM_NCCALCSIZE。
SWP_HIDEWINDOW;隐藏窗口。
SWP_NOACTIVATE:不激活窗口。如果未设置标志,则窗口被激活,并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置)。
SWP_NOCOPYBITS:清除客户区的所有内容。如果未设置该标志,客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区。
SWP_NOMOVE:维持当前位置(忽略X和Y参数)。
SWP_NOOWNERZORDER:不改变z序中的所有者窗口的位置。
SWP_NOREDRAW:不重画改变的内容。如果设置了这个标志,则不发生任何重画动作。适用于客户区和非客户区(包括标题栏和滚动条)和任何由于窗回移动而露出的父窗口的所有部分。如果设置了这个标志,应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分。
SWP_NOREPOSITION;与SWP_NOOWNERZORDER标志相同。
SWP_NOSENDCHANGING:防止窗口接收WM_WINDOWPOSCHANGING消息。
SWP_NOSIZE:维持当前尺寸(忽略cx和Cy参数)。
SWP_NOZORDER:维持当前Z序(忽略hWndlnsertAfter参数)。
SWP_SHOWWINDOW:显示窗口。
返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误消息,请调用GetLastError函数。
备注:如果设置了SWP_SHOWWINDOW和SWP_HIDEWINDOW标志,则窗口不能被移动和改变大小。如果使用SetWindowLoog改变了窗口的某些数据,则必须调用函数SetWindowPos来作真正的改变。使用下列的组合标志:SWP_NOMOVEISWP_NOSIZEISWP_FRAMECHANGED。
有两种方法将窗口设为最顶层窗口:一种是将参数hWndlnsertAfter设置为HWND_TOPMOST并确保没有设置SWP_NOZORDER标志;另一种是设置窗口在Z序中的位置以使其在其他存在的窗口之上。当一个窗口被置为最顶层窗口时,属于它的所有窗口均为最顶层窗口,而它的所有者的z序并不改变。
如果HWND_TOPMOST和HWND_NOTOPMOST标志均未指定,即应用程序要求窗口在激活的同时改变其在Z序中的位置时,在参数hWndinsertAfter中指定的值只有在下列条件中才使用:
在hWndlnsertAfter参数中没有设定HWND_NOTOPMOST和HWND_TOPMOST标志。
由hWnd参数标识的窗口不是激活窗口。
如果未将一个非激活窗口设定到z序的顶端,应用程序不能激活该窗口。应用程序可以无任何限制地改变被激活窗口在Z序中的位置,或激活一个窗口并将其移到最高级窗口的顶部或非最高级窗口的顶部。
如果一个顶层窗口被重定位到z序的底部(HWND_BOTTOM)或在任何非最高序的窗口之后,该窗口就不再是最顶层窗口。当一个最顶层窗口被置为非最顶级,则它的所有者窗口和所属者窗口均为非最顶层窗口。
一个非最顶端窗口可以拥有一个最顶端窗口,但反之则不可以。任何属于顶层窗口的窗口(例如一个对话框)本身就被置为顶层窗口,以确保所有被属窗口都在它们的所有者之上。
如果应用程序不在前台,但应该位于前台,就应调用SetForegroundWindow函数来设置。
Windows CE:如果这是一个可见的顶层窗口,并且未指定SWP_NOACTIVATE标志,则这个函数将激活窗口、如果这是当前的激活窗口,并且指定了SWP_NOACTIVATE或SWP_HIDEWINDOW标志,则激活另外一个可见的顶层窗口。
当在这个函数中的nFlags参数里指定了SWP_FRAMECHANGED标志时,WindowsCE重画窗口的整个非客户区,这可能会改变客户区的大小。这也是重新计算客户区的唯一途径,也是通过调用SetwindowLong函数改变窗口风格后通常使用的方法。
SetWindowPos将使WM_WINDOWPOSCHANGED消息向窗口发送,在这个消息中传递的标志与传递给函数的相同。这个函数不传递其他消息。
Windows CE 1.0不支持在hWndlnsertAber参数中的HWND_TOPMOST和HWND_NOTOPMOST常量。
Windows CE1.0不支持在fuFags参数中的SWP_DRAWFRAME和SWP_NOCOPYBITS标志。
速查:Windows NT:3.1以上版本;Windows:95以上版本;Windows CE:1.0以上版本;头文件:winuser.h库文件:eser32lib。
21. 介绍函数过程中一种任意键退出同时能处理消息的实现方法
1. 设置定时器,用于使::GetMessage(...)函数总能快速取到消息.
2. 在函数处理中加入:
函数每执行完一步后执行下面的代码.
if (::GetMessage(&msg, 0, 0, 0))
{
if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) return ;
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else ::PostQuitMessage(0);
22. 如何隐藏工具栏添加如下两个函数
隐藏:
void CMainFrame::OnHide()
{
if(m_wndToolBar.IsWindowVisible())
m_wndToolBar.ModifyStyle(WS_VISIBLE,0);
SendMessage(WM_SIZE);
}
显示:
void CMainFrame::OnShow()
{
if(!m_wndToolBar.IsWindowVisible())
m_wndToolBar.ModifyStyle(0,WS_VISIBLE);
SendMessage(WM_SIZE);
}
23. 如何动态获取工具条指针并给工具条加标题?问题提出:
工具条也是窗口,是窗口就有标题,如何给工具条加标题?
程序实现:
不想动态改变工具条的标题就在CMainFrame::OnCreate()中:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
......
m_wndToolBar.SetWindowText(_T("Standdard"));
return 0;
}
若想动态改变工具条的标题,如下:
声明一个菜单,并响应事件,如响应:OnMyToolBar()函数
void CMainFrame::OnMyToolBar()
{
// TODO: Add your command handler code here
CToolBar *pToolBar = (CToolBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);
pToolBar->SetWindowText (_T("Standdard"));
}
不要在TooBar悬浮时做OnMyToolBar()会出错的.
顺便提一下如何获得状态条的指针:
CStatusBar * pStatusBar =(CStatusBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
24. 在状态条中显示鼠标的设备坐标与逻辑坐标显示器的设备坐标系的原点在客户区的左上角,x轴向右增长,y轴向下增长。我们要设置的逻辑坐标系的原点则在客户区的中心,x轴向右增长,y轴向上增长,如一个笛卡尔坐标系一般。
为CChildView添加一个成员函数void OnPrepareDC(CDC * pDC, CPrintInfo * pInfo = NULL);
void OnPrepareDC(CDC * pDC, CPrintInfo * pInfo){
CRect rect;
// 设置映射模式为LOMETRIC (0.1mm),右上为增长方向
pDC->SetMapMode (MM_LOMETRIC);
// 将坐标原点定在客户区的中心
GetClientRect(rect);
pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2);
}
为CChildView响应鼠标移动消息,并在状态条中显示鼠标的坐标值。m_ptMouse数据成员是原打算做十字交叉线用的,在此使用没有实际意义。
void CChildView::OnMouseMove(UINT nFlags, CPoint point){
CClientDC dc(this);
CString str;
OnPrepareDC(&dc);
//要访问类CMainFrame,需要将mainfrm.h文件引入
CMainFrame * pFrame = (CMainFrame *) AfxGetApp()->m_pMainWnd;
//要访问CMainFrame的数据成员m_wndStatusBar,需要手工修改mainfrm.h,public这个数据成员
CStatusBar * pStatus = (CStatusBar *) &pFrame->m_wndStatusBar;
m_ptMouse = point;
str.Format ("设备坐标 X=%i pixel, Y=%i pixel", m_ptMouse.x, m_ptMouse.y);
pStatus->SetPaneText(1, str);
dc.DPtoLP(&m_ptMouse);
str.Format ("逻辑坐标 X=%i * 0.1mm, Y=%i * 0.1mm", m_ptMouse.x, m_ptMouse.y);
pStatus->SetPaneText(2, str);
}
25. 如何用VC++ 动态修改应用程序菜单问题提出
本文将介绍一些使用CMenu的方法,如查找指定菜单,在指定选项前添加菜单项.....
解决方法
使用CWnd::GetMenu( )访问主菜单,GetMenu( )返回指向CMenu对象的指针,它有一些成员函数,允许我们修改一个菜单。
1) 如何实现找到一个菜单项:
步骤如下:
{
//动态修改菜单:
// Get the Main Menu
CMenu* pMainMenu = AfxGetMainWnd()->GetMenu();
CMenu* pSubMenu = NULL;
int i;
for (i=0; i<(int)pMainMenu->GetMenuItemCount(); i++)
{
pSubMenu = pMainMenu->GetSubMenu(i);
if (pSubMenu && pSubMenu->GetMenuItemID(0) == ID_FILE_NEW)
break;
}
CString s;
s.Format("%d",i);//菜单项的位数.
AfxMessageBox(s);
ASSERT(pSubMenu);
}
2) 动态编辑菜单:
步骤如下(可以用上例的pSubMenu,要加的菜单你自己定义.):
1) 添加一个称为Wzd2,命令ID为IDC_NAME_NEW1的菜单命令到该菜单中,可以用:
pSubMenu->AppendMenu(0,IDC_NAME_NEW1,"New&1");
2) 在New1前插入New2,可以用:
pSubMenu->InsertMenu(IDC_NAME_NEW1,MF_BYCOMMAND,IDC_NAME_NEW2, "New&2");
3) 把New1改变成New3,可以用:
pSubMenu->ModifyMenu(IDC_NAME_NEW1,MF_BYCOMMAND,IDC_NAME_NEW3, "New&3");
4) 删除该菜单中第二项,可以用:
pSubMenu->RemoveMenu(1,MF_BYPOSITION);
26. VC++中的3D按钮的编程
运行AppWizard生成一个基于对话框的test工程,在对话框中加入一个CButton控件。在CButton控件的General属性页将控件的ID改为IDC_3DTEXTBTN,Caption改为“谁与争疯”,在控件Styles属性页选中OwnerDraw,其余设置保持默认。
用classwizard创建一个新类:C3dTextButton,基类为CButton。为C3dTextButton类添加一个protected的函数void Draw(CDC* pDC, const CRect& rect, UINT state)。如下所示编写代码:
void C3dTextButton::Draw(CDC *pDC, const CRect &rect, UINT state)
{
CString text; GetWindowText(text);
int l=text.GetLength();
CRect rectClient=rect;
//获得控件的字体
CFont* pFont=GetFont();
//确定所选字体有效高度和宽度
LOGFONT logfont;
pFont->GetObject(sizeof(LOGFONT),&logfont);
if(logfont.lfHeight==0)logfont.lfHeight=20;
logfont.lfWidth=0;//宽度设为0,宽度值由高度确定
logfont.lfWeight=1000;
logfont.lfEscapement=logfont.lfOrientation=0;
CFont tryfont; VERIFY(tryfont.CreateFontIndirect(&logfont));
CFont* pFontOld=pDC->SelectObject(&tryfont);
//根据控件大小,调整字体的高度,使文本与控件协调
CSize textSizeClient=pDC->GetTextExtent(text,l);
if(rectClient.Width()*textSizeClient.cy>rectClient.Height()*textSizeClient.cx)
{
logfont.lfHeight=::MulDiv(logfont.lfHeight,rectClient.Height(),textSizeClient.cy);
}
else{
logfont.lfHeight = ::MulDiv(logfont.lfHeight,rectClient.Width(),textSizeClient.cx);
}
//创建并选择协调后的字体
CFont font; font.CreateFontIndirect(&logfont);
pDC->SelectObject(&font);
textSizeClient=pDC->GetTextExtent(text,l);
//确定文本与控件边界的距离minx,miny
int minx=rectClient.left+(rectClient.Width()-textSizeClient.cx)/2;
int miny=rectClient.top+(rectClient.Height()-textSizeClient.cy)/2;
int oldBkMode=pDC->SetBkMode(TRANSPARENT);
COLORREF textcol=::GetSysColor(COLOR_BTNTEXT);
COLORREF oldTextColor=pDC->SetTextColor(textcol);
int cx = minx;
int cy = miny;
int s=(state&ODS_SELECTED)?-1:+1;
cx+= 3; cy+= 3;
//实现3D效果
pDC->SetTextColor(::GetSysColor(COLOR_3DDKSHADOW));
pDC->TextOut(cx-s*2,cy+s*2,text);
pDC->TextOut(cx+s*2,cy-s*2,text);
pDC->TextOut(cx+s*2,cy+s*2,text);
pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
pDC->TextOut(cx+s*1,cy-s*2,text);
pDC->TextOut(cx-s*2,cy+s*1,text);
pDC->TextOut(cx-s*2,cy-s*2,text);
pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
pDC->TextOut(cx-s*1,cy+s*1,text);
pDC->TextOut(cx+s*1,cy-s*1,text);
pDC->TextOut(cx+s*1,cy+s*1,text);
pDC->SetTextColor(::GetSysColor(COLOR_3DLIGHT));
pDC->TextOut(cx,cy-s*1,text);
pDC->TextOut(cx-s*1,cy,text);
pDC->TextOut(cx-s*1,cy-s*1,text);
pDC->SetTextColor(textcol);
//输出标题
pDC->TextOut(cx,cy,text);
//恢复设备描述表
pDC->SetTextColor(oldTextColor);
pDC->SetBkMode(oldBkMode);
pDC->SelectObject(pFontOld);
}
用classwizard重载C3dTextButton类的DrawItem函数。编写代码如下所示:
void C3dTextButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC=CDC::FromHandle(lpDrawItemStruct->hDC);
ASSERT_VALID(pDC);
CRect rectClient=lpDrawItemStruct->rcItem;
Draw(pDC,rectClient,lpDrawItemStruct->itemState);
}
用classwizard为IDC_3DTEXTBTN建立一个C3dTextButton控件变量m_3dTextButton1。把“3dTextButton.h”加入testDlg头文件。编译并测试应用程序。
27. 如何正确的得到ComBox的指针
CComboBox *mComb = (CComboBox*)GetDlgItem(IDC_DuanCB);
CComboBox *mComb = (CComboBox*)::GetDlgItem(m_hWnd,IDC_DuanCB);
28. 如何让对话框中的CEdit控件类接收对话框的消息
////////////////////////////////////////////////
// 如何让对话框中的CEdit控件类接收对话框的消息
////////////////////////////////////////////////
1、在对话框中增加一个ID 为IDC_EDIT1的CEdit1控件
2、通过ClassWizard 生成一个基于CEdit的新类CMyEdit,
CMyEdit m_wndEdit;
3、在对话框OnInitDialog()中,将m_wndEdit子类化,使其能够接受对话框的消息。
m_wndEdit.SubclassDlgItem (IDC_EDIT1,this);
29.利用WM_CTLCOLOR消息实现编辑控制(Edit Control)的文本与背景色的改变
首先要明白:WM_CTLCOLOR是一个由控制(Control)发送给它父窗口的通知消息(Notification message)。实现步骤:
生成一个标准的单文档应用程序框架,假设应用程序的名称为Color。我将利用它的About对话框做示范。在About dialog中添加两个Edit control,设定其ID为IDC_EDIT1与IDC_EDIT2。
第一种方法(对应于IDC_EDIT1): 按照标准的Windows编程,由其父窗口的消息处理函数负责处理WM_CTLCOLOR消息。
1. 在CAboutDlg中添加一个数据成员:HBRUSH m_brMine;
2. 利用向导映射AboutDlg的WM_CTLCOLOR消息,产生函数:HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
pDC是AboutDlg的设备上下文,pWnd是AboutDlg中发送该消息的control指针,nCtlColor市Control的类型编码。对其进行如下修改:
HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if ((pWnd->GetDlgCtrlID() == IDC_EDIT1) && (nCtlColor == CTLCOLOR_EDIT))
{
COLORREF clr = RGB(255,0,0);
pDC->SetTextColor(clr); //设置红色的文本
clr = RGB(0,0,0);
pDC->SetBkColor(clr); //设置黑色的背景
m_brMine = ::CreateSolidBrush(clr);
return m_brMine; //作为约定,返回背景色对应的刷子句柄
}
else
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
return hbr;
}
}
第二种方法(对应于IDC_EDIT2):利用MFC 4.0的新特性: Message reflection。
1.利用向导添加一个新的类:CColorEdit,基类为CEdit;
2.在CColorEdit中添加一个数据成员: HBRUSH m_bkBrush;
3.利用向导映射CColorEdit的=WM_CTLCOLOR消息,产生函数:
HBRUSH CColorEdit::CtlColor(CDC* pDC, UINT nCtlColor);
对其进行如下修改:
HBRUSH CColorEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
COLORREF clr = RGB(0,0,0);
pDC->SetTextColor(clr); //设置黑色的文本
clr = RGB(255,0,0);
pDC->SetBkColor(clr); //设置红色的背景
m_bkBrush = ::CreateSolidBrush(clr);
return m_bkBrush; //作为约定,返回背景色对应的刷子句柄
}
4.利用向导为IDC_EDIT2生成一个数据成员CColorEdit m_coloredit;
5.在定义CAboutDlg的color.cpp文件中加入:#include "coloredit.h"
30. 如何防止密码被非法获取?问题提出
这两天大家比较专注在获取Edit密码框的密码.在盗取时,我们如何防范呢?
解决方法
此方法针对于通过SendMessage向此窗口发送WM_GETTEXT或EM_GETLINE消息来取得密码.跟我来.
程序实现
方法很简单,用CWnd::DefWindowProc函数拦截得到的消息(向Edit发的).
建立名为My的对话框工程.建立一个Edit控件ID=IDC_EDIT1.建一个新类名为CMyProtectEdit,派生于CEdit.
在MyDlg.cpp中声明全局变量:BOOL g_bIdentity;
BOOL g_bIdentity;
在MyProtecEdit.cpp中:
extern BOOL g_bIdentity;
响应CMyProtectEdit的DefWindowProc函数:
LRESULT CMyProtectEdit::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
// 对Edit的内容获取必须通过以下两个消息之一,不对其采用默认的处理:
if(( message == WM_GETTEXT) || ( message == EM_GETLINE))
{ //检查是否为合法
if(!g_bIdentity)
{ //非法获取,显示非法信息
AfxMessageBox(_T("不能让你看我的密码,:( !"));
return 0;
)
g_bIdentity = FALSE;//合法获取
}
return CEdit::DefWindowProc(message, wParam, lParam);
}
然后在MyDlg.cpp中
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CGetPasswordDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
if( pDX->m_bSaveAndValidate)
{
g_bIdentity = TRUE;
}
//}}AFX_DATA_MAP
}
即可.找个程序(盗取)的试试.31. 如何在编辑控件中以追加的方式添入字符?
问题提出:
SetDlgItemText可以向Edit控件中输入字符,发送更新的消息也可是Edit控件显示与其关联的变量的值,但若是向已有的Edit字符后追加字符,该如何做?
程序实现:
建立名为My的对话框工程,添加一个Edit和一个Button控件.Edit的ID=IDC_EDIT1,Button的ID=IDC_BUTTON1.建立和IDC_BUTTON1的响应函数:OnButton1()
void CMyDlg::OnButton1()
{
CString pText="你好";
CEdit *m_Edit=(CEdit *)GetDlgItem(IDC_EDIT1);
int nLen=m_Edit->GetWindowTextLength();
m_Edit->SetFocus();
m_Edit->SetSel(nLen, nLen);
m_Edit->ReplaceSel(pText);
}
在Edit控件中输入字符,想追加时按IDC_BUTTON1按钮.看看效果.
32.属性页标题改名
我用CPropertySheet创建属性页,用的CPropertyPage对象只有一个,也就是每个属性页的内容一样.现在的问题是:这样每个属性页的标题都是一样的,是对话框的标题!怎样动态的改变这个标题,使每个属性页的标签的名称都不同??
CTabCtrl * pCtrl = pSheet->GetTabControl();
TCITEM tc;
tc.mask = TCIF_TEXT;
tc.pszText = "新标题";
pCtrl->SetItem(0,&tc);//0即是你要改的TAb的索引
33. 怎样去掉属性页的Apply与Help按钮?
//去掉Help
m_psh.dwFlags |= PSH_HASHELP ;
m_psh.dwFlags &= ~PSH_HASHELP ;
//除掉应用按钮 m_psh.dwFlags|=PSH_NOAPPLYNOW;
34. 如何给树控件加入工具提示
1.首先给树控件加入TVS_INFOTIP属性风格,如下所示:
if (!m_ctrlTree.Create(WS_CHILD|WS_VISIBLE|
TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT|TVS_SHOWSELALWAYS|TVS_INFOTIP, //加入提示TVS_INFOTIP,jingzhou xu(树控件ID:100)
CRect(0, 0, 0, 0), &m_wndTreeBar, 100))
{
TRACE0("Failed to create instant bar child/n");
return -1;
}
2.其次加入映射消息声明,如下所示:
afx_msg void OnGetInfoTip(NMHDR* pNMHDR,LRESULT* pResult); //树控件上加入提示消息,jingzhou xu
ON_NOTIFY(TVN_GETINFOTIP, 100, OnGetInfoTip) //树控件条目上加入提示,jingzhou xu
3.最后加入呼应涵数处理:
void CCreateTreeDlg::OnGetInfoTip(NMHDR* pNMHDR,
LRESULT* pResult)
{
*pResult = 0;
NMTVGETINFOTIP* pTVTipInfo = (NMTVGETINFOTIP*)pNMHDR;
LPARAM itemData = (DWORD) pTVTipInfo->lParam;
//对应每个条目的数据
HTREEITEM hItem = pTVTipInfo->hItem;
CString tip;
HTREEITEM hRootItem = m_chassisTree.GetRootItem();
if (hRootItem != pTVTipInfo->hItem)
{
tip = "树结点的提示";
}
else
{
tip = "树根上的提示";
}
strcpy(pTVTipInfo->pszText, (LPCTSTR) tip);
}
35. 如何在TreeList中加图标?问题提出:
请问treeview控件和treectrl控件的用法有何不同呢?向如何imagelist控件中加图象呀?
解决方法:
1)
HICON hicon[8];
m_imageList.Create(16,16,0,8,8);
hicon[0]=AfxGetApp()->LoadIcon(IDI_ICON0);
hicon[1]=AfxGetApp()->LoadIcon(IDI_ICON1);
hicon[2]=AfxGetApp()->LoadIcon(IDI_ICON2);
hicon[3]=AfxGetApp()->LoadIcon(IDI_ICON3);
hicon[4]=AfxGetApp()->LoadIcon(IDI_ICON4);
hicon[5]=AfxGetApp()->LoadIcon(IDI_ICON5);
hicon[6]=AfxGetApp()->LoadIcon(IDI_ICON6);
hicon[7]=AfxGetApp()->LoadIcon(IDI_ICON7);
for(int n=0;n<8;n++)
m_imageList.Add(hicon[n]);
CTreeCtrl *pTree=(CTreeCtrl *)GetDlgItem(IDC_TREE);
pTree->SetImageList(&m_imageList,TVSIL_NORMAL);
2)
CImageList cil1;
cil1.Create(32,32,TRUE,2,2);
cil1.Add(pApp->LoadIcon(IDI_DAO1));
cil1.Add(pApp->LoadIcon(IDI_DAO2));
cil1.Add(pApp->LoadIcon(IDI_DAO3));
cil1.Add(pApp->LoadIcon(IDI_DAO4));
cil1.Add(pApp->LoadIcon(IDI_DAO5));
cil1.Add(pApp->LoadIcon(IDI_DAO6));
cil1.Add(pApp->LoadIcon(IDI_DAO7));
cil1.Add(pApp->LoadIcon(IDI_DAO8));
cil1.Add(pApp->LoadIcon(IDI_DAO9));
//设置图象列表
m_list.SetImageList(&cil1,LVSIL_NORMAL);
36. 如何双击列表框项启动一个与文件关联的程序?有人问我如何双击列表框项启动一个程序?其实这个问题很简单,Windows中有一个API函数可以打开任何类型的文件:
ShellExecute(NULL,"open",lpFileName,NULL,NULL,SW_SHOWNORMAL);
参数 lpFileName 是文件的全路径名。用这个变量你可以传递象“C://MyExcelFile.xls”或者“http://www.vckbase.com”启动Excel程序或者浏览器程序。如果你只是想获取与文件关联的程序名,而不是要运行程序,那么调用::FindExecutable就可以了。37. 如何防止在listbox中添加很多数据出现不停的刷新?
问题提出:
在listbox添加很多数据的时候,由于控件不停的刷新,导致出现闪烁,如何解决?
解决方法:
再添加数据以前,禁止控件刷新,数据添加完毕以后,再刷新一次。
程序实现:
(其中:m_ListBox是CListBox的控件类型的变量)
m_ListBox.LockWindowUpdate();//禁止本listbox刷新。
for(int i=0;i<9999;i++)
{
m_ListBox.AddString("test");
}//添加数据。
this->RedrawWindow(NULL,NULL,RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
38. 如何得到CListBox所选择项的String?
问题提出:
如何得到CListBox所选择项的String
解决方法:
用到:CListBox::GetText()
程序实现:
CString scInfo;
pList->GetText( GetCurSel(),scInfo);
39. 用鼠标移动基于对话框的无标题栏程序的简单方法
void CVCTestDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
//一句话解决问题
SendMessage(WM_SYSCOMMAND,0xF012,0);
CDialog::OnLButtonDown(nFlags, point);
}
40. 如何改变框对话或窗体视窗的背景颜色调用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 );
}
41.如何禁止对话框关闭按钮和浮动工具条上的系统菜单
1、禁止对话框中的关闭按钮有二种方法。
第一种方法,用ModiftMenu()涵数来实现:
CMenu* pMenu = this->GetSystemMenu(FALSE);
pMenu->ModifyMenu(SC_CLOSE,MF_BYCOMMAND | MF_GRAYED );
第二种方法,用EnableMenuItem()涵数来实现:
CMenu* pMenu = this->GetSystemMenu(FALSE);
pMenu->EnableMenuItem( SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
2、禁止浮动工具条上的系统菜单。
新建一个CToolBar的派生类CxxToolBar,在新类中的左键双击(CxxToolBar::OnLButtonDblClk(...))
和左键单击(CxxToolBar:: OnLButtonDown(...))涵数中分别加入下面代码既可:
if (IsFloating()) //工具条正在浮动状态中
{
CWnd* pMiniFrame;
CWnd* pDockBar;
pDockBar = GetParent();
pMiniFrame = pDockBar->GetParent();
//去除其上系统菜单
pMiniFrame->ModifyStyle(WS_SYSMENU, NULL);
//重绘工具条
pMiniFrame->ShowWindow(SW_HIDE);
pMiniFrame->ShowWindow(SW_SHOW);
}
3、禁止窗口最大化按钮
在PreCreateWindow()涵数中去掉WS_MAXIMIZEBOX风格显示既可。
BOOL CxxFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style &= ~WS_MAXIMIZEBOX;
return CFrameWnd::PreCreateWindow(cs);
}
42.如何拷贝一个工程的对话框资源到另一个工程中?有两种方法可以实现:
1)你可以直接拷贝resource,用VC++以文本的方式或者直接用文本编辑器打开.rc文件,将有关的片段从
一个工程拷贝到另一个工程.你可以通过查找如下字样的片段(此片段用来定义对话框资源)来拷贝你要
的部分:
IDD_MYDIALOG_ID DIALOG DISCARDABLE 0, 0, 235, 55
这里的IDD_MYDIALOG_ID是你的对话框的ID,将到此片段结尾的部分全拷下来,通常你还要给新的工程
加一个ID(通过DevStudio的工具或者直接修改resource.h文件).
2)可以通过DevStudio的copy/paste功能.首先,在编辑器以"auto"模式打开.rc文件,这时resource
正确的显示出来.然后,选中要拷贝的对话框的ID,在Edit菜单里选Copy或者按住Ctrl+C.然后打开目标
resource文件,在Edit菜单里选Paste或者按住Ctrl+V.
43.如何实现点一下对话框外面的区域,自动隐藏对话框?问题提出:
如果想在点击对话框外面的地方使得对话框关闭,该如何做?
解决方法:
试试下面的代码,原理是在激活对话框时,捕获鼠标的动作,当鼠标点击时判断是否点击在对话框外,是的话就释放对话框.
程序实现:
建立名为My的对话框程序.实现如下步骤:
在MyDlg.h中加入:
class CShowWindow1Dlg : public CDialog
{
// Construction
public:
int m_cx;
int m_cy;
......
};
在MyDlg.cpp中:
//定义消息映象,处理鼠标单击及激活
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
//{{AFX_MSG_MAP(CMyDlg)
ON_WM_LBUTTONDOWN()
ON_WM_ACTIVATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CMyDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect(&rect);
rect.InflateRect(m_cx, m_cy);
//Release dialog if the user click outside it.
if(!rect.PtInRect(point))
{
EndDialog(IDCANCEL);
}
CDialog::OnLButtonDown(nFlags, point);
}
void CMyDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CDialog::OnActivate(nState, pWndOther, bMinimized);
if( nState == WA_ACTIVE || nState == WA_CLICKACTIVE)
SetCapture();
else
ReleaseCapture();
}
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
.....
OSVERSIONINFO info;
memset((char*)&info, 0, sizeof(OSVERSIONINFO));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(GetVersionEx(&info))
{ //we don't run on Win32s, so check only two values
if(info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{ //On windows 95
m_cx = GetSystemMetrics(SM_CXFIXEDFRAME);
m_cy = GetSystemMetrics(SM_CYFIXEDFRAME);
}
else
{ //On NT
m_cx = GetSystemMetrics(SM_CXDLGFRAME);
m_cy = GetSystemMetrics(SM_CYDLGFRAME);
}
}
}
说明:
1)WM_ACTIVATE消息在ClassWizard中没有,按如下步骤添加,右击CMyDlg类,选Add Windows Message Handle,接着在Filter for messages available to中选Window,在New Windows messages/events列表中就会出现WM_ACTIVATE,选中,点击Add Handler
2)SM_CXDLGFRAME,SM_CYDLGFRAME NT中取得有WS_DLGFRAMEstyle风格的窗口的高和宽 95中已经废弃而采用SM_CX_FIXEDFRAME和SM_CYFIXEDFRAME44. 初始化应用程序的大小
如果想使应用程序界面(文档)在开始运行是按你的尺寸展现在屏幕上,
添加代码如下:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
int xsize=::GetSystemMetrics(SM_CXSCREEN);
int ysize=::GetSystemMetrics(SM_CYSCREEN);
cs.cx=xsize*5/10;
cs.cy=ysize*5/10;
cs.x=(xsize-cs.cx)/2;
cs.y=(ysize-cs.cy)/2;
}
其中的5/10是你的初始界面占屏幕的百分比,可以自己修改。如果想使应用程序大小固定添加cs.style&=~WS_THICKFRAME;
45. 如何得到视图指针?问题提出:
现在你有一个多线程的Demo,你想在多线程里处理视图指针里的函数,我们给这个函数起个名字:Put();该如何实现呢?
//有两种方法可以实现你的要求:
//1)第一种方法:
//要是多线程不是在App.cpp里出现,那么要在多线程的.cpp中加上extern CYourApp theApp;
//获得文档模板:
POSITION curTemplatePos = theApp.GetFirstDocTemplatePosition();
CDocTemplate *m_doc=theApp.GetNextDocTemplate(curTemplatePos);
//获得文档:
curTemplatePos=m_doc->GetFirstDocPosition();
CYourDoc *m_pdoc=(CA8Doc*)m_doc->GetNextDoc(curTemplatePos);
//获得视图:
curTemplatePos=m_pdoc->GetFirstViewPosition();
CYourView *m_pview=(CYourView*)m_pdoc->GetNextView(curTemplatePos);
//调用视图函数:
m_pview->Put();
//2)第二种方法:
//获得窗体指针:
CMainFrame *pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;
//获得与该窗体符合的视图:
CYourView *m_pView = (CYourView *) pFrame->GetActiveView();
//调用视图函数:
m_pView->Put();
46. 如何使我的程序在启动时不创建一个新文档?问题:
如何使我的程序在启动时不创建一个新文档?
解答:
在程序的InitInstance中的ProcessShellCommand函数之前加入:
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing
47. 如何将标题栏上的右键菜单屏蔽掉?解决方法:
右键菜单是系统菜单,只要将其WS_SYSMENU的属性去掉即可.
程序实现:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
........
long style = GetWindowLong(m_hWnd, GWL_STYLE);
style &= ~WS_SYSMENU;
SetWindowLong(m_hWnd, GWL_STYLE, style);
return 0;
}
48.如何全屏显示(没有标题,没有菜单,没有工具条)解决方法:
重载CMainFrame的ActivateFrame函数:
void CMainFrame::ActivateFrame(int nCmdShow)
{
CRect cRectdesktop;
WINDOWPLACEMENT windowplacement;
::GetWindowRect(::GetDesktopWindow(),&cRectdesktop);
::AdjustWindowRectEx(&cRectdesktop,GetStyle(),TRUE,GetExStyle());
windowplacement.rcNormalPosition=cRectdesktop;
windowplacement.showCmd=SW_SHOWNORMAL;
SetWindowPlacement(&windowplacement);
CFrameWnd::ActivateFrame(nCmdShow);
}
49.如何设置有背景颜色的文本
(1)解决方法
用到了CDC::SetBkMode();
程序实现
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rcView;//加 句
GetClientRect(rcView);
// TODO: add draw code for native data here
CString str (_T("Perfect Text..."));
pDC->SetBkMode(TRANSPARENT);
rcView.OffsetRect (1,1);
pDC->SetTextColor(RGB (0,0,0));
pDC->DrawText(str,str.GetLength(),rcView,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
rcView.OffsetRect(-1,-1);
pDC->SetTextColor(RGB (255,0,0));
pDC->DrawText(str,str.GetLength(),rcView,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
(2) 建立名为My的SDI或MDI,并响应WM_ERASEBKGND.
BOOL CMyView::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CBrush Brush (RGB(114,147,171));
// Select the brush into the device context .
CBrush* pOldBrush = pDC->SelectObject(&Brush);
// Get the area that needs to be erased .
CRect ViewClip;
pDC->GetClipBox(&ViewClip);
//Paint the area.
pDC->PatBlt(ViewClip.left,ViewClip.top,ViewClip.Width(),ViewClip.Height(),PATCOPY);
//Unselect brush out of device context .
pDC->SelectObject (pOldBrush );
// Return nonzero to half fruther processing .
return TRUE;
return CView::OnEraseBkgnd(pDC);
}
此方法也适合基类是EditView的SDI或MDI的情况,但是字体的颜色和底色不行.建议用WM_CTLCOLOR.
50.串太长时往让其末尾显示一个省略号(在SDI或MDI的View中)问题提出:
如何在串太长时往让其末尾显示一个省略号(在SDI或MDI的View中)?
程序实现:
建立名为My的SDI或MDI工程.
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->DrawText(CString("It's a long string,so we will add a '...' at the end."),CRect (110, 110, 180, 130),DT_LEFT | DT_END_ELLIPSIS);
//Add ellpsis to middle of string if it does not fit
pDC->DrawText(CString("It's a long string,so we will add a '...' at the end."),CRect (110, 140, 300, 160),DT_LEFT | DT_PATH_ELLIPSIS);
}
51. 如何获得其他程序的图标,并显示在View中
问题提出:
有的时候,如:类资源管理器会遇到获得程序图标并显示的操作,如何实现呢?
解决方法:
SDK函数SHGetFileInfo来获得有关文件的很多信息:如大小图标,属性,类型等.
程序实现:
建立名为My的SDI工程.在OnPaint()函数中加入:
void CMyView::OnPaint()
{
CPaintDC dc(this); // device context for painting
HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);
if (hIcon && hIcon!=(HICON)-1)
dc.DrawIcon(10,10,hIcon);
// TODO: Add your message handler code here
// Do not call CView::OnPaint() for painting messages
}
说明:_T("NotePad.exe")指的是要获得什么程序的图标.
或者在OnDraw()中(此时必须保证没有OnPaint()函数,想想为何?)
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);
if (hIcon &&hIcon!=(HICON)-1)
pDC->DrawIcon(10,10,hIcon);
}
52 .RichEdit
在Dialog(FormView中打开)中加入CRichEdit控件后,这个dialog 为什么打不开如何处理?解决方法:
在函数:InitInstance的第一句加入AfxInitRichEdit();
53. 如何使FormView中显示dialog时,不是凹的?
问题提出:
为什么FormView中显示dialog时,是凹的,能不能不这样
解决方法:
在Dialog的属性中:
增加属性WS_BORDER 或者 WS_EX_WINDOWEDGE
用程序实现:
pView->ModifyStyle(,WS_BORDER) 或者pView->ModifyStyleEx(,WS_EX_WINDOWEDGE )
54. 如何改变窗口标题?
问题提出:
在应用程序的不同运行时期,要反映当前状态往往会修改应用程序标题.
解决方法:
在MFC类库中提供了CWnd::SetWindowText函数,通过该函数可以改变任何窗体(包括控件)的标题.
改变主窗体的标题:
CWnd *m_pMainWnd;
m_pMainWnd=AfxGetMainWnd();
m_pMainWnd->SetWindowText(_T("改变标题"));
当改变多视MDI的子窗口的标题时,用:
GetParentFrame()->SetWindowText(_T("MDI Child改变标题"));
当改变按钮的标题时(假设按钮的ID=IDC_BUTTON1):
GetDlgItem(IDC_BUTTON1)->SetWindowText(_T("Button 改变标题"));
运行看看.
55.图标透明(1).Windows中的图标其实是有两个图像组成的,其中一个用于与它要显示的位置的图像做“AND”操作,另一个作“XOR”操作。
透明:用“白色”AND,用“黑色”XOR
反色:用“白色”AND,用“白色”XOR
正常色:用“黑色”AND,用正常颜色XOR.
(2). WIN9X中好像是对像素的操作实现透明的
WIN2K中就有API直接实现透明了!
WIN2K中
GetWindowLong
SetWindowLong
SetLayeredWindowAttributes
三个API就可以实现透明了!
(3)
::DrawIconEx(pDC->GetSafeHdc(),point.x,point.y,icon,icosize,icosize,0,NULL,DI_NORMAL);
56.ASSERT()是干什么用的
ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,你可以这样写程序:
......
ASSERT( n != 0);
k = 10/ n;
......
ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。
assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。
56. 将RADIO控件初始状态设置成为选中
1、在OnInitialDialog中用CButton::CheckRadioButton(...)
2、在OnInitialDialog中用CButton::SetCheck(...)
3、关联一个整型值,在构造函数中设为0。
57.获得视图
CFrameWnd* pFrameWnd = (CFrameWnd*)theApp.GetMainWnd();
CMyView* pView = (CMyView*)pFrameWnd->GetActiveView();
58.如何得到屏幕的真实尺寸问题提出:
我的屏幕是1024*800,如何得到屏幕的真实大小,我用GetSystemMetrics(SM_CYFULLSCREEN)得到的高度总是小于800
问题解答:
GetSystemMetrics(SM_CYFULLSCREEN)得到的只是屏幕用户区的大小。要得到屏幕的真实大小需要使用
GetDeviceCaps函数,该API函数原型是这样的:
int GetDeviceCaps(
HDC hdc, // handle to DC
int nIndex // index of capability
);
///得到屏幕尺寸的代码如下
void CMyDlg::OnPaint()
{
CPaintDC dc(this);
int cx = ::GetDeviceCaps(dc.m_hDC,HORZRES);///得到宽度
int cy = ::GetDeviceCaps(dc.m_hDC,VERTRES);///得到高度
CDialog::OnPaint();
59. 修改标题栏高度
NONCLIENTMETRICS nm
调用SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(nm),&nm,0)
重设SystemParametersInfo(SPI_SETNONCLIENTMETRICS,sizeof(nm),&nm,0)
60. 如何实现“气球式”工具提示。
本程序介绍一个与CToolTipCtrl相似的类CTooolTipWnd。
使用该类的方法如下:
1. 增加ToolTipWnd.cpp到工程文件。
2. 在头文件中添加#include "ToolTipWnd.h" 。
3. 在类声明中添加:
CToolTipWnd m_BalloonToolTip;
4. 在OnInitDialog(对话框)或OnInitialUpdate(表单视)中添加下面代码:
m_BalloonToolTip.Create(this);
m_BalloonToolTip.AddTool(GetDlgItem(), , [text color]);
eg.
m_BalloonToolTip.AddTool(GetDlgItem(IDC_EDIT1),"Tooltip", RGB(255,0,0));
第三个参数为可选,缺省为RGB(0, 0, 0)。缺省文本颜色可以用SetDefTextColor进行设置。
4. 重载PreTranslateMessage并添加下面代码:
if(m_BalloonToolTip)
m_BalloonToolTip.RelayEvent(pMsg);
61. dlg 上建立View的方法:
OnInitDialog()
{
CDialog:;OnInitDialog();
CRect rectWindows;
GetWinodwRect(&rectWindows);
CRuntimeClass *pViewClass=RUNTIME_CLASS(CXXXView);
CCreateContext *pContext=new CCreateContext;
pContext->m_pCurrentDoc=NULL;
pContext->m_pCurrentFrame=NULL;
pContext->m_pLastView=NULL;
pContext->m_pNewDocTemplate=NULL;
pContext->m_pNewViewClass=pViewClass;
CWnd *pWnd=DYNAMIC_DOWNCAST(CWnd,pviewClass->CreateObject());
pWnd->Create(NULL,NULL,AFX_WS_DEFAULT_VIEW,CRect(0,0,0,0),this,pContext);
delete pContext;
CXXXView *pView=DYUNAMIC_DOWNCAST(CXXXView,pWnd);
...............
}
62. 窗口最大化、最小化及关闭的消息是什么?如何截获?
最大化、最小化将发送WM_SYSCOMMAND消息。要处理该消息,可以这么做:
1、在Form的头文件中添加:
void __fastcall RestrictMinimizeMaximize(TMessage &Msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_SYSCOMMAND, TMessage, RestrictMinimizeMaximize)
END_MESSAGE_MAP(TForm)
2、在Form的单元文件中添加:
void __fastcall TForm1::RestrictMinimizeMaximize(TMessage& Msg)
{
if (Msg.WParam == SC_MINIMIZE)
{
//catches minimize...
}
else if (Msg.WParam == SC_MAXIMIZE)
{
//catches maximize...
}
TForm::Dispatch(&Msg);
// or "else TForm::Dispatch(&Msg)" to trap
}
关闭窗口的消息为WM_CLOSE,C++Builder提供了OnClose事件。
63. 如何遍历整个目录树查找文件
在应用程序的开发过程中,会遇到如何查找某一文件以确定此文件路径的问题。利用CFileFind类可以比较方便地在当前目录下进行文件查找,但却不能对其子目录中的文件进行搜寻。而实际应用中往往需要对某一整个目录树,甚至是整个C盘或D盘驱动器进行文件搜寻。通过实践,我们在Visual C++ 6.0中编程实现了如何遍历任意目录树,以查找某一特定的文件。 在下面的具体陈述中可以看到,在确定要查找的文件名和要进行搜索的目录的名称后,将调用函数Search_Directory进行文件的查找。首先依次查找当前目录下的每一个实体(文件或是子目录),如果是某一子目录,则进入该子目录并递归调用函数Search_Dirctory进行查找,查找完毕之后, 再返回上一级目录;如果不是子目录而是某一文件,则判断其是否就是我们要查找的文件,如果是则输出其完整的文件路径。这样,通过Search_Directory函数的反复递归调用,就可以实现对整个目录,包括子目录的遍历搜索。下面将举例详细讲述如何在VC++中编程实现在整个目录树中的文件查找。
1. 在Visual C++ 6.0(VC++ 5.0与之类似)中用默认方式创建了一基于对话框的应用程序Search。在主窗口对话框上放置一命令按钮,其Caption为“Search File”,ID为ID_BUTTON_SEARCH。单击此按钮将完成文件的查找工作。
2. 利用ClassWizard为“Search File”按钮的BN_CLICKED 事件添加处理函数OnButtonSearch,代码如下:
#include <direct.h>
#include <io.h>
void CSearchDlg::OnButtonSearch()
{
// TODO: Add your control notification handler code here
char szFilename[80];
// 字符串 szFilename 表示要查找的文件名
strcpy(szFilename,"Mytext.txt");
_chdir("d://"); // 进入要查找的路径(也可为某一具体的目录)
// 查找文件, 如果查到则显示文件的路径全名
Search_Directory(szFilename);
// 为CSearchDlg类的一成员函数
MessageBox(″查找文件完毕!″);
// 显示查找完毕的信息
}
3. 在CSearchDlg类中增加成员函数Search_Directory,它将完成具体的文件查找工作,代码如下:
void CSearchDlg::Search_Directory(char* szFilename)
{
long handle;
struct _finddata_t filestruct;
//表示文件(或目录)的信息
char path_search[_MAX_PATH];
//表示查找到的路径结果
// 开始查找工作, 找到当前目录下的第一个实体(文件或子目录),
// "*"表示查找任何的文件或子目录, filestruct为查找结果
handle = _findfirst("*", &filestruct);
// 如果handle为-1, 表示当前目录为空, 则结束查找而返回
if((handle == -1)) return;
// 检查找到的第一个实体是否是一个目录(filestruct.name为其名称)
if( ::GetFileAttributes(filestruct.name) & FILE_ATTRIBUTE_DIRECTORY )
{
// 如果是目录, 则进入该目录并递归调用函数Search_Dirctory进行查找,
// 注意: 如果目录名的首字符为'.'(即为"."或".."), 则不用进行查找
if( filestruct.name[0] != '.' )
{
_chdir(filestruct.name);
Search_Directory(szFilename);
// 查找完毕之后, 返回上一级目录
_chdir("..");
}
}
else // 如果第一个实体不是目录, 则检查是否是要查找的文件
{
// stricmp对两字符串进行小写形式的对比, 返回为0表示完全一致
if( !stricmp(filestruct.name, szFilename) )
{
// 先获得当前工作目录的全路径
_getcwd(path_search,_MAX_PATH);
// 再获得文件的完整的路径名(包含文件的名称)
strcat(path_search,"//");
strcat(path_search,filestruct.name);
MessageBox(path_search); //输出显示
}
}
// 继续对当前目录中的下一个子目录或文件进行与上面同样的查找
while(!(_findnext(handle,&filestruct)))
{
if( ::GetFileAttributes(filestruct.name) & FILE_ATTRIBUTE_DIRECTORY )
{
if(*filestruct.name != '.')
{
_chdir(filestruct.name);
Search_Directory(szFilename);
_chdir("..");
}
}
else
{
if(!stricmp(filestruct.name,szFilename))
{
_getcwd(path_search,_MAX_PATH);
strcat(path_search,"//");
strcat(path_search,filestruct.name);
MessageBox(path_search);
}
}
}
_findclose(handle);
// 最后结束整个查找工作
}
这样我们就可以对整个目录进行遍历搜索,查找某一特定的文件,并输出显示其完整的文件路径。以上的程序在Visual C++ 6.0中已调试通过。64. Richedit control的设置背景图片办法
1:继承CRichEditCtrl::OnEraseBkgnd(CDC* pDC)消息事件中,给Richedit控件绘制上背景图片:m_bmpBackground.DrawDIB(pDC, 0, 0, rc.Width(), rc.Height());当然也可以通过subclass richedit window之后,在回调函数中处理WM_ERASEBKGND消息。
2:设置了Richedit控件的透明属性;
3:依照kenwhale所说的,Hook了GDI32.DLL中的ExtTextOut函数,将RichEdit的text-output options去除ETO_OPAQUE style
。综上所述,即可实现RichEdit控件的背景图片效果。
据此,我还实现了RichEdit控件背景绘制AVI动画效果。
65. MFC程序中如何创建多级目录
BOOL mkdirEx(const char* lpPath)
{
CString pathname = lpPath;
if(pathname.Right(1) != "/")
pathname += "/" ;
int end = pathname.ReverseFind('/');
int pt = pathname.Find('/');
if (pathname[pt-1] == ':')
pt = pathname.Find('/', pt+1);
CString path;
while(pt != -1 && pt<=end)
{
path = pathname.Left(pt+1);
if(_access(path, 0) == -1)
_mkdir(path);
pt = pathname.Find('/', pt+1);
}
return true;
}
66. 解决外部符号错误:_main,_WinMain@16,__beginthreadex
在创建MFC项目时, 不使用MFC AppWizard向导, 如果没有设置好项目参数, 就会在编译时产生很多连接错误, 如error LNK2001错误, 典型的错误提示有:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex下面介绍解决的方法:
1). Windows子系统设置错误, 提示:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Windows项目要使用Windows子系统, 而不是Console, 可以这样设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:console改成/subsystem:windows
2). Console子系统设置错误, 提示:
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
控制台项目要使用Console子系统, 而不是Windows, 设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:windows改成/subsystem:console
3). 程序入口设置错误, 提示:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
通常, MFC项目的程序入口函数是WinMain, 如果编译项目的Unicode版本, 程序入口必须改为wWinMainCRTStartup, 所以需要重新设置程序入口:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Output,
再在Entry-point symbol中填入wWinMainCRTStartup, 即可
4). 线程运行时库设置错误, 提示:
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
这是因为MFC要使用多线程时库, 需要更改设置:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Code Generation,
再在Use run-time library中选择Debug Multithreaded或者multithreaded
其中,
Single-Threaded单线程静态链接库(release版本)
Multithreaded多线程静态链接库(release版本)
multithreaded DLL多线程动态链接库(release版本)
Debug Single-Threaded单线程静态链接库(debug版本)
Debug Multithreaded多线程静态链接库(debug版本)
Debug Multithreaded DLL多线程动态链接库(debug版本)
单线程: 不需要多线程调用时, 多用在DOS环境下
多线程: 可以并发运行
静态库: 直接将库与程序Link, 可以脱离MFC库运行
动态库: 需要相应的DLL动态库, 程序才能运行
release版本: 正式发布时使用
debug版本: 调试阶段使用
67. 创建包含多个子目录的目录
void CreateAllDirectories(CString strDir)
{
//remove ending / if exists
if(strDir.Right(1)=="//")
strDir=strDir.Left(strDir.GetLength()-1);
// base case . . .if directory exists
if(GetFileAttributes(strDir)!=-1)
return;
// recursive call, one less directory
int nFound = strDir.ReverseFind('//');
CreateAllDirectories(strDir.Left(nFound));
// actual work
CreateDirectory(strDir,NULL);
}
68. ReverseFind()
#include <STDIO.H>
#include <AFX.H>
int main()
{
CString s;
s.Format("abcdefghijk");
int nPos = s.ReverseFind('a');
printf("nPos is %d/n",nPos);
return 0;
}
其中,'a'对应的nPos是0,'h'对应的nPos是7,以此类推。但是:s.ReverseFind('a')和s.Find('a')的结果是一样的。
问题是:ReverseFind() 和 Find() 有什么区别呢:
对于ReverseFind(),查找顺序是从后往前,找到后的nPos是按前后顺序排列的。
而Find()是从前往后查的,找到后的nPos也是按前后顺序排列的。69. MDI中如何只屏蔽掉子框架的右上角的关闭按钮
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
return -1;
。。。
CMenu* pSysMenu = GetSystemMenu(FALSE);
pSysMenu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND |MF_DISABLED|MF_GRAYED);
return 0;
}
70. 程序如何删除自己
/////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE h, HINSTANCE b, LPSTR psz, int n) {
// Is this the Original EXE or the clone EXE?
// If the command-line 1 argument, this is the Original EXE
// If the command-line >1 argument, this is the clone EXE
if (__argc == 1) {
// Original EXE: Spawn clone EXE to delete this EXE
// Copy this EXEcutable image into the user''s temp directory
TCHAR szPathOrig[_MAX_PATH], szPathClone[_MAX_PATH];
GetModuleFileName(NULL, szPathOrig, _MAX_PATH);
GetTempPath(_MAX_PATH, szPathClone);
GetTempFileName(szPathClone, __TEXT("Del"), 0, szPathClone);
CopyFile(szPathOrig, szPathClone, FALSE);
//***注意了***:
// Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE
HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL, OPEN_EXISTI
NG, FILE_FLAG_DELETE_ON_CLOSE, NULL);
// Spawn the clone EXE passing it our EXE''s process handle
// and the full path name to the Original EXE file.
TCHAR szCmdLine[512];
HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE, GetCurrentProcessId());
wsprintf(szCmdLine, __TEXT("%s %d /"%s/""), szPathClone, hProcessOrig, szPat
hOrig);
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
CloseHandle(hProcessOrig);
CloseHandle(hfile);
// This original process can now terminate.
} else {
// Clone EXE: When original EXE terminates, delete it
HANDLE hProcessOrig = (HANDLE) _ttoi(__targv[1]);
WaitForSingleObject(hProcessOrig, INFINITE);
CloseHandle(hProcessOrig);
DeleteFile(__targv[2]);
// Insert code here to remove the subdirectory too (if desired).
// The system will delete the clone EXE automatically
// because it was opened with FILE_FLAG_DELETE_ON_CLOSE
}
return(0);
}
这一段程序思路很简单:不是不能在运行时直接删除本身吗?好,那么程序先复制(CLONE)一个自己,用复制品起动另一个进程,然后自己结束运行,则原来的EXE文件不被系统保护.这时由新进程作为杀手删除原来的EXE文件,并且继续完成程序其他的功能。 新进程在运行结束后,复制品被自动删除。这又是值得介绍的一个把戏了,注意:
// Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE
HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL,OPEN_EXISTIN
G, FILE_FLAG_DELETE_ON_CLOSE, NULL);
这里面的FILE_FLAG_DELETE_ON_CLOSE标志,这个标志是告诉操作系统,当和这个文件相关的所有句柄都被关闭之后(包括上面这个CREATEFILE创建的句炳),就把这个文件删除。几乎所有的临时文件在创建时,都指明了这个标志。另外要注意的是:在复制品进程对原始程序操刀之前,应该等待原进程退出.在这里用的是进程同步技术.用HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE,GetCurrentProcessId());得到原进程句柄.SYNCHRONICE标志在NT下有效,作用是使OpenProcess得到的句柄可以做为同步对象.复制品进程用WaitForSingleObject函数进行同步,然后一个DeleteFile,以及进行其它销毁证据(比如删目录)的工作,一切就完事了。
程序是基于CONSOLE的,通过传入的参数确定是原始的进程还是复制品新进程,并且得到需要操作的目标文件的信息(主要是路径),复制品放在系统的TEMP目录(GetTempPath得到),你也可以随便找个你认为安全的地方(比如:WINDOWS/SYSTEM32等等)。这里面没有甚么深的技术.再看其他的一些实现删除自己的例子,比如说在进程退出前,用fwrite等方法输出一个.BAT文件,在里面写几句DEL,然后WINEXEC一下这个BAT文件即可.玩儿过DOS的虫虫大多都会。
71. 隐藏标题栏和菜单栏
隐藏标题栏 ModifyStyle(WS_CAPTION,0)
隐藏菜单栏 SetMenu(NULL)
72. InflateRect
InflateRect这个函数用于增大或减小一个矩形的大小.
如m_graphRect.InflateRect(-70, -30, -30, -50);
将矩形左边坐标加70,上面加30,右边减30,下面减50。
73. 怎么让无模式对话框显示在主窗口后面
要解决这个问题的关键在于CDialog的Create并不能建立一个无属主的窗口.必须用另外方式建窗口.
比如你的对话框类叫CDlgNoOwner,在CMainFrame中加一个CDlgNoOwner类的成员变量,
弹出这个对话框的消息处理函数为
void CMainFrame::OnNoowner()
{
CDlgNoOwner *m_dlgTest=new CDlgNoOwner(this);
HWND hwndDlg=::CreateDialog(AfxGetInstanceHandle(),MAKEINTRESOURCE(CDlgNoOwner::IDD),NULL/*owner*/,NULL/*dlgproc*/);
//注意此处DLGPROC为NULL,并不要紧,因为接下要subclass啦
m_dlgTest->SubclassWindow (hwndDlg);//挂接到成员变量!
m_dlgTest->ShowWindow (SW_SHOW);
//这时可以看到一个"自由"的对话框弹出,和你的主窗口是平起平坐的.
}
当然不要忘了在对话框关闭时DestroyWindow()..那都是在对话框类中的标准处理了.74. 隐藏窗口(子窗口没有焦点时)
在程序启动时 InitDialog 中使用 SetWindowPos 将窗体设置到屏幕以外
然后再隐藏
1.在OnInitDialog()函数里设置定时器:(WINDOWS API里面响应消息WM_INITDIALOG)
SetTimer(1, 1, NULL);
2.添加处理WM_TIMER的消息处理函数OnTimer,添加代码:
if(nIDEvent == 1)
{
DeleteTimer(1);
ShowWindow(SW_HIDE);
}
75.修改视图背景
How do I change the background color of a view?
To change the background color for a CView, CFrameWnd, or CWnd object, process the WM_ERASEBKGND message. The following code shows how:
BOOL CSampleView::OnEraseBkgnd(CDC* pDC)
{
// Set brush to desired background color.
CBrush backBrush(RGB(255, 128, 128));
// Save old brush.
CBrush* pOldBrush = pDC->SelectObject(&backBrush);
CRect rect;
pDC->GetClipBox(&rect); // Erase the area needed.
pDC->PatBlt(rect.left, rect.top, rect.Width(),
rect.Height(), PATCOPY);
pDC->SelectObject(pOldBrush);
return TRUE;
}
I solved the problem like this:
HBRUSH dlgtest::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
switch (nCtlColor)
{
case CTLCOLOR_BTN:
case CTLCOLOR_STATIC:
{
pDC->SetBkMode(TRANSPARENT);
}
case CTLCOLOR_DLG:
{
CBrush* back_brush;
COLORREF color;
color = (COLORREF) GetSysColor(COLOR_BTNFACE);
back_brush = new CBrush(color);
return (HBRUSH) (back_brush->m_hObject);
}
}
return(CFormView::OnCtlColor(pDC, pWnd, nCtlColor));
}
76. 如何实现点击对话框外的地方使对话框到主窗口的后面只能将桌面做为父窗口
pMDlg = new CMDlg;
pMDlg->Create(IDD_M_DIALOG,CWnd::GetDesktopWindow()/* 设置父窗口 */);
pMDlg->ShowWindow(SW_SHOW);
然后在任务栏里隐藏对话框程序
如何让对话框应用程序在在任务栏上不出现,并且不隐藏窗口。解决方法:
把对话框的扩展属性修改成为WS_EX_TOOLWINDOW。
程序实现:
把对话框的属性设置成为toolwindow,然后在需要的地方执行本代码。
DWORD Style = ::GetWindowLong(AfxGetMainWnd()->m_hWnd,GWL_EXSTYLE);
Style = WS_EX_TOOLWINDOW ;
AfxGetMainWnd()->ShowWindow(FALSE);
::SetWindowLong(AfxGetMainWnd()->m_hWnd,GWL_EXSTYLE,Style);
AfxGetMainWnd()->ShowWindow(TRUE);
77. 想在程序一启动时就自动关闭窗口,不在任务栏里显示用CTRL+W打开ClassWizard;
点击Class Info页,类名是工程名Dlg,
再在左下方的"Filter"中选择"Windows";
回到Message Maps页,就可以看到消息中有WM_WINDOWPOSCHANGING,
加入代码,如上所示.
这样运行*.EXE,不但看不到主界面,任务栏也没有,就是任务管理器中的"应用程序"中也不列出,那该如何关闭它?
在任务管理器的"进程"中可以找到它,这是黑客程序常用的方法.
如果需要的话,连"进程"中也看不到.这样要终止它就是问题了.
78.修改打印预览的ToolBar
为AFX_IDD_PREVIEW_TOOLBAR这个ID创建一个DialogBar。则系统就会用新创建的DialogBar代替系统默认的那个
79. 如何实现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.否则会出错。80. 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);
}
81. 如何用键盘滚动分割的视口?
我的问题是当我用鼠标滚动分割窗口时,视口滚动都很正常,但用键盘时,却什么也没有发生.
在你的视图继承类中加入如下两个函数,假定该类为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;
}
82. 如何改变默认的光标形状?
我试着将光标改变为其它的形状和颜色,但却没有变化.
在对话框/窗口/你需要的地方加上对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.83. 如何选择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;
}
84. 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);
}
}
85. 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;
}
86. 如何限制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;
}
87. 怎样实现3D效果?在对话框中怎样实现Edit和Listboxes控件的3D效果?(环境95/NT VC5.0)
1). 使用带WS_EX_CLIENTEDGE标志的::CreateWindowEx来替换::CreateWindow 或者用CWnd::CreateEx替换CWnd::Create.
2).在建立控件之后,调用ModifyStyleEx(0, WS_EX_CLIENTEDGE).
88. How do I update the text of a pane in a status bar?
By default, a CStatusBar pane is not enabled when the pane is created. To activate a pane, you must call the ON_UPDATE_COMMAND_UI() macro for each pane on the status bar and update the panes. Because panes do not send WM_COMMAND messages, you cannot use ClassWizard to activate panes; you must type the code manually. For example, suppose one pane has ID_INDICATOR_PAGE as its identifier and that it contains the current page number in a document. To make the ID_INDICATOR_PAGE pane display text, add the following to a header file (probably the MAINFRM.H file):
afx_msg void OnUpdatePage(CCmdUI *pCmdUI);
Add the following to the application message map:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_PAGE, OnUpdatePage)
Add the following to a source code file (probably MAINFRM.CPP):
void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI)
{
pCmdUI->Enable();
}
To display text in the panes, either call SetPaneText() or call CCmdUI::SetText() in the OnUpdate() function. For example, you might want to set up an integer variable m_nPage that contains the current page number. Then, the OnUpdatePage() function might read as follows:
void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI)
{
pCmdUI->Enable();
char szPage[16];
wsprintf((LPSTR)szPage, "Page %d", m_nPage);
pCmdUI->SetText((LPSTR)szPage);
}
This technique causes the page number to appear in the pane during idle processing in the same manner that the application updates other indicators.
89. 动态修改对话框的大小问题提出:
关于如何动态改变对话框的大小,我做了个Demo,大家看看.
程序实现:
//本函数使用方法:
//第一个参数:如果是TRUE表示显示扩展的对话框,如果是FALSE,表示缩小对话框。
//第二个参数:表示本对话框的HWND,
//第三个参数:表示缩小后大小的控件的ID
void COptionDlg::ExpandBox(BOOL fExpand, HWND hwnd, int nIDDefaultBox)
{
CWnd *pWndBox=GetDlgItem(nIDDefaultBox);
RECT rcDefaultBox,rcChild,rcIntersection,rcWnd;
pWndBox->GetWindowRect(&rcDefaultBox);
HWND hwndChild = ::GetTopWindow(hwnd);
for (; hwndChild != NULL; hwndChild = ::GetNextWindow(hwndChild,GW_HWNDNEXT))
{
::GetWindowRect(hwndChild, &rcChild);
if (!IntersectRect(&rcIntersection, &rcChild, &rcDefaultBox))
::EnableWindow(hwndChild, fExpand);
}
::GetWindowRect(hwnd, &rcWnd);
if (GetWindowLong(hwnd, GWL_USERDATA) == 0)
{
SetWindowLong(hwnd, GWL_USERDATA,
MAKELONG(rcWnd.right - rcWnd.left,
rcWnd.bottom - rcWnd.top));
::ShowWindow(pWndBox->m_hWnd, SW_HIDE);
}
::SetWindowPos(hwnd, NULL, 0, 0,
rcDefaultBox.right - rcWnd.left,
rcDefaultBox.bottom - rcWnd.top,
SWP_NOZORDER | SWP_NOMOVE);
if(fExpand)
{
DWORD dwDims = GetWindowLong(hwnd, GWL_USERDATA);
::SetWindowPos(hwnd, NULL, 0, 0,
LOWORD(dwDims), HIWORD(dwDims), SWP_NOZORDER | SWP_NOMOVE);
::SendMessage(hwnd, DM_REPOSITION, 0, 0);
}
}
90. 用DoModal()调用模态对话框,总是显示在正中,我重载了它,并添加了MoveWindow(),可是其m_hWnd是一串零,调用失败。请问有何方法可使调用的模态对话框显示于自定义位置?多谢 我不清楚你把MoveWindow()加在什么地方了,正确的方法是在OnInitDialog中添加MoveWindow,如:
MoveWindow(0, 1, 300, 200);
需要注意的是前两个参数不能都为0。如果你确实希望把窗口放在(0, 0)处,可以在对话框设计窗口的属性中选中Absolute Align,然后再加入
MoveWindow(0, 0, 300, 200);
为什么会是这样?你看了MFC的源程序就会明白。原来MFC在调用你的OnInitDialog之后,会调用CDialog::CheckAutoCenter()(在dlgcore.cpp中)检查是否需要将窗口居中,你看了这个函数后就明白为什么需要上面那么做了。希望本文所述对大家的MFC程序设计有所帮助。