//窗口最大化
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
m_pMainWnd->UpdateWindow();
//
//扩展风格:使 List 带有 整行选择、表格、整行预选 功能
DWORD dwStyleEx;
dwStyleEx = LVS_EX_FULLROWSELECT | LVS_REPORT | LVS_EX_ONECLICKACTIVATE;
m_GndDzStatusList.SetExtendedStyle( dwStyleEx );
//
//设置光标
SetCursor(AfxGetApp()->LoadCursor(IDC_CrossArrow));
//菜单加对勾
pCmdUI->SetCheck(m_bIsDrawLine);
//颜色
pDC->SetTextColor(RGB(0, 0, 0));
//背景颜色
pDC->SetBkColor(RGB(192, 255, 192));
//得到CString的长度
CClientDC dc(this);
CString sTextWord;
CSize cSize;
cSize = dc.GetTextExtent(m_strBaseWord[m_BaseWordID].TextWord,
m_strBaseWord[m_BaseWordID].TextWord.GetLength());
//得到当前字体的信息
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
//创建光标宽度,高度
CreateSolidCaret(tm.tmAveCharWidth/8, tm.tmHeight);
//设置光标
SetCaretPos(m_pPoint);
//显示光标
ShowCaret();
//
AfxMessageBox(_T("删除 ") + Purpose + _T(" 失败!"), MB_ICONSTOP);
//选中一项
m_ExpenseList.SetSelectionMark(0);
//插入首一项
m_ExpenseList.InsertItem(nIndex, m_Purpose, 0);
//自动选中一项
m_GndDzStatusList.SetItemState(nItem, LVIS_SELECTED, LVIS_SELECTED);
//自动滚动确保选中项能见
m_ContractList.EnsureVisible(nIndex, 1);
//删除CString中的%号
CString sString.Remove('%');
//防止添加Box重复
while (pStockMsgSet->IsEOF() == FALSE)
{
if ( m_StockTypeBox.FindStringExact(-1, pStockMsgSet->m_StockType) < 0 )
m_StockTypeBox.AddString(pStockMsgSet->m_StockType);
pStockMsgSet->MoveNext();
}
//打开数据库
TRY
{
pStationNameSet->Open();
}
CATCH(CDBException, e)
{
AfxMessageBox("不能打开[StationName]表, /n 导出厂站失败!");
delete pStationNameSet;
return FALSE;
}
END_CATCH
//
//取当前目录
char Path[255];
GetCurrentDirectory(255, Path);
//
//用对话框找文件
CString sSelectPathName;
CString sSelectName;
CString sSelectExt;
CFileDialog dlg(TRUE);
dlg.m_ofn.Flags = OFN_NOCHANGEDIR;
dlg.m_ofn.lpstrFilter = "Micrsoft Acess 数据库 (*.mdb)/0*.mdb/0/0";
if (dlg.DoModal() == IDCANCEL)
return FALSE;
//创建 ODBC ,选择数据库
sSelectPathName = dlg.GetPathName();
sSelectName = dlg.GetFileTitle();
sSelectExt = dlg.GetFileExt();
if (sSelectExt.CompareNoCase("mdb") != 0)
{
AfxMessageBox("所选文件扩展名必须是 .mdb ! /n 请重新选 择数据库。");
return FALSE;
}
//
//显示盘符
void CFileMakerView::FillDriveInfo()
{
DWORD dwDrives;
char a;
CString DriveName;
dwDrives = GetLogicalDrives();
a = 'A';
while (dwDrives > 0)
{
if (dwDrives % 2 == 1)
{
DriveName.Format("%c", a);
GetTreeCtrl().InsertItem(DriveName, m_nImageClose, m_nImageOpen, TVI_ROOT,
TVI_LAST);
}
a++;
dwDrives /= 2;
}
}
//
//判该程序是否运行(BOOL CMakeSheetApp::InitInstance())
HANDLE Handle;
Handle = CreateMutex(NULL, TRUE, _T("MakeSheet3.0"));
if (Handle == NULL)
return FALSE;
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox("MakeSheet3.0已运行!", MB_ICONSTOP);
return FALSE;
}
//
//得到System所在目录(为与WindowsNT兼容)
char cSysDir[100], cFileName[] = "//odbcjt32.dll";
GetSystemDirectory(cSysDir, 100);
//连接两个字符串
strcat(cSysDir, cFileName);
//
//给对话框加上位图
//在头文件中定义(public)
CDibApi m_BackBmp;
BOOL CDeviceQueryDlg::OnInitDialog()
{
m_BackBmp.OpenBmp(".//位图.bmp");
m_BackBmp.InitDIBData();
}
void CDeviceQueryDlg::OnPaint()
{
m_BackBmp.DrawDIB(&dc, 0, 0);
}
//------------------------------------------------------------
我也来写一个,最近发生的!
#error : Please use the /MD switch for _AFXDLL builds
/************/
ALT + F7 -> c/c++ -> 分类里选择 Code Generation -> userun-time library 选择
Debug Multihreaded Dll
//------------------------------------------------------------
让静态文本框改变背景
继承CStatic 类,重载 CtlColor
HBRUSH CMyStatic::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
// TODO: Return a non-NULL brush if the parent's handler should not be called
// return NULL;
ASSERT(nCtlColor == CTLCOLOR_STATIC);
pDC->SetBkMode( TRANSPARENT );
return (HBRUSH)GetStockObject( NULL_BRUSH );
}
//------------------------------------------------------------
在两个对话框的控件上传值
在CDialog2中定义一个static的变量m_judge
假设IDC_TEXT对应的变量是m_text则:
if(CDialog1.DoModul()==IDOK)
{
CDialog2::m_judge=m_text;//就可以了
}
//------------------------------------------------------------
微软源码里经常用这些
1.计算数组中元素个数
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
2.错误跳转goto 的代用品
do{
//出错时break;
return TRUE;
}while(1);
//清除工作
...
return FALSE;
//------------------------------------------------------------
关于注册控件
ActiveX控件的文件名最好是短文件名(8.3), 因为在注册表中对应的文件名是短文件名,
若为长文件名,则变成xxx~1,xxx~2等, 这样容易出错!!
如某个控件名本来是MyActiveX.ocx, 结果在注册表中记为MyActi~1.ocx. 做个实验: 注
册
之后,将文件名改为MyActiveX1.ocx, 结果系统还是认为MyActiveX1.ocx就是原来的文件
呢!!
所以容易出错.
ActiveX控件的文件名和路径名最好不要用中文!否则虽能注册成功,但无法使用。
//-----------------------------------------------------------------------------
1) 在View中获得Doc指针
2) 在App中获得MainFrame指针
3) 在View中获得MainFrame指针
4) 获得View(已建立)指针
5) 获得当前文档指针
6) 获得状态栏与工具栏指针
7) 获得状态栏与工具栏变量
8) 在Mainframe获得菜单指针
9) 在任何类中获得应用程序类
10) 从文档类取得视图类的指针(1)
11) 在App中获得文档模板指针
12) 从文档模板获得文档类指针
13) 在文档类中获得文档模板指针
14) 从文档类取得视图类的指针(2)
15) 从一个视图类取得另一视图类的指针
VC中编程对于刚刚开始学习的同学,最大的障碍和问题就是消息机制和指针获取与操作。其
实这些内容基本上是每本VC学习工具书上必讲的内容,而且通过MSDN很多问题都能解决。
下面文字主要是个人在编程中指针使用的一些体会,说的不当的地方请指正。
一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架,无论是多文档还
是单文档,都存在指针获取和操作问题。
下面这节内容主要是一般的框架,然后再讲多线程中的指针使用。使用到的类需要包含响应
的头文件。首先一般获得本类(视,文档,对话框都支持)实例指针this,用this的目的,主
要可以通过类中的函数向其他类或者函数中发指针,以便于在非本类中操作和使用本类中的
功能。
1) 在View中获得Doc指针 CYouSDIDoc *pDoc=GetDocument();一个视只能有一个文档。
2) 在App中获得MainFrame指针
CWinApp 中的 m_pMainWnd变量就是MainFrame的指针
也可以: CMainFrame *pMain =(CMainFrame *)AfxGetMainWnd();
3) 在View中获得MainFrame指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()-
>m_pMainWnd;
4) 获得View(已建立)指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()-
>m_pMainWnd;
CyouView *pView=(CyouView *)pMain->GetActiveView();
5) 获得当前文档指针 CDocument * pCurrentDoc =(CFrameWnd *)m_pMainWnd-
>GetActiveDocument();
6) 获得状态栏与工具栏指针 CStatusBar * pStatusBar=(CStatusBar *)AfxGetMainWnd
()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
CToolBar * pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow
(AFX_IDW_TOOLBAR);
7) 如果框架中加入工具栏和状态栏变量还可以这样 (CMainFrame *)GetParent()-
>m_wndToolBar;
(CMainFrame *)GetParent()->m_wndStatusBar;
8) 在Mainframe获得菜单指针 CMenu *pMenu=m_pMainWnd->GetMenu();
9) 在任何类中获得应用程序类
用MFC全局函数AfxGetApp()获得。
10) 从文档类取得视图类的指针
我是从 http://download.cqcnc.com/soft/program/article/vc/vc405.html学到的,从文档
获得视图类指针目的一般为了控制同一文档的多个视图的定位问题,我的体会特别是文字处
理CEditView当产生多个视图类时,这个功能是非常需要的。
CDocument类提供了两个函数用于视图类的定位:
GetFirstViewPosition()和GetNextView() virtual POSITION GetFirstViewPosition()
const;
virtual CView* GetNextView(POSITION& rPosition) const;
注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。
GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个
POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调用
的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因
此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变
量来辅助操作): CTestView* pTestView;
POSITION pos=GetFirstViewPosition();
pTestView=GetNextView(pos);
这样,便可到了CTestView类的指针pTestView.执行完几句后,变量pos=NULL,因为没有下一
个视图类,自然也没有下一个视图类的POSITION.但是这几条语句太简单,不具有太强的通
用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们
需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,
可用IsKindOf()成员函数时行检查,如: pView->IsKindOf(RUNTIME_CLASS(CTestView));
即可检查pView所指是否是CTestView类。
有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文
档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下: CView*
CTestDoc::GetView(CRuntimeClass* pClass)
{
CView* pView;
POSITION pos=GetFirstViewPosition();
while(pos!=NULL){
pView=GetNextView(pos);
if(!pView->IsKindOf(pClass))
break;
}
if(!pView->IsKindOf(pClass)){
AfxMessageBox("Connt Locate the View./r/n http://www.VCKBASE.com");
return NULL;
}
return pView;
}
其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:
1.pos为NULL,即已经不存在下一个视图类供操作;
2.pView已符合要求。
1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同
时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全有可能既是
pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一个视图类时就如引。
因此需采用两次判断。
使用该函数应遵循如下格式(以取得CTestView指针为例):CTestView* pTestView=
(CTestView*)GetView(RUNTIME_CLASS(CTestView));
RUNTIME_CLASS是一个宏,可以简单地理解它的作用:将类的名字转化为CRuntimeClass为指
针。
至于强制类型转换也是为了安全特性考虑的,因为从同一个基类之间的指针类型是互相兼容
的。这种强制类型转换也许并不必要,但能避免一些可能出现的麻烦。
3.从一个视图类取得另一视图类的指针 综合1和2,很容易得出视图类之间互相获得指针的
方法:就是用文档类作中转,先用1的方法得到文档类的指针,再用2的方法,以文档类的视
图定位函数取得另一个视图类。同样,可以实现成一个函数:
(假设要从CTestAView中取得指向其它视图类的指针)CView* CTestAView::GetView
(CRuntimeClass* pClass)
{
CTestDoc* pDoc=(CTestDoc*)GetDocument();
CView* pView;
POSITION pos=pDoc->GetFirstViewPosition();
while(pos!=NULL){
pView=pDoc->GetNextView(pos);
if(!pView->IsKindOf(pClass))
break;
}
if(!pView->IsKindOf(pClass)){
AfxMessageBox("Connt Locate the View.");
return NULL;
}
return pView;
}
这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在
GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员
函数。
有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如下:CTestBView*
pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));
11)对于单文档中也可以加入多个文档模板,但是一般的开发就使用MDI方式开发多文档模
板,其方法与上述视图的获取方法很接近,这里稍做解释,如果不清楚,请查阅MSDN,(以
下四个内容(11、12、13、14)来源: http://sanjianxia.myrice.com/vc/vc45.htm)
可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位
置;
利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。
POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;
第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检
索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是
模板列表中的最后一个,则pos参数被置为NULL。
//-----------------------------------------------------------------------------
一些初学者的常见问题:
1.如何让编译好的VC程序能在其他未装VC的机器上正常运行:
在编译工具条上选择release,然后Rebuild All.
2.如何使一个按钮变灰:
方法1:GetDlgItem(按钮ID)->EnableWindow(false);
方法2:先将一个控件变量(如m_button1)与欲变灰的按钮进行关联,然后,执行如下代码:
m_button1.EnableWindow(false);
3.如果你在建立了一个工程后,发现你要对一个数据源进行操作,但你又不想重新开一个工
程,方法如下:
a.在菜单上点击"建立类向导"或者"MFC ClassWizard".
b.点击Add Class按钮.
c.输入你的数据库类名,并选择基类为CRecordset,下面的操作想必大家都会了吧。
4.如何使一个菜单变灰:
这个严格说来不能算是一个初级问题了,其实现方法也很简单。
使菜单变灰的实现原理实际上与使按钮变灰的原理是截然不同的方法如下:
使菜单变灰实际上是由编程者事先写好逻辑代码,再将逻辑代码嵌入到菜单项的消息处理
函数UPDATE_COMMAND_UI中,当程序执行时,由程序根据潜入到UPDATE_COMMAND_UI中的代码
来决定是否将菜单项变灰:
a.点击"建立类向导"或者"MFC classWizard"菜单项,在第一选项页Message Maps中的
Object IDs中选择你想变灰的菜单ID,在右边选择UPDATE_COMMAND_UI,点击Add Function
按钮增加消息处理函数,点击Edit Code按钮,在消息处理函数中添加代码如下(我做过的一
个程序):
extern short MyRight;
pCmdUI->Enable(MyRight == 111 || MyRight == 222);
这段代码实际上是根据全局变量MyRight的值来决定是否将此菜单项变灰,如果条件不
成立,菜单项就是灰的。你还可以自己编写全局函数并根据函数的返回值来确定是否变灰。
5.如何解决unexpected end of file问题:
当你在编译一个MFC工程时,如果出现如上提示,并且你确定你的头文件中的{}都是配套
的也没有缺少“;”,你可以在头文件的开头加上如下代码:
#include "stdafx.h"即可解决此问题。
6.如何查一个函数的定义出处?
将光标放到欲查的函数名前,点击菜单上的来源流览器,选择Definitions and
References.
1 RECT, PRECT, NPRECT, LPRECT;它们之间有什么区别?
RECT是一个表示矩形的结构
PRECT表示指向举行结构的指针
NPRECT表示指向举行结构的近指针
LPRECT表示指向举行结构的长指针
最后这3个结构在32位的Windows中都是一样的,在16位的Windows系统中就有区别了,只是
为了与老的16位程序兼容才定义了这些结构指针的,对于现在的程序来说,都是在32位
Windows上进行的开发,所以用那个都一样,就看你的习惯了
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
m_pMainWnd->UpdateWindow();
//
//扩展风格:使 List 带有 整行选择、表格、整行预选 功能
DWORD dwStyleEx;
dwStyleEx = LVS_EX_FULLROWSELECT | LVS_REPORT | LVS_EX_ONECLICKACTIVATE;
m_GndDzStatusList.SetExtendedStyle( dwStyleEx );
//
//设置光标
SetCursor(AfxGetApp()->LoadCursor(IDC_CrossArrow));
//菜单加对勾
pCmdUI->SetCheck(m_bIsDrawLine);
//颜色
pDC->SetTextColor(RGB(0, 0, 0));
//背景颜色
pDC->SetBkColor(RGB(192, 255, 192));
//得到CString的长度
CClientDC dc(this);
CString sTextWord;
CSize cSize;
cSize = dc.GetTextExtent(m_strBaseWord[m_BaseWordID].TextWord,
m_strBaseWord[m_BaseWordID].TextWord.GetLength());
//得到当前字体的信息
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
//创建光标宽度,高度
CreateSolidCaret(tm.tmAveCharWidth/8, tm.tmHeight);
//设置光标
SetCaretPos(m_pPoint);
//显示光标
ShowCaret();
//
AfxMessageBox(_T("删除 ") + Purpose + _T(" 失败!"), MB_ICONSTOP);
//选中一项
m_ExpenseList.SetSelectionMark(0);
//插入首一项
m_ExpenseList.InsertItem(nIndex, m_Purpose, 0);
//自动选中一项
m_GndDzStatusList.SetItemState(nItem, LVIS_SELECTED, LVIS_SELECTED);
//自动滚动确保选中项能见
m_ContractList.EnsureVisible(nIndex, 1);
//删除CString中的%号
CString sString.Remove('%');
//防止添加Box重复
while (pStockMsgSet->IsEOF() == FALSE)
{
if ( m_StockTypeBox.FindStringExact(-1, pStockMsgSet->m_StockType) < 0 )
m_StockTypeBox.AddString(pStockMsgSet->m_StockType);
pStockMsgSet->MoveNext();
}
//打开数据库
TRY
{
pStationNameSet->Open();
}
CATCH(CDBException, e)
{
AfxMessageBox("不能打开[StationName]表, /n 导出厂站失败!");
delete pStationNameSet;
return FALSE;
}
END_CATCH
//
//取当前目录
char Path[255];
GetCurrentDirectory(255, Path);
//
//用对话框找文件
CString sSelectPathName;
CString sSelectName;
CString sSelectExt;
CFileDialog dlg(TRUE);
dlg.m_ofn.Flags = OFN_NOCHANGEDIR;
dlg.m_ofn.lpstrFilter = "Micrsoft Acess 数据库 (*.mdb)/0*.mdb/0/0";
if (dlg.DoModal() == IDCANCEL)
return FALSE;
//创建 ODBC ,选择数据库
sSelectPathName = dlg.GetPathName();
sSelectName = dlg.GetFileTitle();
sSelectExt = dlg.GetFileExt();
if (sSelectExt.CompareNoCase("mdb") != 0)
{
AfxMessageBox("所选文件扩展名必须是 .mdb ! /n 请重新选 择数据库。");
return FALSE;
}
//
//显示盘符
void CFileMakerView::FillDriveInfo()
{
DWORD dwDrives;
char a;
CString DriveName;
dwDrives = GetLogicalDrives();
a = 'A';
while (dwDrives > 0)
{
if (dwDrives % 2 == 1)
{
DriveName.Format("%c", a);
GetTreeCtrl().InsertItem(DriveName, m_nImageClose, m_nImageOpen, TVI_ROOT,
TVI_LAST);
}
a++;
dwDrives /= 2;
}
}
//
//判该程序是否运行(BOOL CMakeSheetApp::InitInstance())
HANDLE Handle;
Handle = CreateMutex(NULL, TRUE, _T("MakeSheet3.0"));
if (Handle == NULL)
return FALSE;
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox("MakeSheet3.0已运行!", MB_ICONSTOP);
return FALSE;
}
//
//得到System所在目录(为与WindowsNT兼容)
char cSysDir[100], cFileName[] = "//odbcjt32.dll";
GetSystemDirectory(cSysDir, 100);
//连接两个字符串
strcat(cSysDir, cFileName);
//
//给对话框加上位图
//在头文件中定义(public)
CDibApi m_BackBmp;
BOOL CDeviceQueryDlg::OnInitDialog()
{
m_BackBmp.OpenBmp(".//位图.bmp");
m_BackBmp.InitDIBData();
}
void CDeviceQueryDlg::OnPaint()
{
m_BackBmp.DrawDIB(&dc, 0, 0);
}
//------------------------------------------------------------
我也来写一个,最近发生的!
#error : Please use the /MD switch for _AFXDLL builds
/************/
ALT + F7 -> c/c++ -> 分类里选择 Code Generation -> userun-time library 选择
Debug Multihreaded Dll
//------------------------------------------------------------
让静态文本框改变背景
继承CStatic 类,重载 CtlColor
HBRUSH CMyStatic::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
// TODO: Return a non-NULL brush if the parent's handler should not be called
// return NULL;
ASSERT(nCtlColor == CTLCOLOR_STATIC);
pDC->SetBkMode( TRANSPARENT );
return (HBRUSH)GetStockObject( NULL_BRUSH );
}
//------------------------------------------------------------
在两个对话框的控件上传值
在CDialog2中定义一个static的变量m_judge
假设IDC_TEXT对应的变量是m_text则:
if(CDialog1.DoModul()==IDOK)
{
CDialog2::m_judge=m_text;//就可以了
}
//------------------------------------------------------------
微软源码里经常用这些
1.计算数组中元素个数
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
2.错误跳转goto 的代用品
do{
//出错时break;
return TRUE;
}while(1);
//清除工作
...
return FALSE;
//------------------------------------------------------------
关于注册控件
ActiveX控件的文件名最好是短文件名(8.3), 因为在注册表中对应的文件名是短文件名,
若为长文件名,则变成xxx~1,xxx~2等, 这样容易出错!!
如某个控件名本来是MyActiveX.ocx, 结果在注册表中记为MyActi~1.ocx. 做个实验: 注
册
之后,将文件名改为MyActiveX1.ocx, 结果系统还是认为MyActiveX1.ocx就是原来的文件
呢!!
所以容易出错.
ActiveX控件的文件名和路径名最好不要用中文!否则虽能注册成功,但无法使用。
//-----------------------------------------------------------------------------
1) 在View中获得Doc指针
2) 在App中获得MainFrame指针
3) 在View中获得MainFrame指针
4) 获得View(已建立)指针
5) 获得当前文档指针
6) 获得状态栏与工具栏指针
7) 获得状态栏与工具栏变量
8) 在Mainframe获得菜单指针
9) 在任何类中获得应用程序类
10) 从文档类取得视图类的指针(1)
11) 在App中获得文档模板指针
12) 从文档模板获得文档类指针
13) 在文档类中获得文档模板指针
14) 从文档类取得视图类的指针(2)
15) 从一个视图类取得另一视图类的指针
VC中编程对于刚刚开始学习的同学,最大的障碍和问题就是消息机制和指针获取与操作。其
实这些内容基本上是每本VC学习工具书上必讲的内容,而且通过MSDN很多问题都能解决。
下面文字主要是个人在编程中指针使用的一些体会,说的不当的地方请指正。
一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架,无论是多文档还
是单文档,都存在指针获取和操作问题。
下面这节内容主要是一般的框架,然后再讲多线程中的指针使用。使用到的类需要包含响应
的头文件。首先一般获得本类(视,文档,对话框都支持)实例指针this,用this的目的,主
要可以通过类中的函数向其他类或者函数中发指针,以便于在非本类中操作和使用本类中的
功能。
1) 在View中获得Doc指针 CYouSDIDoc *pDoc=GetDocument();一个视只能有一个文档。
2) 在App中获得MainFrame指针
CWinApp 中的 m_pMainWnd变量就是MainFrame的指针
也可以: CMainFrame *pMain =(CMainFrame *)AfxGetMainWnd();
3) 在View中获得MainFrame指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()-
>m_pMainWnd;
4) 获得View(已建立)指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()-
>m_pMainWnd;
CyouView *pView=(CyouView *)pMain->GetActiveView();
5) 获得当前文档指针 CDocument * pCurrentDoc =(CFrameWnd *)m_pMainWnd-
>GetActiveDocument();
6) 获得状态栏与工具栏指针 CStatusBar * pStatusBar=(CStatusBar *)AfxGetMainWnd
()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
CToolBar * pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow
(AFX_IDW_TOOLBAR);
7) 如果框架中加入工具栏和状态栏变量还可以这样 (CMainFrame *)GetParent()-
>m_wndToolBar;
(CMainFrame *)GetParent()->m_wndStatusBar;
8) 在Mainframe获得菜单指针 CMenu *pMenu=m_pMainWnd->GetMenu();
9) 在任何类中获得应用程序类
用MFC全局函数AfxGetApp()获得。
10) 从文档类取得视图类的指针
我是从 http://download.cqcnc.com/soft/program/article/vc/vc405.html学到的,从文档
获得视图类指针目的一般为了控制同一文档的多个视图的定位问题,我的体会特别是文字处
理CEditView当产生多个视图类时,这个功能是非常需要的。
CDocument类提供了两个函数用于视图类的定位:
GetFirstViewPosition()和GetNextView() virtual POSITION GetFirstViewPosition()
const;
virtual CView* GetNextView(POSITION& rPosition) const;
注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。
GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个
POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调用
的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因
此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变
量来辅助操作): CTestView* pTestView;
POSITION pos=GetFirstViewPosition();
pTestView=GetNextView(pos);
这样,便可到了CTestView类的指针pTestView.执行完几句后,变量pos=NULL,因为没有下一
个视图类,自然也没有下一个视图类的POSITION.但是这几条语句太简单,不具有太强的通
用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们
需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,
可用IsKindOf()成员函数时行检查,如: pView->IsKindOf(RUNTIME_CLASS(CTestView));
即可检查pView所指是否是CTestView类。
有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文
档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下: CView*
CTestDoc::GetView(CRuntimeClass* pClass)
{
CView* pView;
POSITION pos=GetFirstViewPosition();
while(pos!=NULL){
pView=GetNextView(pos);
if(!pView->IsKindOf(pClass))
break;
}
if(!pView->IsKindOf(pClass)){
AfxMessageBox("Connt Locate the View./r/n http://www.VCKBASE.com");
return NULL;
}
return pView;
}
其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:
1.pos为NULL,即已经不存在下一个视图类供操作;
2.pView已符合要求。
1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同
时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全有可能既是
pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一个视图类时就如引。
因此需采用两次判断。
使用该函数应遵循如下格式(以取得CTestView指针为例):CTestView* pTestView=
(CTestView*)GetView(RUNTIME_CLASS(CTestView));
RUNTIME_CLASS是一个宏,可以简单地理解它的作用:将类的名字转化为CRuntimeClass为指
针。
至于强制类型转换也是为了安全特性考虑的,因为从同一个基类之间的指针类型是互相兼容
的。这种强制类型转换也许并不必要,但能避免一些可能出现的麻烦。
3.从一个视图类取得另一视图类的指针 综合1和2,很容易得出视图类之间互相获得指针的
方法:就是用文档类作中转,先用1的方法得到文档类的指针,再用2的方法,以文档类的视
图定位函数取得另一个视图类。同样,可以实现成一个函数:
(假设要从CTestAView中取得指向其它视图类的指针)CView* CTestAView::GetView
(CRuntimeClass* pClass)
{
CTestDoc* pDoc=(CTestDoc*)GetDocument();
CView* pView;
POSITION pos=pDoc->GetFirstViewPosition();
while(pos!=NULL){
pView=pDoc->GetNextView(pos);
if(!pView->IsKindOf(pClass))
break;
}
if(!pView->IsKindOf(pClass)){
AfxMessageBox("Connt Locate the View.");
return NULL;
}
return pView;
}
这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在
GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员
函数。
有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如下:CTestBView*
pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));
11)对于单文档中也可以加入多个文档模板,但是一般的开发就使用MDI方式开发多文档模
板,其方法与上述视图的获取方法很接近,这里稍做解释,如果不清楚,请查阅MSDN,(以
下四个内容(11、12、13、14)来源: http://sanjianxia.myrice.com/vc/vc45.htm)
可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位
置;
利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。
POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;
第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检
索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是
模板列表中的最后一个,则pos参数被置为NULL。
//-----------------------------------------------------------------------------
一些初学者的常见问题:
1.如何让编译好的VC程序能在其他未装VC的机器上正常运行:
在编译工具条上选择release,然后Rebuild All.
2.如何使一个按钮变灰:
方法1:GetDlgItem(按钮ID)->EnableWindow(false);
方法2:先将一个控件变量(如m_button1)与欲变灰的按钮进行关联,然后,执行如下代码:
m_button1.EnableWindow(false);
3.如果你在建立了一个工程后,发现你要对一个数据源进行操作,但你又不想重新开一个工
程,方法如下:
a.在菜单上点击"建立类向导"或者"MFC ClassWizard".
b.点击Add Class按钮.
c.输入你的数据库类名,并选择基类为CRecordset,下面的操作想必大家都会了吧。
4.如何使一个菜单变灰:
这个严格说来不能算是一个初级问题了,其实现方法也很简单。
使菜单变灰的实现原理实际上与使按钮变灰的原理是截然不同的方法如下:
使菜单变灰实际上是由编程者事先写好逻辑代码,再将逻辑代码嵌入到菜单项的消息处理
函数UPDATE_COMMAND_UI中,当程序执行时,由程序根据潜入到UPDATE_COMMAND_UI中的代码
来决定是否将菜单项变灰:
a.点击"建立类向导"或者"MFC classWizard"菜单项,在第一选项页Message Maps中的
Object IDs中选择你想变灰的菜单ID,在右边选择UPDATE_COMMAND_UI,点击Add Function
按钮增加消息处理函数,点击Edit Code按钮,在消息处理函数中添加代码如下(我做过的一
个程序):
extern short MyRight;
pCmdUI->Enable(MyRight == 111 || MyRight == 222);
这段代码实际上是根据全局变量MyRight的值来决定是否将此菜单项变灰,如果条件不
成立,菜单项就是灰的。你还可以自己编写全局函数并根据函数的返回值来确定是否变灰。
5.如何解决unexpected end of file问题:
当你在编译一个MFC工程时,如果出现如上提示,并且你确定你的头文件中的{}都是配套
的也没有缺少“;”,你可以在头文件的开头加上如下代码:
#include "stdafx.h"即可解决此问题。
6.如何查一个函数的定义出处?
将光标放到欲查的函数名前,点击菜单上的来源流览器,选择Definitions and
References.
1 RECT, PRECT, NPRECT, LPRECT;它们之间有什么区别?
RECT是一个表示矩形的结构
PRECT表示指向举行结构的指针
NPRECT表示指向举行结构的近指针
LPRECT表示指向举行结构的长指针
最后这3个结构在32位的Windows中都是一样的,在16位的Windows系统中就有区别了,只是
为了与老的16位程序兼容才定义了这些结构指针的,对于现在的程序来说,都是在32位
Windows上进行的开发,所以用那个都一样,就看你的习惯了