MFC学习笔记(五)——InvalidateRect()

    当需要更新窗口的时候可以使用Invalidate()来重绘整个窗口,但是重绘整个窗口计算量是很大的,这会导致屏幕出现闪烁,为了防止这种情况,可以用InvalidateRect()来替代Invalidate(),毕竟计算一个矩形里面要绘制的内容计算量会要小很多。
函数原型:
BOOL InvalidateRect(
 HWND hWnd, //需要重绘的窗口的句柄
 LPCRECT lpRect, //要重绘的矩形区域
 BOOL bErase = TRUE //重绘矩形的方式。
);
    InvalidateRect()的第一个参数是需要重绘的窗口的句柄,但是似乎在CView类及它的派生类中可以不要填写,第二个参数是一个CRect矩形类,矩形的坐标必须是逻辑坐标。第三个参数是重绘矩形的方式,为FALSE是重绘传入的矩形,为TRUE的时候似乎是重绘整个窗口。

关于逻辑坐标(LP – Logical Point)和设备坐标(DP – Device Point)
    对于屏幕,逻辑坐标的原点在最左下角,让整个屏幕在坐标轴的第一象限。设备坐标是在输出设备上定点绘制图形对象是用的,它采用笛卡尔坐标系,原点在屏幕的最左上角,x轴的值自左向右增加,y轴自右向左增加,单位为像素(设备单位)。另外设备坐标是绝对的,逻辑坐标是相对的,可以根据窗口的位置变化来变化的,引用逻辑坐标,就是为了更加直观的来表示屏幕的坐标。还有一种物理坐标,实际上就是设备坐标。

关于逻辑坐标和设备坐标的转换
逻辑坐标转换成设备坐标的函数:
函数原型:
BOOL LptoDP(
 LPPOINT lpPoints, 指向POINT结构数组的指针,每一个POINT结构中的X坐标和Y坐标将被转换
 int nCount, //指定数组中点的数目,不写则为1
 HDC hdc //指向设备环境的句柄。我在写程序的时候,似乎没有写哦,不过也通过了,会不会又是因为写在了CView类里面的原因呢?
);
    如果函数调用成功,返回值为非零值。否则为零。
使用:
CPoint pt(0, 0);
pDC->LPtoDP(&pt);

逻辑坐标转换成设备坐标的函数:
函数原型:
BOOL DptoLP(
 HDC hdc, //指向设备环境的句柄。还是没有写过……-_-!!!…
 LPPOINT lpPoints, //指向POINT结构数组的指针,每个POINT结构中的X和Y坐标将被转换
 int nCount, //规定数组中点的数目,不写则为1
);
    如果函数调用成功,返回值为非零值。否则为零。
使用:
CPoint pt(0, 0);
pDC->DPtoLP(&pt);
    注意,里面接的参数不是矩形,而是CPoint类。

MFC学习笔记(四)——CFile类和CFileDialog结合使用

在MFC中使用CFile类和CFileDialog可以很简单的载入和保存文件……

CFileDialog文件选择对话框的使用:
首先构造一个对象并提供相应的参数,构造函数原型如下:
CFileDialog::CFileDialog(
BOOL bOpenFileDialog, //为TRUE则显示打开对话框,为FALSE则显示保存对话文件对话框
LPCTSTR lpszDefExt = NULL, //默认的文件扩展名
LPCTSTR lpszFileName = NULL, //默认的文件名
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, //设定风格
LPCTSTR lpszFilter = NULL, //列出可供选择的文件类型和相应的扩展名
CWnd* pParentWnd = NULL //父窗口句柄指针
);
 
LPCTSTR lpszFilter 参数格式如:"Chart Files (*.xlc)|*.xlc|Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc;*.xls|All Files (*.*)|*.*||";文件类型说明和扩展名间用 | 分隔,同种类型文件的扩展名间可以用 ; 分割,每种文件类型间用 | 分隔,末尾用 || 指明。

