<script type=text/javascript> function showFrameMenu(p_el) { if ((typeof(Spaces) != "undefined") && Spaces.PartFrame) { Spaces.PartFrame.showMenu(p_el, 'http/x3a/x2f/x2fspaces.live.com/x2fapi.aspx/x3fwx_action/x3daddModule/x26mkt/x3dzh-CN/x26wx_targetSite/x3dPersonalSpaces/x26wxp_url/x3d/x7b0/x7d'); } return false; } var g_siteType = "PersonalSpace"; </script>
<script language=javascript> ; </script>
|
1, 指向常量的指针&&指针常量
Char ch[5]=”lisi”; Const char * pStr=ch; const char *等同char const * Char * const *pStr=ch; 指针是常量,指针不可更改,其内容可更改
2, 读写
文件读取操作 FILE *pFile=fopen("1.txt","r");
char ch[100]="0"; //数组被赋值为全零 memset(ch,0,100);//等同于上一句?
//char ch[100]; //如果不把数组赋零,也可以在写入文件中多写一个空字符 如fwrite("I Love You",1,strlen("I Love You")+1,pFile); //memset(ch,0,100); 把数组赋为全零的另一种方法。 fread(ch,1,100,pFile); fflush(pFile);
3, 获取文件大小
fseek(pFile,0,SEEK_END); //把文件指针移到文件末尾 int n=ftell(pFile); //得到文件长度
rewind(pFile); //把指针移回文件头 fseek(pFile,0,SEEK_BEGIN) pbuf=new char[n+1]; pbuf[n]=0; //文件尾加/0作为文件结束符 fread(pbuf,1,n,pFile);
4, 文本和二进制方式。读取和写入的保持一致 文本:写入, 换行(10)è回车--换行(ASCII为13、10) 读取, 回车--换行(ASCII 13、10)è换行(10)
二进制:将数据在内存中的存储形式原样输出到文件中 不管是文本文件还是二进制文件,都可以用文本方式或者二进制方式中的任意一种打开
5, 字符和数字
FILE *pFile=fopen("2.txt","w");
int i=98341; //非要使他可用,可增加itoa(i,ch,10); fwrite(&i,4,1,pFile);
6, C++中文件操作
需要加头文件#include "fstream.h" ofstream os("3.txt"); os.write("I love you!",strlen("I love you!")); os.close();
读文件:
ifstream ifs("3.txt",ios::in); char ch[100]="0"; memset(ch,0,100) ifs.read(ch,100); ifs.close();
//循环读取文件每一行
While(!ifs.getline(ch,100).eof())
{//do something with data in buffer ch
}
下一次重新getline之前,需要 ifs.clear()清除eof标志
7, Win32API函数存取文件
(1)写入 HANDLE hfile; hfile=CreateFile("6.txt",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); DWORD dwWrites; WriteFile(hfile,"I Love You!(5)",strlen("I Love You!(5)"),&dwWrites,NULL); CloseHandle(hfile); (2)读取 HANDLE hfile; hfile=CreateFile("6.txt",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); char ch[100]="0"; //如果只写char ch[dwRead-1];可以在之后用ch[dwRead]=0设结束符 DWORD dwRead; ReadFile(hfile,ch,100,&dwRead,NULL); ch[dwRead]=0; CloseHandle(hfile);
8, SDK方法
(1)写入:
CFile file("7.txt",CFile::modeCreate | CFile::modeWrite); file.Write("I Love You 1000",strlen("I Love You 1000")); file.Close();
(2)读取:
CFile file("7.txt",CFile::modeRead); char *pBuf; DWORD i=file.GetLength();
pBuf=new char[i+1]; file.Read(pBuf,i);
pBuf[i]=0;
file.Close();
9, 构造文件对话框,存取文件方法
(1)写入:
CFileDialog fileDlg(FALSE); fileDlg.m_ofn.lpstrTitle="我的文件保存对话框"; fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)/0*.txt/0All Files(*.*)/0*.*/0/0"; //注意lpstrFilter的构造:每个段落后边都要加/0,末尾要加两个/0,括号里的只是显示,实//现在紧跟着的/0后边,此过滤器只为过滤可见文件使用,并不能按所见格式保存。 fileDlg.m_ofn.lpstrDefExt="txt"; if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeCreate | CFile::modeWrite); file.Write("I Love You 1000",strlen("I Love You 1000")); file.Close(); }
(2)读取
CFileDialog fileDlg(TRUE); fileDlg.m_ofn.lpstrTitle="我的文件打开对话框"; fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)/0*.txt/0All Files(*.*)/0*.*/0/0"; if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeRead); char *pbuf; DWORD i=file.GetLength(); pbuf=new char[i+1]; //动态建立缓冲区,值得学习 pbuf[i]=0; file.Read(pbuf,i); MessageBox(pbuf); file.Close();
}
10, 读写配置文件
CXXXApp::InitInstance(){
// 写在SetRegistryKey(_T("Local AppWizard-Generated Applications"));之后(也可以重新设置表项)
::WriteProfileString("songpeng","sp","song");用来在C:/WINDOWS/win.ini中写入数据。一方面为了兼容十六位程序,另一方面提高程序运行速度 //在win32中为[HKEY_CURRENT_USER]è[Software]è[Local Appwizard-Generated Applications]è[File]
CString str; ::GetProfileString("songpeng","sp","peng",str.GetBuffer(100),100);
}
11, 读写注册表
读写配置文件的函数WriteProfileString(),GetProfileString()在win32下自动成为注册表的读写。
SetRegistryKey(_T("Local AppWizard-Generated Applications"));用来在注册表的HKEY_CURRENT_USER->Software下增加主键Local AppWizard-Generated Applications 子键及值由WriteProfileString("songpeng","sp","song");增加
(1)写入注册表:
HKEY hkey; RegCreateKey(HKEY_LOCAL_MACHINE,"Software//MyItem//Name",&hkey); RegSetValue(hkey,NULL,REG_SZ,"song",strlen("song")); RegSetValueEx(hKey,”age”,0,REG_DWORD,(CONST BYTE*)&dwAge,4);
RegCloseKey(hkey);
(2)读取注册表
注意要先获取字段大小
LONG lValue; RegQueryValue(HKEY_LOCAL_MACHINE,"Software//MyItem//Name",NULL,&lValue); char *buf=new char[lValue]; //注意建立缓冲区方法 RegQueryValue(HKEY_LOCAL_MACHINE,"Software// MyItem//Name",buf,&lValue);
LONG RegQueryValue( HKEY hKey, // handle to key to query LPCTSTR lpSubKey, // name of subkey to query LPTSTR lpValue, // buffer for returned string PLONG lpcbValue // receives size of returned string ); If lpValue is NULL, and lpcbValue is non-NULL, the function returns ERROR_SUCCESS, and stores the size of the data, in bytes, in the variable pointed to by lpcbValue. This lets an application determine the best way to allocate a buffer for the value's data. 所以,我们要调用两次RegQueryValue,第一次查询键值长度,第二次获得键值
HKEY hkey; RegOpenKey(HKEY_LOCAL_MACHINE, "Software//MyItem//Name",&hkey); //打开主键 DWORD dwType; DWORD dwAge; DWORD dwValue; RegQueryValueEx(hkey,"age",0,&dwType,(LPBYTE)&dwAge,&dwValue);
其他函数:
RegDeleteKey(); RegDeleteValue();等
第12课 文件操作
1.常量指针与指针常量的区分 char ch[5]="lisi"; const char *pStr=ch;//const在*之前,表明指针指向的内容为常量,即为常量指针 char * const pStr=ch;//const在*之后,表明指针的地址不能改变,即为指针常量 明白? 2.对文件读写的三种方法 1.C中 FILE *pFile=fopen("1.txt","w"); fwrite("http://www.sunxin.org",1,strlen("http://www.sunxin.org"),pFile); //fseek(pFile,0,SEEK_SET); //fwrite("ftp:",1,strlen("ftp:"),pFile); //fwrite("http://www.sunxin.org",1,strlen("http://www.sunxin.org"),pFile); fclose(pFile);*/ //fflush(pFile); 2.C++中 /* ofstream ofs("4.txt"); ofs.write("http://www.sunxin.org",strlen("http://www.sunxin.org")); ofs.close();*/ 要包括头文件 "fstream.h" 3.MFC中 用CFile类,哈哈!简单好用 CFileDialog fileDlg(FALSE); fileDlg.m_ofn.lpstrTitle="我的文件保存对话框"; fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)/0*.txt/0All Files(*.*)/0*.*/0/0"; fileDlg.m_ofn.lpstrDefExt="txt"; if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeCreate | CFile::modeWrite); file.Write("http://www.sunxin.org",strlen("http://www.sunxin.org")); file.Close(); } 4.利用win32 API函数 CreateFile(),及WriteFile() 4.注册表读写 1.对win.ini的读写 //::WriteProfileString("http://www.sunxin.org","admin","zhangsan"); /* CString str; ::GetProfileString("http://www.sunxin.org","admin","lisi", str.GetBuffer(100),100); AfxMessageBox(str);*/ 2.注册表的读写 HKEY hKey; DWORD dwAge=30; RegCreateKey(HKEY_LOCAL_MACHINE,"Software//http://www.sunxin.org//admin",&hKey); RegSetValue(hKey,NULL,REG_SZ,"zhangsan",strlen("zhangsan")); RegSetValueEx(hKey,"age",0,REG_DWORD,(CONST BYTE*)&dwAge,4); RegCloseKey(hKey);以上是写入 代码比较简单,不再详细介绍。
◆1. 指向常量的指针 它指向的内容是常量,不能修改;但是指针本身存放的是指针值,可以修改。 char ch[5]="list" 这种赋值形式只能在数组定义的同时进行。 常量字符串会自动加上/0,所以字符串长度为五 通常,如果想用指针传参,通常会将形参声明的指向常量的指针类型 当实参传入之后,形参无法修改实参指向的内容,从而保证了数据的一致性 ------------------------------------------------------------------------ ◆指针常量 与指向常量的指针相反,指针本身是一个常量不可修改;而它指向内容可以修改 文件的读写分为二进制与文本方式,它们是有区别的 ------------------------------------------------------------------------ ◆文件是在计算机内存中以二进制表示的数据在外部存储介质上的另一种存放形式 文件通常分为二进制文件和文本文件 二进制文件是包含在ASCII及扩展ASCII字符中编写的数据或程序指令的文件。 一般是可执行程序,图形,图像,声音等文件。 文本文件(也称为ASCII文件): 它的每一个字节存放的是可表示为一个字符的ASCII代码的文件。 它是以“行”为基本结构的一种信息组织和存储方式的文件, 可用任何文字处理程序阅读的简单文本文件 如果以文本方式存储三个字符到文件中,其中有一个是换行符,文件会自动增加一个回车符,再以文本方式读取时仍然是三个字符,但以二进制方式读取时是四个字符 ASCII码 10为换行符 13为回车符 ------------------------------------------------------------------------ ◆文本方式和二进制方式 当我们按照文本方式王文件中写入数据时,一旦遇到换行字符(ASCII为10), 则会转换为回车--换行(ASCII为13、10)。在读取文件时,一旦遇到回车--换行 的组合(即连续的ASCII13、10),则会转换为换行字符(ASCII为10)。 当我们按照二进制方式往文件中写入数据,则将数据在内存中的存储形式原样输出到文件中 如果以二进制方式存储一个换行符,文件不会自动增加一个回车符,而上直存储ASCII码10,当以文本方式读取二进制存储的换行符时,我们看到的是一个小黑块。 ------------------------------------------------------------------------ 所以最好用与保存文件时一样的方式来读取文件。 ◆一个题目: 要将98341写入文件,并在记事本中原样输出数字. //要将98341写入文件,并在记事本中原样输出数字。 //直接将一个整形数据98341写入文件读取出来将是乱码 //如果将整形数据用itoa(i,ch,10)转换成字符之后就OK int i=98341; fwrite(&i,4,1,pFile); //其实在文件中,都是以ASCII码写入的, //可以将作为一个字符串保存到文件 CString str="98341"; fwrite(str,1,strlen(str),pFile); //如果对ACSII码比较熟悉,用下面的办法也可以 char ch[5]; ch[0]=9+48; ch[1]=8+48; ch[2]=3+48; ch[3]=4+48; ch[4]=1+48; fwrite(ch,1,5,pFile); fclose(pFile);
2.
C语言中文件写入操作 FILE *pFile=fopen("1.txt","w"); //w方式,会销毁打开文件中原先的内容 fwrite("I Love You",1,strlen("I Love You"),pFile); //写入数据 --------------------------------------------------------------------------------- 注意:在C语言中,所有读写的文件都是先保存在缓冲区,等缓冲区满后才保存入文件。 要提前终止缓冲区使马上保存文件,可以用fclose或fflush。 fclose(pFile); 功能是关闭文件,结束缓冲区,将缓冲区内容保存到文件 fflush(pFile); 用来刷新缓冲区,让缓冲区数据写入文件中 --------------------------------------------------------------------------------- 文件中有一个文件指针,它始终指向下一个需要写入位置。 当写入一个字符之后,它会自动指向下一个写入位置。 Fseek(FILE *stream/*文件指针*/, long offset/*偏移量*/, int origin/*起始位置*/); ---------------------------------------------------------------------------------
C语言中文件读取操作 size_t fread( void* buffer, size_t size, size_t count, FILE* stream ); void *memset( void* dest, int c, size_t count ); --------------------------------------------------------------------------------- 如何根据文件长度分配内存容量 1.首先要获得文件长度 我们可以先将文件指针移动到文件尾部,然后用ftell得到文件指针的位置,即是文件长度 2.根据文件长度,用new分配内存
3. C++读写文件方法 在C++中,可以用ofstream类来写入文件,这个类的头文件是 <fstream.h> 可以用ifstream类来读取文件,ifstream类的用法与ofstream类似
4. 用Win32API函数操作文件: 1.保存文件 首先用CreateFile 打开文件,然后调用WriteFile将数据写入到文件当中。 2.读取文件 首先用CreateFile 打开文件,然后调用ReadFile读取数据到ch字符数组中。 --------------------------------------------------------------------------------- MFC中操作文件的类Cfile 1.写入文件 首先构造一个CFile对象:CFile file("CFile.txt",CFile::modeCreate | CFile::modeWrite); 然后调用CFile对象的Write方法写入数据: file.Write("teshorse@hotmail.com", strlen("teshorse@hotmail.com")); 最后关闭文件:file.Close(); 2.读取文件 首先构造一个CFile对象:CFile file("CFile.txt",CFile::modeRead); 然后创建一个字符指针pBuf,用来接收读取数据;创建一个DWORD变量存实际放读取的数量dwFileLen 接着根据dwFileLen用new char[dwFileLen+1] 为字符指针pBuf分配内存。 这里就可以调用CFile对象的Read方法读取数据了: file.Read(pBuf,dwFileLen); 最后关闭文件:file.Close(); 在VC中,这种方法非常方便,在开发VC软件牵涉到文件操作时,推荐用这种方法。 ---------------------------------------------------------------------------------
增加“打开”和“保存”对话框 用CFileDialog类就可以完成打开和保存对话框 “打开”对话框的创建步骤: 1.先构造一个CFileDialog对象:CFileDialog fileDlg(FALSE); 2.显示对话框:fileDlg.DoModal(); 若要改变对话框的各种属性,可以修改CFileDialog类的数据成员m_ofn , 它是OPENFILENAME结构体变量 “保存”对话框的创建步骤: 1.先构造一个CFileDialog对象:CFileDialog fileDlg(TRUE); 2.设置对话框的属性,如标题、过滤器 3.用if(IDOK==fileDlg.DoModal()){}显示对话框窗口 4.在if体内,写入读取文件的代码,与前面的方法完全一样
5. 往win.ini的文件中写入数据
在实际开发程序时,我们经常要把一些初始化信息写入到一个配置文件中,当程序启动的时候读入这些初始化的信息。早期的软件会将这些信息写入一个叫 win.ini的文件中(当然现有很多软件将这些信息写到注册表中)。如果我们也想在win.ini文件中写入信息,可以利用 WriteProfileString函数。
我们在CFileApp::InitInstance()函数中,调用WriteProfileString函数往win.ini的文件中写入一些数据 CFileApp::InitInstance() 是程序初始化时需要调用的虚函数。只要在CFileApp::InitInstance()找个位置写下一条代码:::WriteProfileString("MyProgram","admin","teshorse");
相应地,要从win.ini文件中获取数据,可以调用GetProfileString函数。 --------------------------------------------------------------------------------- 上面的GetProfileString与WriteProfileString是API函数,CWinApp类中也有这两个函数。 不同的是,CWinApp::WriteProfileString 在Windows NT 操作系统中,将值保存在注册表中。 --------------------------------------------------------------------------------- CFileApp::InitInstance()函数中发现有一条代码: SetRegistryKey(_T("Local AppWizard-Generated Applications")); 用来在注册表的HKEY_CURRENT_USER->Software下 增加主键Local AppWizard-Generated Applications 子键及值调用WriteProfileString("MyProgram","admin","teshorse")增加
6. 修改注册表 对注册表的操作是通过几个函数完成的。 在注册表中,值包含三个部分:值的名称,值的类型,值的本身即数据 使用RegSetValue()向注册表写入数据:
注意:RegCreateKey只能建立REG_SZ型数据,要建立其他数据需要调用RegCreateKeyEx
使用RegSetValueEx()向注册表写入数据: 使用RegQueryValue读取注册表: 要获得其他数据类型数据,调用RegQueryValueEx 使用RegQueryValueEx读取注册表
第11课 如何让CDC上输出的文字、图形具有保持功能
1.创建4个菜单,为其添加消息响应,用成员变量保存绘画类型。添加LButtonDown和Up消息。 2.当窗口重绘时,如果想再显示原先画的数据,则需要保存数据。为此创建一个新类来记录绘画类型和两个点。 class CGraph { public: CPoint m_ptOrigin;//起点 CPoint m_ptEnd;//终点 UINT m_nDrawType;//绘画类型 CGraph(); CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);//此为构造函数。 virtual ~CGraph();
}; 然后在void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)中加入如下代码 //CGraph graph(m_nDrawType,m_ptOrigin,point);//不能用局部变量 //m_ptrArray.Add(&graph);//加入这种指针数组中 /* OnPrepareDC(&dc);//这个函数中可以重新设置窗口原点,对于滚动条中,保存数据前要调用此函数 dc.DPtoLP(&m_ptOrigin);//将设备坐标转换为逻辑坐标 dc.DPtoLP(&point);// CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);//在堆中创建新的对象 m_ptrArray.Add(pGraph);*///加入到指针数组中 在GraphicView.h中有如下代码 CPtrArray m_ptrArray; 在OnDraw中重画时调出数据 for(int i=0;i<m_ptrArray.GetSize();i++) 3.在CView::OnPaint()调用了OnDraw(),但在void CGraphicView::OnPaint()中MFC的Wizard没有调用OnDraw(),要注意这个区别。如果你此时想调用,必须手动添加代码。 OnDraw(&dc);
实际上也是调用了BeginPaint和EndPaint
如果在CGraphicView里面捕获了OnPaint,必须在里面调用OnDraw函数
void CGraphicView::OnPaint()
{
CPaintDC dc(this);
OnPrepareDC(&dc);//坐标转换-->-->-->-->-->
OnDraw(&dc);
} 4.让窗口具有滚动条的功能。 第1.将CGraphicView的头文件中的CView全部替换成CSrollView 第2.添加如下的代码 void CGraphicView::OnInitialUpdate() { CScrollView::OnInitialUpdate();
// TOD Add your specialized code here and/or call the base class SetScrollSizes(MM_TEXT,CSize(800,600));//设置映射模式,设定窗口大小。OK! }
5.坐标系的转换,此处不再详细介绍,需要时请查阅相关资料。
6.解决重绘时线跑到上面的问题。为什么会错位?因为逻辑坐标和设备坐标没有对应起来。
在CScrollView::OnInitialUpdate()函数中: ptVpOrg=-GetDeviceScrollPosition();
pDC->SetViewportOrg(ptVpOrg); 解决方法: 在OnLButtonDown画完图后,保存之前。调用 /* OnPrepareDC(&dc);//重新设置逻辑坐标的原点!!! dc.DPtoLP(&m_ptOrigin);//设备坐标转化为逻辑坐标 dc.DPtoLP(&point); CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point); m_ptrArray.Add(pGraph);*/ 7.另外两种方法来保存数据。 一种是用CMetaFileDC,另一种是利用兼容DC,重绘时利用 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY); 将兼容DC的图拷贝到屏幕DC上去。 此处不再详细介绍这两种方法,因为介绍多了容易搞晕。呵呵
孙鑫VC学习笔记:第十一讲 坐标空间与各种转换的概念
介绍一下坐标空间、逻辑空间、映射方式等概念 MicroSoft Windows 下的程序运用坐标空间和转换来对图形输出进行缩放,平移,旋转,斜切和反射。 一个坐标空间是一个二维空间,通过使用连个相互垂直并且长度相等的轴来定义二维对象
坐标空间 API使用四种坐标空间:世界坐标系空间,页面空间,设备空间,和物理设备空间。基于Win32的应用程序运用世界坐标系空间对图形输出进行旋转、斜切或者反射。
Win32 API把世界坐标系空间和页面空间称为逻辑空间;最后一种坐标空间(即物理设备空间)通常指应用程序窗口的客户区;但是他也包括整个桌面、完整的窗口(包括框架、标题栏和菜单栏)或打印机的一页或绘图仪的一页纸。物理设备的尺寸随显示器、打印机、绘图仪的所设置的尺寸而改变。
转换 如果要在一个物理设备上绘制输出,Windows把一个矩形区域从一个坐标空间拷贝到(或映射到)另一个坐标空间,直至最终完整的输出呈现在物理设备上(通常是屏幕或打印机)
如果该应用程序调用了SetWorldTransForm函数,那么映射就从应用程序的世界坐标系空间开始;否则,映射在页面空间中进行。在 Windows把矩形区域的每一点从一个空间拷贝到另一个空间时,他采用一种被称作转换的算法,转换是把对象从一个坐标空间拷贝到另一个坐标空间时改变(或转变)这一对象的大小,方位,和形态,尽管转换把对象看成一个整体,但他也作用与对象的每一个点或每一条线。
--------------------------------------------------------------------------------- 页面空间到设备空间的转换 页面空见到设备空间的转换是原Windows程序接口的一部分。这种转换确定与一特定设备描述表相关的所有图形输出的映射方式。 所谓映射方式是指确定用于绘图操作的单位大小的一种量度转换,映射方式是一种影响几乎任何客户区绘图的设备环境属性。另外还有四种设备环境属性:窗口原点,视口原点,窗口范围和视口范围,这四种属性与映射关系密切相关。
页面空间到设备空间的转换所用的是两个矩形的宽与高的比率,其中页面空间中的矩形被称为窗口,设备空间中的矩形被称为视口。Windows把窗口原点映射到视口原点,把窗口范围映射到视口范围,就完成了这种转换。
--------------------------------------------------------------------------------- 设备空间到物理空间的转换 设备空间到物理空间的转换:只限于平移,并由Windows窗口管理部分控制,这种转换的唯一用途是确保设备空间的原点被映射到物理设备上的适当点上。没有函数能设置这种转换,也没有函数能获取有关数据。 所以通常我们所要考虑的是从页面空间到设备空间的转换。页面空间通常称为逻辑空间。
--------------------------------------------------------------------------------- 默认转换 一旦应用程序建立了设备描述表,并立即开始调用GDI绘图或输出函数,则运用默认页面空间到设备空间的转换和设备空间到客户区的转换,(在应用程序调用SetWorldTransform之前不会世界坐标空间到页面空间的转换。
默认页面空间到设备空间的转换是一对一的映射;即页面空间上给出的一点映射到设备空间上的一个点。这种转换没有以矩阵指定,而是通过把视口宽除以窗口宽,把视口高除以窗口高而得出的,在默认情况下,视口尺寸为1*1像素,窗口尺寸为1*1页单位。
设备空间到物理设备(客户区,桌面和打印机),得转换结果总是一对一的;既设备空间上的一个单位总是与客户区,桌面,和打印机上的一个单位对应。这一转换的唯一用途是平移,无论窗口移到桌面的什么位置,它永远取保输出能够正确无误地出现在窗口上。
默认装换的一个独特之处是设备空间与应用程序窗口的y轴方向。 在默认的状态下,y轴正向朝下,-y方向朝上。
--------------------------------------------------------------------------------- 逻辑坐标和设备坐标 几乎在所有GDI函数中使用的坐标值都是逻辑单位,Windows必须将逻辑坐标值转换为“设备单位”, 即像素。这种转换是由映射方式,窗口和视口的原点以及窗口和视口的范围决定的。 Windows对所有消息(如WM_SIZE,WM_MOUSEMOVE,WM_LBUTTONDOWN,WM_LBUTTONUP),所有的非GDI函数和一些GDI函数(GetDeviceCaps函数(获取设备的范围)),永远使用设备坐标。
窗口是基于逻辑坐标的,逻辑坐标可以是像素,毫米,英寸等单位,使口是基于设备坐标(像素)的。通常,视口和客户区是相同的。 缺省的映射模式是MM_TEXT,在这种映射模式下,逻辑单位和设备单位相同。
--------------------------------------------------------------------------------- 窗口(逻辑)坐标和视口(设备)坐标的转换 xViewPort=(xWindow-xWinOrg)*xViewExt/xWinExt+xViewOrg; yViewPort=(yWindow-yWinOrg)*yViewExt/yWinExt+yViewOrg; 视口(设备)坐标和窗口(逻辑)坐标的转换与上面相反; --------------------------------------------------------------------------------- 在MM_TEXT映射方式下窗口(逻辑)坐标和视口(设备)坐标的转换: xViewPort=xWindow-xWinOrg+xViewOrg; yViewPort=yWindow-yWinOrg+yViewOrg; 视口(设备)坐标和窗口(逻辑)坐标的转换与上面相反; --------------------------------------------------------------------------------- CDC中提供两个成员函数SetViewpoitOrg和SetWindowOrg,用来改变视口和窗口的原点。 如果将视口原点设为(xViewOrg,yViewOrg),则逻辑点(0,0)就会被映射为设备点(xViewOrg,yViewOrg),如果将窗口原点改变为(xWinOrg,yWinOrg),则逻辑点(xWinOrg,yWinOrg)就会被映射为设备点(0,0), 即左上角。 不管对窗口和视口原点作什么改变,设备点(0,0)始终是客户区的左上角。
---------------------------------------------------------------------------------
图形的保存和重绘 编写画图代码,设定一个标识,在OnLButtonDown中保存鼠标按下去的点,在OnLButtonUp中捕获鼠标弹起的点,利用switch语句分别画图。这是上节课的内容,上节课还讲了窗口重绘的原理,实际上分为两步, 首先擦除以前的背景,然后再进行窗口重绘。 所以当拖动窗口改变窗口大小时,窗口要发生重绘,首先会擦除以前的背景,于是先前所画图像会消失。
解决办法是将画图代码写在OnDraw()函数中,窗口每次重绘都会调用该函数重新绘制图像。 但是怎么保存每次重绘图像需要的代码呢? 事实上,要画的图形由三个因素决定: 1.图形类型(点、直线、矩形、椭圆) 2.图形绘制初始点 3.图形绘制终点 所以我们可以用数组类保存三个变量:m_nDrawType,m_ptOrigin,m_ptEnd。
首先需要新建一个Class type为Generic Class的名为CGraph类, 在CGraph中增加三个成员变量UINT m_nDrawType,CPoint m_pOrigin,CPoint m_pEnd, 并在不带参数构造函数中将三个成员变量初始化。 然后构造一个带参数的构造函数 CGraph(UINT m_nDrawType,CPoint m_pOrigin,CPoint m_pEnd); 用来接收三个变量值,代码如下:
CGraph::CGraph(UINT m_nDrawType,CPoint m_pOrigin,CPoint m_pEnd) { this->m_nDrawType=m_nDrawType; this->m_pOrigin=m_pOrigin; this->m_pEnd=m_pEnd; } 至此,我们可以用CGraph类保存绘制一个图形的三要素,根据这三要素可以绘制我们所作的图形。 -------------------------------------------------------------------------------- 但是如果我们绘制了多个图形怎么办? 如果用CGraph类对象的一个数组,那么它只能保存有限个对象, 而我们在绘制图形的时候,并不能限定只能绘制多少个图形。 用链表可以实现动态存储,但是操作十分复杂, 于是我们想到了MFC提供一集合类,如前面用到过的CStringArray,可以动态存储CString对象 这里要用到的是另一个集合类,CPtrArray,它用来动态保存CGraph对象。 (它的用法大家可以参见 MSDN或右边。)
于是在OnLButtonUp函数中画完图形之后 ,我们想创建一个CGraph对象, 把画这个图形所必须的三要素保存到这个对象之中。 如果我们直接在尾部增加下面两行代码: graph(m_nDrawType,m_ptOrigin,point) m_ptrArray.Add(&graph); 但是运行程序之后,我们所绘制的图形没能在OnDraw函数中重绘。 这是因为我们在OnLButtonUp函数中创建的CGraph对象在本函数结束时被销毁,内存被回收。 --------------------------------------------------------------------------------- 解决办法: 在OnLButtonUp函数中创建的CGraph对象指针,然后用new方法为它在堆中分配内存。 因为new方法分配的内存在堆中,除非显式地调用Delete方法去释放它, 否则其生命周期直到程序结束时才结束 。 CGraph *=new CGraph(m_nDrawType,m_ptOrigin,point); m_ptrArray.Add(pGraph); 虽然pGraph 指针在函数结束时也会析构,但是没有关系,m_ptrArray已经保存了该指针, 而new所建立的对象是在堆上的,除非显式地调用Delete方法去释放它, 否则其生命周期直到程序结束时才结束 。
窗口重绘会调用OnDraw函数,所以要在此函数中增加画图代码,代码如下:
注: 1. m_ptrArray.GetSize()从得到数组长度,构造for循环 2. ((CGraph*)m_ptrArray.GetAt(i))->得到CGraph类的各变量, 3. 其中m_ptrArray.GetAt(i)返回CObject*,要强制转换成CGraph*。 --------------------------------------------------------------------------------
下面研究一下,OnDraw函数为什么能在窗口重绘过程中被调用 OnDraw函数不是WM_PAINT的相应函数,为什么能在窗口重绘过程中被调用? 下面是一段MFC资源文件中的代码:(是WM_PAINT消息响应函数OnPaint()) void CView::OnPaint() { // standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc); } 从上面的代码我们知道,原来在基类View类中调用了OnDraw函数,所以我们认为OnDraw专门用来重绘的 其实我们也可以增加WM_PAINT消息响应,自己在其响应函数写重绘窗口的代码。 前面讲过,在响应WM_PAINT时候,只能利用BeginPaint()获得dc的句柄,用EndPaint()释放dc句柄。 而上面代码并没有发现BeginPaint()与EndPaint(),查看一下CPaintDC 就知道, CPaintDC 类的构造函数中调用了BeginPaint(),析构函数中调用了EndPaint()。
注意:CPaintDC只能在OnPaint中使用,因为在MSDN中这样说了: CClientDC objects encapsulate working with a device context that represents only the client area of a window. The CClientDC constructor calls the GetDC function, and the destructor calls the ReleaseDC function. CWindowDC objects encapsulate a device context that represents the whole window, including its frame. 如果要在OnPaint以外地方获得dc句柄的话,调用GetDC ,释放用ReleaseDC。
CPtrArray----->MSDN!!
如何把元文件保存到文件当中
1.为“打开”,“保存”添加命令相应函数。 2.用 CopyMetaFile 拷贝元文件到指定文件中 CopyMetaFile 将windows格式的元文件拷贝到指定的文件当中。 首先我们在“保存”命令响应函数OnFileSave()中完成保存元文件到文件的功能,
执行之后在项目工程文件夹中增加了一个meta.wmf的文件,用ACDsee可以打开。 -------------------------------------------------------------------------------- 然后在“打开”命令响应函数OnFileOpen()中完成保存元文件到文件的功能,打开文件时用GetMetaFile或GetEnhMetaFile,接着将将文件的图形拷贝到m_dcMetaFile元文件中,最后调用Invalidate()引起窗口重画,使图形在OnDraw()重绘,代码如下:
注:CopyMetaFile,GetMetaFile函数已经被废弃现在使用增强的函数CopyEnhMetaFile,GetEnhMetaFile。用法相同,为了与16-bit Windows API兼容,老函数仍能使用。 -----------------------------------------------------------------------------
介绍利用兼容DC保存图形与重绘图形的方式
1.构造兼容DC对象:CDC m_dcCompatible 2.在CGraphicView::OnLButtonUp写下面代码
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CClientDC dc(this); CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); //dc.SelectObject(pBrush); m_dcMetaFile.SelectObject(pBrush);
if(!m_dcCompatible.m_hDC) { m_dcCompatible.CreateCompatibleDC(&dc); CRect rect; GetClientRect(&rect); CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height()); m_dcCompatible.SelectObject(&bitmap);
m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);
m_dcCompatible.SelectObject(pBrush); }
3.在CGraphicView::OnDraw中写入下面代码:
CRect rect; GetClientRect(&rect); pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
//出现问题,见下面粗斜体,解决代码见上面粗斜体; CBitmap::CreateCompatibleBitmap 通过指定的宽高创建一个兼容位图。初始化一个与指定设置相兼容的位图 BOOL CreateCompatibleBitmap(CDC* pDC,int nWidth,int nHeight ); -------------------------------------------------------------------------------- CreateCompatibleBitmap 返回的位图对象只包含相应设备描述表中的位图信息头,不包含颜色表和像素数据块。因此,选入该位图对象的设备描述表不能像选入普通对象的设备描述表一样使用,必须在SelectObject函数之后,调用BitBlt将原始设备描述表的颜色表以及像素数据块拷贝到兼容设备描述表。
如果我们想在保存图象的同时显示图像,可以在调用 m_dcCompatible.MoveTo(m_ptOrigin); m_dcCompatible.LineTo(point); 的同时调用 dc.MoveTo(m_ptOrigin); dc.LineTo(point);
如何使窗口具有滚动条?
本程序的CGraphicView 类是从View类继承的 class CGraphicView : public Cview 首先将基类改成CScrollView class CGraphicView : public CScrollView 然后将源文件cpp文件中的CView全部替换成CScrollView 在文件中替换字或词的步骤, 从菜单中选择 Edit->Replace, 在弹出的对话框中再选择 Match whole word only(仅全词匹配) -------------------------------------------------------------------------------- 要使窗口可以滚动,还需要设置一些滚动的参数: 用SetScrollSizes方法来设置映射模式、滚动视图总的尺寸等等 而这个函数最好在虚函数的重载函数OnInitiaUpdate里面调用。 这个虚函数在一个视图与一个文档关联之后调用,也就是在窗口完全创建之后第一个调用的函数。 不过它的调用是在OnDraw函数调用之前。所以这个函数可以做一些窗口创建之后的初始化工作。
void CGraphyicView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); SetScrollSizes(MM_TEXT,CSize(800,600)); }
--------------------------------------------------------------------------------
程序运行之后,将窗口的大小调到小到800*600时,就可以看到滚动条了。 但是我们发现一个问题,先将垂直滚动条下拉一些,然后在窗口右下角画一条直线,将其最小化,还原之后直线的位置上移了,这是什么原因呢?
调试之后,可以断定OnLButtonDown与重绘函数OnDraw中所画图形的三个要素相同,所以不是它们的问题。
在前面研究窗口重绘为什么调用OnDraw函数的时候, void CView::OnPaint() { // standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc); } 发现在调用OnDraw之前调用了OnPrepareDC ,问题就出在这里。 在MFC资源文件中去查看OnPrepareDC 发现这样一条代码:pDC->SetViewportOrg(ptVpOrg); 调试一下,发现ptVpOrg=(x=0, y= -150); 可见OnPrepareDC调整了显示上下文的属性将视口的原点设置为(0,-150),根据公式yViewPort=yWindow- yWinOrg+yViewOrg;得到设备点y坐标出现在原先显示地方的上方。 -------------------------------------------------------------------------------- 关于解决办法的说明: 首先,绘制图形之后保存坐标点之前调用OnPrepareDC调整显示上下文的属性,将视口原点设置为(0,-150)然后调用设备坐标转换逻辑坐标函数DPtoLP将设备坐标(680,390)转换为逻辑坐标, 根据公式 yWindow=yViewport-yViewOrg+yWinOrg;得到y坐标为540。 窗口重绘时会先调用OnPrepareDC,调整显示上下文的属性,将视口的原点设置为(0,-150) 然后GDI函数用逻辑坐标点(680,540)绘制图形,被转换为(680,390)
所以解决的办法就是在OnLButtonDown函数中,绘制完图形之后,保存图形元素之前 加入下面代码: OnPrepareDC(&dc); dc.DPtoLP(&m_ptOrigin); //视口(设备)坐标和窗口(逻辑)坐标 dc.DPtoLP(&point);
当滚动条在最上端,窗口发生重绘时OnPrepareDC调整显示上下文, 会将视口的原点设置为(0,0),而不是(0,-150) OnPrepareDC会随时根据滚动窗口的位置来调整视口的原点
介绍两种保存图形与重绘图形的方式
第一种,要利用要一个类 CMetaFileDC 第一步创建一个CMetaFileDC 对象。 (private) 接着调用CMetaFileDC 构造器,然后再调用Create 函数 创建一个设备上下文与CMetaFileDC 对象关联起来。
下一步给CMetaFileDC 对象发送一系列的CDC命令
在为元文件发送完命令之后,调用Close成员函数关闭元文件的设备上下文, 并返回一个元文件 的句柄。接着就可以处理CMetaFileDC 对象。
CDC::PlayMetaFile 方法可以用来使用元文件句柄播放元文件,我们可以多次调用本函数
介绍一些与本操作相关的成员函数 -------------------------------------------------------------------------------- CMetaFileDC::Create BOOL Create(LPCTSTR lpszFilename = NULL ); 如果指定一个字符串,那么就指定了元文件的文件名,用来保存元文件 如果变量pszFilename 为空,那么就会在内存中创建一个元文件 -------------------------------------------------------------------------------- CMetaFileDC::Close 关闭一个设备上下文,返回一个DC句柄。
-------------------------------------------------------------------------------- 用元文件来重绘图形的具体步骤: 1.先在CGraphicView类中,增加一个CMetaFileDC变量m_dcMetaFille (private) 2.在CGraphicView类的构造函数中调用m_dcMetaFille的Create()方法, Create方法的参数的NULL时创建一个内存的元文件 3.在CGraphicView类的OnLButtonUp()方法中,写入绘图命令, OnLButtonUp()方法代码如下:
m_dcMetaFile.SelectObject(pBrush);
4.窗口重绘时,即在OnDraw()函数中,去关闭元文件,从而获得元文件句柄。
HMETAFILE hmetaFile=m_dcMetaFile.Close();
pDC->PlayMetaFile(hmtaFile);
m_dcMetaFile.Create();//前面已经关闭了,可能再绘制图形,故重新创建一个
m_dcMetaFile.PlayMetaFile(hmtaFile);
DeleteMetaFile(hmtaFile);
有了元文件的句柄,就可以利用CDC::PlayMetaFile来播放它。 注意:关闭元文件并不意味着删除元文件。
View::OnFileSave()
HMETAFILE hmetaFile=m_dcMetaFile.Close();
CopyMetaFile(hmetaFile,"meta.wmf");
m_dcMetaFile.Create();//前面已经关闭了,可能再绘制图形,故重新创建一个
DeleteMetaFile(hmtaFile);
View::OnFileOpen
GetEnhMetaFile();
void CGraphicView::OnFileOpen() { // TODO: Add your command handler code here HMETAFILE hmetaFile; hmetaFile=GetMetaFile("meta.wmf"); m_dcMetaFile.PlayMetaFile(hmetaFile); DeleteMetaFile(hmetaFile); Invalidate(); }
问题:元文件关闭以后,难道不能再次打开,继续画图? 希望有经验的朋友可以讲解一下这个问题!
添加对话框,再用ClassWizard添加new class之后,出现找不到类的提示,可以删除.clw文件,重编译即可
一、绘制图形
CClientDC dc(this);
CPen pen(PS_SOLID,1,RGB(255,0,0));
dc.SelceObject(&pen);
//此处可以调用函数进行绘制了
dc.SetPixel(point,RGB(255,0,0));
//dc.Rectangle(CRect(m_pOrigin, point));
//此处本来需要LPCRECT...但在这个地方不做转换也可以...
//CRect重载了操作符 Converts a CRect --> LPCRECT, 隐式转换!!
//掌握本质/原理上的东西,再看到这种转换,就明白了,当然要记得向MSDN求证
dc.MoveTo();dc.LineTo();dc.Rectangle()
二、透明画刷
在绘图前添加
CBrush *pBrush=CBrush::FromHandle(GetStockObject(NULL_BRUSH));
dc.SelctObject(pBrush);
//设置对话框
if(IDOK==dlg.Domodal()) {//获取用户所输入的线宽值...先设成员变量 m_nLineWidth }
三、颜色对话框
数据m_cc
CColorDialog dlg;
dlg.m_cc.Flags|=CC_RGBINIT;//可以再|CC_FULLOPEN,但不能写成
//
rgbResult
If the CC_RGBINIT flag is set,
rgbResult specifies the color initially selected when the dialog box is created. If the specified color value is not among the available colors, the system selects the nearest solid color available. If
rgbResult is zero or CC_RGBINIT is not set, the initially selected color is black. If the user clicks the OK button,
rgbResult specifies the user's color selection. To create a COLORREF color value, use the RGB macro.
dlg.m_cc.Flags=CC_RGBINIT;//不能写成这样
go to definition 后按Ctrl+Tab键,则回到刚才的窗口
dlg.m_cc.rgbResult=m_clr;
if(IDOK==dlg.Domodal())
m_clr=dlg.m_cc.rgbResult;//m_clr这两句能保存已经选择的颜色
四、字体对话框
数据成员m_cf
CFont m_font;
CString m_strFontName;
CFontDialog dlg;
if(IDOK==dlg.Domodal())
{
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont); -----1//
MSDN的Example:
CFontDialog --> m_cf -->
Example// The code fragment creates a font based on the information
// we got from CFontDialog::m_cf variable.
CFontDialog dlg;
if (dlg.DoModal() == IDOK)
{
// Create the font using the selected font from CFontDialog.
LOGFONT lf;
memcpy(&lf, dlg.m_cf.lpLogFont, sizeof(LOGFONT));
CFont font;
VERIFY(font.CreateFontIndirect(&lf));
// Do something with the font just created...
CClientDC dc(this);
CFont* def_font = dc.SelectObject(&font);
dc.TextOut(5, 5, "Hello", 5);
dc.SelectObject(def_font);
// Done with the font. Delete the font object.
font.DeleteObject();
} |
m_strFontName=dlg.m_cf.lpLogFont->lfFaceName;
Invalidate(); //在OnDraw函数里"写字",这里只要使窗口重绘而已
}
在OnDraw()中添加
CFont *pOldFont=pDC->SelectObject(&m_font);
pDC->TextOut(0,0,m_strFontName);
pDC->SelectObject(pOldFont);
运行,第二次选择字体时出错(二次初始化),将上面程序语句1修改为
if(m_font.m_hObject)m_font.DeleteObject();
//CGDIObject类的成员方法
注意区分CGDIObject (类的对象) 和Windows GDI object(资源对象),两个不同的概念
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);
控件和成员变量关联时
控件值=>成员变量 UpdateData(); //注意如果没调用这个,则不会立即显示,
成员变量可以直接赋值给控件并显示
线型示例:
CRect rect; GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);//一定要先得到GetDlgItem,否则得到的是框架窗口 ScreenToClient(&rect); // 见GetWindowRect注释, CRect重载了LPSTR操作符
dc.MoveTo(rect.left+20,rect.top+rect.Height()/2); dc.LineTo(rect.right-20,rect.top+rect.Height()/2);
The GetWindowRect function retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
五、对话框背景色
CSettingDlg中加COLORREF型public成员变量 m_clr
//接下来...省略三千字...
1,消息WM_CTLCOLOR
添加WM_CTLCOLOR消息处理函数
CBrush m_brush;
m_brush.CreateSolidBrush(RGB(0,255,0));
在CXXX::OnCtlColor()中返回m_brush即可;
nCtlColor
-
Contains one of the following values, specifying the type of control:
-
CTLCOLOR_BTN Button control -
CTLCOLOR_DLG Dialog box -
CTLCOLOR_EDIT Edit control -
CTLCOLOR_LISTBOX List-box control -
CTLCOLOR_MSGBOX Message box -
CTLCOLOR_SCROLLBAR Scroll-bar control -
CTLCOLOR_STATIC Static control
调用多次,每一个控件传一个DC进来
2,对话框上如何精确获得控件?
CSettingDlg中增加Windows message handler, 增加CBrush型private变量 m_brush
在构造函数中:
m_brush.CreateSolidBrush(RGB(0,0,255));
在CXX::OnCtlColor()中添加
return m_brush; //要点: 在OnCtlColor函数里面返回了自己定义的蓝色brush
上面只是控制某一类控件,如果要精确控制某一个控件,则调用GetDlgCtrlID()
1)对按静态框、组合框等可以如下
if(pWnd->GetDlgCtrID()==IDC_STATIC1)
{
pDC->SetTextColor(RGB(255,0,0));
pDC->SetBkMode(TRANSPARENT);//设置文字的背景色
return m_brush;
}
2)对文本框(单行编辑控件)如下
if(pWnd->GetDlgCtrID()==IDC_EDIT1) { pDC->SetTextColor(RGB(255,0,0)); pDC->SetBkColor(RGB(255,0,0));//设置文本框的背景色 return m_brush; }
3,改变静态框显示文字类型
CFont m_font; m_font.CreatePointFont(200,"华文行楷"); if(pWnd->GetDlgCtrID()==IDC_STATIC1) pDC->SelectObject(&m_font);
4,OK按钮
1)在对话框类 =>Insert=>New Class添加以CButton为基类的类CTstBtn 2)增加CtstBtn的虚函数DrawItem
An owner-drawn button has the BS_OWNERDRAW style set. Override this member function to implement drawing for an owner-drawn CButton object. The application should restore all graphics device interface (GDI) objects selected for the display context supplied in lpDrawItemStruct before the member function terminates.
Example Copy Code // NOTE: CMyButton is a class derived from CButton. The CMyButton
// object was created as follows:
//
// CMyButton myButton;
// myButton.Create(_T("My button"),
// WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_OWNERDRAW,
// CRect(10,10,100,30), pParentWnd, 1);
//
// This example implements the DrawItem method for a CButton-derived
// class that draws the button's text using the color red.
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
UINT uStyle = DFCS_BUTTONPUSH;
// This code only works with buttons.
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
// If drawing selected, add the pushed style to DrawFrameControl.
if (lpDrawItemStruct->itemState & ODS_SELECTED)
uStyle |= DFCS_PUSHED;
// Draw the button frame.
::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem,
DFC_BUTTON, uStyle);
// Get the button's text.
CString strText;
GetWindowText(strText);
// Draw the button text using the text color red.
COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC, RGB(255,0,0));
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(),
&lpDrawItemStruct->rcItem, DT_SINGLELINE|DT_VCENTER|DT_CENTER);
::SetTextColor(lpDrawItemStruct->hDC, crOldColor);//restore的含义
} |
3)在CtstBtn::DrawItem()(增加的纯虚函数)中添加 (直接拷过来就OK啦...) UINT uStyle =DFCS_BUTTONPUSH; ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON); if(lpDrawItemStruct->itemState & ODS_SELECTED)uStyle |= DFCS_PUSHED; ::DrawFrameControl(lpDrawItemStruct->hDC,&lpDrawItemStruct->rcItem,DFC_BUTTON,uStyle); CString strText; GetWindowText(strText); COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC,RGB(255,0,0)); ::DrawText(lpDrawItemStruct->hDC,DT_SINGLELINE|DT_VCENTER|DT_CENTER); ::SetTextColor(lpDrawItemStruct->hDC,crOldColor);
//注意OK按钮属性 Style-->OwnerDraw
4)将OK按钮关联CTstBtn的一个变量,并将按钮的owner_draw的选项选上。这一步就是把对话框上的控件和类关联
//Further:要改变按钮背景色?
给大家提供两个类: 老孙的起演示的,和网上另一个人写的可以用于工程开发的;BtnST, 和SXBtn这两个类...
Cancel按钮的OwnerDraw属性勾选上,关联变量...Control型
增加消息处理:WM_INITDIALOG
Set(In)ActiveB/FgColor: 设置活动时的 背/前 景颜色
六、在窗口中显示位图
1,创建位图
CBitmap bm; bm.LoadBitmap(IDB_BITMAP1);
2,创建兼容DC
CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC);
3,将位图选到兼容DC中
dcCompatible.SelectObject(&bm);
4,将兼容DC中的位图贴到当前DC中。
pDC->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY);
超过256色不能在IDE中编辑,但是可以正常显示!!
七、窗口绘制
1,擦除背景
添加WM_ERASEBKGND消息对应的函数
在CXXXView::OnEraseBkgnd()中添加
CBitmap bm;
bm.LoadBitmap(IDB_BITMAP1);
获取位图信息
//BITMAP bmp; //bmp=bm.GetBitmap(&bmp);//注释这两句是为StretchBlt准备的 CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); dcCompatible.SelectObject(&bm); CRect rect; GetClientRect(&rect);
//BitBlt函数进行1:1的拷贝函数,不能压缩或者拉伸位图
pDC->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY);
//pDC->StretchBlt(rect.left,rect.top,rect.Width(),rect.Height(),&dcCompatible,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
return TRUE;
并将return CView::OnEraseNkgnd(pDC);注释掉
在这里实现闪烁比较小
满屏显示的问题: StretchBlt ----多了nSrcWidth/Height指示原矩形的高度和宽度
2,重新显示
将以上代码复制到OnDraw()函数中,也能正常显示,但是闪烁比较大
题目:创建CDialogBar并在其上放置控件,重复以上的功能,并在列表框中显示位图
第10课 图形的绘制,如何使用自定义画笔 1.画图: a.创建四个菜单,为其添加消息响应; b.在View中添加m_DrawType,保存绘画类型; c.增加成员变量,m_PtOrigin,当按下鼠标左键时,保存此点; d.在OnLButtonUp中画点,线,矩形,椭圆,别忘记设置成透明画刷 2.为其添加一个设置对话框(线型和线宽) a.创建对话框,为其创建一个新类关联它; b.为其中的线宽关联成员变量; c.在View中增加一个菜单,响应新的对话框; d.添加线型选项设置,将其Group属性选中,并为单选按纽关联成员变量。在view中增加一个线型变量m_nLineStyle 3.添加一个颜色对话框 a.实例化一个CColorDialog b.调用DoModal方法 4.添加字体对话框,将选择的字体在View中显示出来。 a.实例化一个对象; b.为View添加一个字体成员变量,得到用户选择的字体。 c.调用Invadate()发出重绘消息; d.再次注意一个对象只能创建一次,故要再次创建,必须将原告的删除! 5.为设置对话框增加示例功能。 a.当控件内容改变时,发出En_change消息。而Radio按纽则为Clicked。需先UpdateData()。另外还需要ScreenToClient(&rect) 6.改变对话框的背景色和控件颜色。 每个控件被绘制时都发出WM_CTlColor消息, 7.如何改变OK按纽的字体和背景? OK按纽 a.创建一个新类,CTestBtn,基类为CButton b.在类中增加虚函数,DrawItem,添加代码。 c.将OK按纽关联成员变量。类型为CTestBtn,注意将OK按纽的OwnerDraw特性选中。 Cancel按纽 用新类来改变。 a.加入新文件。 b.为Cancel关联一个成员变量,类型为CSXBtn; c.调用CSXBtn的方法。 Cancel2按纽 a.方法同上。 8.在窗口中贴图,4个步骤 1、创建位图 CBitmap bitmap; bitmap.LoadBitmap(IDB_BITMAP1); 2、创建兼容DC CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); 3、将位图选到兼容DC中 dcCompatible.SelectObject(&bitmap); 4、将兼容DC中的位图贴到当前DC中。在WM_EraseBkgnd()中调用,但不能再调用基类的擦除背景函数。也可以在OnDraw函数中完成,但效率低,图像会闪烁,因为它先擦除背景,慢。 pDC->BitBlt(rect.left,rect.top,rect.Width(), rect.Height(),&dcCompatible,0,0,SRCCOPY);
http://blog.csdn.net/huahuamoon/archive/2007/12/25/1966566.aspx
先转一篇: 去LG手机客服的窝火体验...
http://bible.younet.com/files/2007/07/09/375809.shtml
看到这篇的XDJM请发信息告知我联系方式. 偶电话15810917481
今天十点半起的(先汗一个)...
这个时间比较尴尬.出了紫荆,见到移动又在充值送U盘的活动...
我本来一直安安心心从来就没想过贪小便宜的.
但今天时间比较尴尬.所以想,要不充个值,然后再去吃饭再去实验室.
于是排队.要开通XX业务.于是开通.
于是去吃饭.
于是来实验室.
但没有"及时"退订...
我的LG的破手机是大约三年前的破烂玩意儿...
CPU极耸...
结果定的那个什么手机报业务.频繁发彩信给我(的手机)...
所以我的可怜的手机崩溃了,恢复到出厂前的状态了...
My poor little mobile phone...
于是手机里面所有的东东,都木有了...
现在我手上拿的是一个"新"手机...汗死...
查了一下内存剩余状态: 2024KB (我的手机本来就只有2M)
...
天才天才天才,完全没有任何征兆地...
天才天才天才...
人都是丢了手机,所以丢了所有的联系方式,偶?...汗...是被中移动把手机搞"残"了,重生了,才丢了所有的联系方式.
为什么我总是这么耸...
前段时间,删了IE收藏夹/删了邮件簿/删了N年前的照片...
都是重装系统/整理硬盘,误删的...
突然间,我的过去----没有了!!
啊...痛...我的过去
http://blog.csdn.net/huahuamoon/archive/2008/01/02/2010658.aspx
全部文章: http://blog.csdn.net/huahuamoon/category/356224.aspx
1:如何修改单文档应用程序的窗口标题,查阅MSDC文章:
Changing the styles of a window created by MFC. 要在CMainFrame的PrecreatWindow()中加入如下代码: cs.style&=~FWS_ADDTOTITLE; cs.lpszName="This is a test!"; 可以先不要上一句试一试! 另一种方法是 : cs.style=WS_OVERLAPPEDWINDOW; 再进行修改,也可以不修改,那么是去掉默认文档标题,而只显示原程序标题!
另一类方法是在窗口创建后再修改,因为在OnCreate中,开始的这些代码: if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar (IDR_MAINFRAME)) { TRACE0("Failed to create toolbarn"); return -1; // fail to create }
if (!m_wndStatusBar.Create(this) || ! m_wndStatusBar.SetIndicators(indicators, sizeof (indicators)/sizeof(UINT))) { TRACE0("Failed to create status barn"); return -1; // fail to create } // TODO: Delete these three lines if you don't want the toolbar to // be dockable m_wndToolBar.EnableDocking (CBRS_ALIGN_ANY); EnableDocking (CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);
完成了窗口创建,工具栏,状态栏的创建等工作,可以在后面利用一个系统全局函数SetWindowLong()函数进行修改:
加入代码为:SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW); 与此相对,还有一个GetWindowLong()函数可供使用!如下面代码去掉了窗口上的最大化按钮: SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE) & ~MAXIMIZEBOX); 当然SetWindowLon()还可以做别的修改.与SetWindowLong()相类似的另一个系统全局函数为 SetClassLong();
2:如何完成一个动画图标 其实就是准备好几个图标,在定时器消息 响应中更改图标即可完成. 第一步是准备好几个(如三个)图标. 第二步是在CMainFrame类中做三个图标类的相关对象的成员变量,或者是一个大小为3的HICON数组. 第三步是在CMainFrame类的OnCreate()函数中LoadIcon()进行对三个图标的加载.其中用到的实例句柄的获取有三种方法: 一:用全局函数AfxGetInstanceHandle()获取, 二:先在CMainFrame类中用extern声明一下全局对象theApp,然后使用theApp.hInstance; 三:使用全局函数AfxGetApp()获取全局对象theApp对象的指针,然后用AfxGetApp()->hInstance; 第二个参数是一个字符指针,可我们只有图标的资源ID,所以要进行必要的转换:用MAKEINTRESOURCE宏! 第四步是设置定时器,也在OnCreate()函数中定义:SetTimer(1,1000,NULL); 第五步是在CMainFrame中添加WM_TIMER消息响应,在其中加入代码: static int index=0; SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]); index=++index%3;
3:在工具栏上新加一个按钮,要让它与前一个按钮之间有一个分隔符,只需要将它轻轻向一旁拖动一点点再放开即可,而要删除工具栏上的一个按钮,你只是选中它再按DEL键是完不成的,它只是将按钮上的图案删除,所以删除一个按钮要将它拖动到工具栏之外,再松手!
4:如何创建一个工具栏 在MSDN的关于CToolBar的讲解页有详细说明! 一:插入工具栏资源, 二:在CMainFrame中加入一个CToolBar类对象的成员变量, 三:在CMainFrame的OnCreate()中加入: if (!m_MyToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || ! m_MyToolBar.LoadToolBar(IDR_MYTOOLBAR)) { TRACE0("Failed to create toolbarn"); return -1; // fail to create }
m_MyToolBar.EnableDocking (CBRS_ALIGN_ANY); DockControlBar(&m_MyToolBar); 各个函数调用及参数传递查看MSDN!
5:如何让一个工具栏隐藏或显示: if(m_MyToolBar.IsWindowVisible()) { m_MyToolBar.ShowWindow(SW_HIDE); } else { m_MyToolBar.ShowWindow(SW_SHOW); } 但这样做的结果是工具栏虽说隐藏了, 但是工具条还在,所以还要在后面加上一句: ReCalcLayout(); 这样做还是有问题,如果工具栏没有停靠在边上而是一个单独的小窗口,那么只做上面的工作只使得工具栏上的按钮不见了,而那个小窗口还在,所以,还要调用一个函数: DockControlBar (&m_MyToolBar); 经过上面这句,小窗口也如愿消失了,但问题还有一点,就是当用户将工具栏放置为一个小窗口时,再点击菜单,要让这个工具栏显示出来,当然我们应该将工具栏仍按用户先前的小窗口样式显示出来比较好,可是这次工具栏又自动停靠在客户区顶部了?这个功能如何实现呢?孙老师只是提示可以查MSDN中CToolBar 的成员函数解决这个问题,并没细讲,所以我看了MSDN,发现有两个函数:CToolBar::IsFloating()利用这个函数可以判断一个工具栏是否处于浮动状态,另一个是CFrameWnd::FloatControlBar()这个函数可以让一个控制栏处于浮动状态,然后我在 CMainFrame中加入了一个BOOL型的成员变量,在每次判断工具栏是否可见时用来记录工具栏是否处于浮动状态,然后在重新生成工具栏时根据它的置决定是否将工具栏设为浮动状态,但是第二个函数好像不太好使,所以我又换用了SetWindowPos()成员函数,可是也不能将它放置为一个独立的小窗口. 显示和隐藏工具栏的第二种方法: 用一个函数:CFrameWnd::ShowControlBar(),因为这个函数的固有特性,上面是if...else...判断就可以简化为一句代码: ShowControlBar(&m_MyToolBar,!m_MyToolBar.IsWindowVisible(),FALSE); 并且我惊讶的发现,用这个函数时,上面提到的浮动工具栏让它在恢复的时候仍回复为浮动的问题自动解决了!哈哈,好.
6:状态栏相关编程 因为MFC自动生成的系统已经包含了一个状态栏,所以我们暂时仅限于已有状态栏的修改,而不是另外生成一个状态栏. 状态栏最左边的那一长条,就是经常显示一些提示字符串的那部分叫做提示行,而右侧那三个小窗口是用来指示 CapsLock,ScrollLock,NumLock开关的状态,称为状态指示器. 状态栏跟工具栏一样,也是在CMainFrame类中定义并在OnCreate()中创建的. 下面的代码在状态指示器的最左边放置了两个小窗口,并在第一个小窗口中放置了一个时钟: 同样的CMainFrame的OnCreate() 中, CTime tm=CTime::GetCurrentTime(); CString strTime=tm.Format("%H:%M:%S");
CClientDC dc(this); CSize sz=dc.GetTextExtent(strTime); m_wndStatusBar.SetPaneInfo (1,IDS_TIMER,SBPS_NORMAL,sz.cx);//调整窗口大小 m_wndStatusBar.SetPaneText(1,strTime); SetTimer(2,1000,NULL); 当然要先有准备工作,在字符串资源中添加两个字符串,ID分别为:IDS_TIMER,IDS_PROGRESS,交将之添加到MainFrm.cpp的 static UINT indicators[] = { ID_SEPARATOR, // status line indicator IDS_TIMER, IDS_PROGRESS, ID_INDICATOR_CAP S, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; 这个数组中,并且上面SetPaneInfo()中的第一个参数就是相应ID在indicators[]数组中的索引值,显然最后三个ID是大小写,数字锁及ScroolLock的标识. 做完上面这些工作,这个时钟还不能变化,只是显示了一个定值,我们还要在定时器的响应函数中再刷新该值: if (2==nIDEvent) { CTime tm=CTime::GetCurrentTime(); CString strTime=tm.Format("%H:%M:%S"); m_wndStatusBar.SetPaneText (1,strTime); } 这样就完成了一个在状态栏中显示的,可以动态变化的时钟!
7:如何创建一个进度条并将之放置在状态栏中的某个位置? 与MFC中其它标准资源一样,进度条也有一个专门的类与之相对应:CProgressCtrl类. 先在CMainFrame中放置一个成员变量:CProgressCtrl m_ProgBar; 然后在OnCreate()中加 入: m_ProgBar.Create(WS_CHILD | WS_VISIBLE,CRect (300,100,500,120),this,88888); 就可以在窗口中显示一个进度条. 至于要将之放置于状态栏上的某个窗格之中,就要先得到窗格所在的矩形区域,然后在上面是第二个参数中进行指定,故将上面一句改为如下: CRect rect; m_wndStatusBar.GetItemRect (2,&rect); m_ProgBar.Create(WS_CHILD | WS_VISIBLE,rect,this,88888); 可是这样并未实现设置,所以我们在上面设置断点,发现到达m_ProgBar.Create()时矩形区域的值并不正常,原来在OnCreate()未结束时,状态栏的设置无法完成,无法获得矩形区域位置,所以,就要想办法在OnCreate()结束时做上面的工作,可以做一个自定义消息,在 OnCreate()末尾发送一个该消息,并做一个消息响应函数,然后将上面的代码放置在其中即可. 要自定义消息,首先要在CMainFrame的头文件首部 做: #define UM_PROGRESS WM_USER+1 //注意,没有; WM_USER是一个系统定义宏,详细情况查MSDN. 然后在CMainFrame的头文件的消息响应中加入消息响应函数的声明: afx_msg void OnProgress(); 再加入消息映射: ON_MESSAGE(UM_PROGRESS,OnProgress) 再实现消息响应函数: void CMainFrame::OnProgress() { CRect rect; m_wndStatusBar.GetItemRect(2,&rect); m_ProgBar.Create(WS_CHILD | WS_VISIBLE,rect,&m_wndStatusBar,123);//注意,这里父窗口就不能设置为this了, 而要是状态栏! m_ProgBar.SetPos(50); } 当然,不能忘了在OnCreate()最 后发送消息: PostMessage(UM_PROGRESS);
这样就可以了! 但是问题还是有的,你会发现当你拉动窗口改变其大小时,状态栏的位置发生了变化,不再覆盖在先关状态栏的那个小窗口上了,怎么办呢?你会想到在窗口大小改变时,系统会接受到一个WM_PAINT消息,只要在那个消息的响应函数中实时获取小窗口的矩形区域,再改变进度条的位置,不就可以了吧,没错,这样的确可以,又因为WM_PAINT消息当窗口显示时,也就是说OnCreate()之后就会马上收到,所以我们也不用像上面那样麻烦的自定义什么消息啦,直接在CMainFrame中加入WM_PAINT的消息响应函数,并在其中加入: CRect rect; m_wndStatusBar.GetItemRect(2,&rect); m_ProgBar.Create(WS_CHILD | WS_VISIBLE,rect,&m_wndStatusBar,123); m_ProgBar.SetPos(50); 即可了,但是问题又来了,当窗口大小一改变时,程序发生了一个致使错误,原来我们不能在每一次响应时都创建进度条,进度条对象只有一个,哪能多次使用呢?所以,改为如下代码: CRect rect; m_wndStatusBar.GetItemRect(2,&rect); if(!m_ProgBar.m_hWnd) { m_ProgBar.Create(WS_CHILD | WS_VISIBLE,rect,&m_wndStatusBar,88888); } else { m_ProgBar.MoveWindow(&rect); } m_ProgBar.SetPos(50); 这下,无论怎么拖,也不会发生异常现象了! 那么,如何让进度条动起来呢?相关的成员函数为 SetStep(),以及StepIt();
8:下面的代码将鼠标当前的位置坐标显示在状态栏的提示行中:
在View类中响应WM_MOUSEMOVE消息,在其中加入代码如下:
CString str; str.Format("x=%d,y=% d",point.x,point.y); ((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
这段代码要正常运行的前提是首先CMainFrame在View文件中不可见,所以要先包含一个MainFrm.h 头文件,然后,因为m_wndStatusBar在CMainFrame中是一个protected变量,所以不能在View类中访问,所以我们要手动把它的访问权限改为public.然后就OK了! 其实还有一种更方便的方法:调用CFrameWnd::SetMessageText() 所以上面的代码就 可以改为: CString str; str.Format("x=%d,y=% d",point.x,point.y); ((CMainFrame*)GetParent())->SetMessageText (str); 这样子,就不用取得状态栏指针了,也就不用去修改其访问权限了! 关于这个工作,还有第三种方法,也不用去修改状态栏的访问权限,这是应用了另一个函数:CHtmlView::GetStatusBar(),上面的代码就可以是: CString str; str.Format("x=%d,y=%d",point.x,point.y); ((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str); 下面还有第四种方式:这是应用了又一个函数:CWnd::GetDescendantWindow(),这个函数可以根据窗口ID来从调用它的窗口出发找到与该ID相同的一个子孙窗口的指针: CString str; str.Format("x=%d,y=%d",point.x,point.y); GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str); 至于上面那个ID号,是从CStatusBar::Create()中看到它!关于GetDescendantWindow()的调用,要注意其第二个参数在MSDN中关于临时窗口及持久窗口的讲解!
9:关于程序的启动画面的制作 这个跟往程序中加入右键PopUp菜单一样,可以使用VC的组件库中的组件.找到一个叫SplashScreen的东西,加入它到工程中,别的什么也别动,先编译运行一下,你会发现程序已经有一个启动画面了,不过是很简陋的. 这个组件在我们的程序中加入了一副默认位图,并加入了一个叫CSplashWnd的类,并且在CMainFrame的OnCreate()函数中加入了一句:CSplashWnd::ShowSplashScreen(this),进行了相应的启动工作. 在CSplashWnd的成员函数OnCreate()中有一句SetTimer(1, 750, NULL);其消息响应函数中进行了启动画面的隐藏,所以,修改其中的时间值可以修改启动画面显示的时间长度! 另外,可以发现窗口的显示与启动画面的显示是同时进行的,如何才能让画面消失时窗口才显示出来呢?
至于启动画面的改变,可以自己插入位图,然后将它的ID修改为IDB_SPLASH 然后RebuildAll,也可以在其源文件中LoadBitmap()中将ID改为自己想要加载的位图的ID!
| |
Thanks for visiting!
<script type=text/javascript> /* */ </script>
| |