创建文件对话框可以使用DoModal(),在返回后可以利用下面的函数得到用户选择:
CString CFileDialog::GetPathName( ) 得到完整的文件名,包括目录名和扩展名如:c:\test\test1.txt
CString CFileDialog::GetFileName( ) 得到完整的文件名,包括扩展名如:test1.txt
CString CFileDialog::GetExtName( ) 得到完整的文件扩展名,如:txt
CString CFileDialog::GetFileTitle ( ) 得到完整的文件名,不包括目录名和扩展名如:test1
POSITION CFileDialog::GetStartPosition( ) 对于选择了多个文件的情况得到第一个文件位置。
CString CFileDialog::GetNextPathName( POSITION& pos ) 对于选择了多个文件的情况得到下一个文件位置,并同时返回当前文件名。但必须已经调用过POSITION CFileDialog::GetStartPosition( )来得到最初的POSITION变量。

下面是实现代码:
//打开文件
//创建文件名缓存(fileBuffer)和文件读取缓存(rBuffer)
//此处如果不把缓存置0,在后面的设定中就会出错,如果是动态申请的,请用memset或者for循环把数组清零
char fileBuffer[5010] = {0}, rBuffer[5010] = {0};
int rBSize;
//定义一个CFile类,用来处理文件
CFile in;
//定义一个CFileDialog类,用来显示标准的打开文件对话框
CFileDialog inDlg(FALSE, NULL, NULL, NULL, _T("自定义文件类型 (*.xxx)|*.xxx|所有文件 (*.*)|*.*||"), NULL);
inDlg.m_ofn.lpstrTitle = _T("打开自定义文件");
//设定一个文件名缓存,因为CFileDialog内置的文件名缓存长度只有200,但是很多时候,文件的路径远大于这个数,为了保险起见,所以要自己设定一个文件名缓存
inDlg.m_ofn.lpstrFile = fileBuffer;
//设定缓存长度
inDlg.m_ofn.nMaxFile = 5000;
//显示对话框,并处理按了确定按钮的事件
if(inDlg.DoModal() == IDOK) {
 //以只读方式打开文件
 if(in.Open(inDlg.GetPathName(), CFile::modeRead)) {
  //得到文件长度,从而确定读取缓存的大小
  rBSize = in.GetLength();
  //避免数组越界,当然在这里也可以动态申请缓存空间
  if(rBSize > 5000) rBSize = 5000;
  //读入数据
  n.Read(rBuffer, rBSize);
  //关闭文件
  in.Close();
  MessageBox("打开文件成功", "搞定了", MB_ICONEXCLAMATION | MB_OK);
 } else {
  MessageBox("打开文件失败", "失败了", MB_ICONSTOP | MB_OK);
 }
}

//保存文件
//创建文件名缓存(fileBuffer)和文件写入缓存(wBuffer)
//此处如果不把缓存置0,在后面的设定中就会出错,如果是动态申请的,请用memset或者for循环把数组清零
char fileBuffer[5010] = {0}, wBuffer[5010] = {0};
int wBSize;
CFile out;
CFileDialog outDlg(FALSE, NULL, NULL, NULL, _T("自定义文件类型 (*.xxx)|*.xxx|所有文件 (*.*)|*.*||"), NULL);
//设定保存对话框标题
outDlg.m_ofn.lpstrTitle = _T("保存自定义文件");
//使用自定义的文件名缓存
outDlg.m_ofn.lpstrFile = fileBuffer;
//设定保存文件的默认后缀,如果自己有输入后缀,则时候输入的后缀
outDlg.m_ofn.lpstrDefExt = "xxx";
//设定缓存大小
outDlg.m_ofn.nMaxFile = 5000;
//显示对话框
if(outDlg.DoModal() == IDOK) {
 //得到文件名,并用创建和写入方式打开
 if(out.Open(outDlg.GetPathName(), CFile::modeWrite | CFile::modeCreate)) {
  //在这里把你要写入文件的内容写入缓存,如果写入的内容过长,可以分次写入
  //得到写入当前缓存中内容的长度
  wBSize = strlen(wBuffer);
  //写入文件
  out.Write(wBuffer, wBSize);
  //关闭文件
  out.Close();
  MessageBox("保存文件成功", "搞定了", MB_ICONEXCLAMATION | MB_OK);
 } else {
  MessageBox("保存文件失败", "失败了", MB_ICONSTOP | MB_OK);
 }
}

MFC学习笔记(三)——CBrush

    CBrush::CreateSolidBrush好变态,虽然可以用RGB(int R, int G, int B)来生成参数,不过如果直接写个数值进去,格式居然是0×00BBGGRR,RGB的顺序是BGR?!我晕倒,什么时候这个也是按字母序的了?

MFC学习笔记(二)——动态更改菜单

    在更改菜单之前,首先要把CMainFrame:: m_bAutoMenuEnable设为FALSE,不然就无法自己更改菜单的样式,而是被MFC自动的设为启动。
    然后用AfxGetMainWnd()得到主窗口的句柄,再调用GetMenu,得到主菜单的指针,但是如果直接用AfxGetMainWnd()->GetMenu()就会出错,因为用AfxGetMainWnd()得到的是CFrameWnd或其它,是CMainFrame的父类,要把把强化转化为当前的框架类CMainFrame才可以再获得菜单等其他资源。
CMenu * pmenu = ((CMainFrame *)AfxGetMainWnd())->GetMenu();
    现在pmenu获得的主菜单是整个菜单项,然后用它来调用GetSubMenu()来获取其中的子菜单。自菜单的序号是从0开始的。
CMenu * psub = pmenu->GetSubMenu(0);
    之后就可以用psub来更改子菜单中的按钮的状态了,方法是:
psub->EnableMenuItem(
UINT uIDEnableItem, //菜单项标识
UINT uEnable //控制标志
);

    个人觉得常用的控制标志只有3个:
MF_DISABLED —— 禁止
MF_ENABLED ——允许
MF_GRAYED —— 变灰
    比如要让打开菜单项(标志为ID_FILE_OPEN)变灰并且禁用(其实纯变灰了就不能用了),那么语句就是psub->EnableMenuItem(ID_FILE_OPEN, MF_GRAYED | MF_DISABLED);

MFC学习笔记(一)——MessageBox的使用

    MessageBox这个函数可以在VC里面显示一个标准对话框。

函数原型:
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT, uType);
参数含义:
HWND hWnd —— 父窗口的句柄。(在使用的时候发现在CView类里面不要填这一项,但是如果是再其他的类中,就要填写)
LPCTSTR lpText —— 对话框的内容
LPCTSTR lpCaption —— 对话框的标题
UINT uType ——窗口的风格
风格:
MB_DEFBUTTON1 —— 缺省按钮为第一个按钮
MB_DEFBUTTON2 —— 缺省按钮为第二个按钮
MB_DEFBUTTON3 —— 缺省按钮为第三个按钮
MB_ICONEXCLAMATION —— 显示图标为惊叹号
MB_ICONQUESTION —— 显示图标为问号
MB_ICONSTOP —— 显示图标为红叉叉
MB_ABORTRETRYIGNORE —— 显示终止、重试、忽略按钮
MB_OK —— 只显示确定按钮
MB_OKCANCEL —— 显示确定和取消按钮
MB_RETRYCANCEL —— 显示重试和取消按钮
同时应用多个风格的时候用|连接。
返回值:
IDABORT —— 按下了终止按钮
IDRETRY —— 按下了重试按钮
IDIGNORE —— 按下了忽略按钮
IDOK —— 按下了确定按钮
IDCANSEL —— 按下了取消按钮
IDYES —— 按下了是按钮
IDNO —— 按下了否按钮

    比如要显示一个带有红叉图标的按钮为终止、重试、忽略,并且默认按钮为重试,标题为“我顶”,内容为“我顶你个肺”的对话框,并且还要求如果单击了终止,则给a赋值为1,那么语句则为:
if(MessageBox("我顶你个肺", "我顶", MB_ABORTRETRYIGNORE |  MB_DEFBUTTON2 | MB_ICONSTOP) == IDABORT) {
a = 1;
}