第一个MFC程序——自行车站点查询系统

一个自行车站点查询系统

大二的在校生,佛山某放假大学,早在圣诞节就结束期期末考试的我们,算是进入了“准放假”,但是学院老师们并不会让我们回家也不会让我们那么闲,按照传统艺能,两周留下来敲代码是必不可少的。这一次,学院老师要折腾我们的是用MFC做一个小软件。
我一听傻了,啥事MFC???
第一个MFC程序——自行车站点查询系统_第1张图片
百度了下这东西,又臭又长,可以可以说完全是0基础,所以弄了一周才弄完,目前仍有很多bug
是算法逻辑上的bug(或者说功能还没完善)但是程序怎么运行,应该都是不会崩溃了的
因为github上传组件出了点问题,还上传不到GitHub,又想趁着刚做完东西还没忘赶紧把过程中学到的东西记录下来,代码就先打包放到百度云上了。
链接: https://pan.baidu.com/s/1NRThoMOVHwv0UAZaOjr8qg 提取码: g5tm

说说功能

第一个MFC程序——自行车站点查询系统_第2张图片目前是长这样的,没有设计天赋,所以外观样式就看且看吧。
先说一下怎么使用这个东西,在文件包里有个 公共自行车站点信息.csv
在导入文件那里点击导入文件,就可以进行一系列操作,图片上有的功能都可以实现,可能包上的版本多了个刷新功能,不过那个没什么用,就删除了

直接说功能吧

这里必须说一下:贴的代码很多都是基于我项目那个类的成员函数,若想移植代码,切不可盲目移植,否则只会扰乱你的代码阅读
CS20180710128 是工程名
CCS20180710128Dlg 是工程目录
想要移植,得把工程名改成你自己工程的名字,还要在工程的类中额外声明你自己的函数(或者只将函数内容移植加到你自己响应函数中,这样就不用额外声明)

1.导入文件

代码片.

//打开文件//打开文件//打开文件//打开文件//打开文件//打开文件//打开文件//打开文件//打开文件//打开文件
void CCS20180710128Dlg::OnChoose()  
{	if(S_FILE_OPEN_FLAG=1) 	m_combox1.ResetContent(); 
	// TODO: Add your control notification handler code here
	BOOL isOpen = TRUE;		//是否打开(否则为保存)
	CString defaultDir = L"D:\\";	//默认打开的文件路径
	CString fileName = L"";			//默认打开的文件名
	CString filter = L"文件 (*.txt; *.csv)|*.txt;*.csv;||";	//文件过虑的类型
	CFileDialog openFileDlg(isOpen, defaultDir, fileName, OFN_HIDEREADONLY|OFN_READONLY, filter, NULL); //寻找路劲
	INT_PTR result = openFileDlg.DoModal();
	
	if(result ==IDOK) {
		fileName = openFileDlg.GetPathName();
		CStdioFile f;
		CFileException e;
		if(!f.Open(fileName, CFile::modeRead, &e))
		{
			TRACE(_T("File could not be opened %d\n"), e.m_cause);
		}
		else{
			MessageBox("导入文件成功!");
			S_FILE_OPEN_FLAG=1;  //文件打开成功,可以进行后续的一些操作
			}
		CString str="";
		f.ReadString(str);//每次读取一行,并且赋值给str,过滤第一行
		f.ReadString(str);
	    int i=0;
		while (str!="" )
		{ 
		   CStringArray* result = DivString(str);//按照excel上的顺序依次下来
		   station_num.Add(_ttoi(result->GetAt(0)));//站点序号存进去,后续查找功能要用到,如果不需要查找功能,这三句都可以不要
		   station_name.Add(result->GetAt(1));//站点姓名 同上

										//	int m_num = _ttoi(m_no_str); //m_no_str是每一行的第一个串,也就是序号那一列
										//	MessageBox(m_no);
										//	if(m_num%10==0)	//筛选,序号是10的倍数才进入box可供选择
										//		{
				temp_str.Add(str);	//全局串变量 temp_str,每次循环到这,他就添加多一条串(上不封顶?)
				m_combox1.InsertString(i,str);//按i的顺序插入项目			
										//		}
				f.ReadString(str);
				i++;
		}
		m_combox1.SetCurSel(0); //设置第nIndex项为显示的内容

	}
//	CWnd::SetDlgItemTextW(IDC_EDIT_SRC, filePath);

	
}

大概的步骤,在注释中已经解释清楚
m_combox1,是我组框的关联变量,调整该变量的值,在相应的组框上就会有显示
temp_str,是一个全局的字符串数组类型变量 CStringArray temp_str 至于为什么叫这个名字?当时想的是只使用这个变量一下下,觉得会是暂时的,但是到后面才发现这个变量用处实在太大了!!改起来很多地方都要改,于是就没再去修改名字
并且要说一下 代码段中的DivString(),不是系统函数,而是自己添加在,函数定义在CS20180710128Dlg.h这个头文件中


//字符串分隔//字符串分隔//字符串分隔//字符串分隔//字符串分隔//字符串分隔//字符串分隔//字符串分隔//字符串分隔//字符串分隔
CStringArray* DivString(CString test)
{
CStringArray* m_result = new CStringArray;  //开辟一个cstringarray地址
while(TRUE)
{
int index = test.Find(_T(","));     //一直寻找  ","  直到没有,进入if
if(index == -1)
{
m_result->Add(test);
return m_result;   //返回一个cstringarray的地址,这个地址是个分割了字符串的数组
}
CString test1 = test.Left(index);  //没进入if之前,建立test存储index左边(逗号左边)的字符串
m_result->Add(test1);				//这个cstringarray变量m_result就逐个储存
test = test.Right(test.GetLength()-index-1);    //test随着循环变成第i个逗号右边的串(i=1,2,3....)
}
}

字符串分割,在很多地方都能用到

站点信息显示

如果试过在组框内选了一个站点后再按“选择该点”,会发现下面可以显示其相关信息

话不多少,贴代码

void CCS20180710128Dlg::OnChosePoint() 
{	if(S_FILE_OPEN_FLAG==1)
	{
		int nIndex = m_combox1.GetCurSel();//组合框内选择的选项,传递到这里
	//	MessageBox(temp_str.GetAt(nIndex));  //提示用
		CStringArray* result = DivString(temp_str.GetAt(nIndex)); 
		m_no=result->GetAt(0);  //站点序号
		m_name1	=result->GetAt(1);  //站点名
		m_adress1=result->GetAt(2);  //站点地址
		m_sum1=result->GetAt(3);  //站点单车总数
		m_useful1=result->GetAt(4);  //可用单车数目
		m_serviec1=result->GetAt(5);	//服务状态
		m_long1=result->GetAt(6);  //经度
		m_lati1=result->GetAt(7);  //纬度
		m_longlati1=result->GetAt(8)+","+result->GetAt(9);  //经纬度
		UpdateData(false);
	}
	else
	MessageBox("未检测到导入文件,请先导入文件!");
}

比较简单
m_combox1.GetCurSel();是获取组框当前选择的序号并返回一个整形数值
UpdateData(false); 用于负责信息的更新显示在静态文本控件上第一个MFC程序——自行车站点查询系统_第3张图片对应代码段分割的9段
第一个MFC程序——自行车站点查询系统_第4张图片
类向导中的变量设置,设置完成后,摆放到特定的位置,就能在点击响应后获取信息并更新

坐标绘制

坐标点的着色绘制涉及到两个功能,显示站点、查询用户轨迹,原理都很简单
首先要声明绘图函数 在CS20180710128lg.h的public声明中有
void DrawEllipse(int x,int y,int r,COLORREF color);
接着你可以在任意头文件中添加以下定义代码

//根据圆心和半径画圆
void CCS20180710128Dlg::DrawEllipse(int x,int y,int r,COLORREF color)
{  CDC *dc;
	dc = m_map01.GetDC(); // m_map01是picture控件对应的变量 
		//将兼容dc上的绘图,拷贝到目标控件上
	dc->BitBlt(0,0,rect.Width(),rect.Height(),&m_MemDC,0,0,SRCCOPY);//将图片左上角作为坐标(0,0)
  	CBrush brush,*oldbrush;
  	brush.CreateSolidBrush(color);
  	oldbrush=dc->SelectObject(&brush);
  	dc->Ellipse(x-r,y-r,x+r,y+r);
  	dc->SelectObject(oldbrush);
	
}

//参数中中x,y,r分别为坐标以及半径,color参数是颜色参数 COLORREF的类对象的实例化
//比如 COLORREF red=RGB(255,0,0);
获取颜色的RGB值有很多方法,百度上有一大堆,这里就不细说

void CCS20180710128Dlg::OnBUTTON4test_draw() 
{
	if(S_FILE_OPEN_FLAG==1)
	{
		CString longitude;  //经纬度串以及经纬度数值
		CString latitude;
		double longitude_num;
		double latitude_num;
		COLORREF blue;  //颜色变量,RGB值
		blue=RGB(123,104,238);
		int i=0;
		for(i=0;i<585;i++)
		{
			CStringArray* result = DivString(temp_str.GetAt(i));  
				longitude=result->GetAt(6);  //经度
				latitude =result->GetAt(7);  //纬度
				longitude_num=((42.1-atof(longitude))/0.4)*580;
				latitude_num=((atof(latitude)+87.8000)/0.2506)*275;						
				DrawEllipse(latitude_num,longitude_num,3,blue);
			
		}

	}
	else
		MessageBox("未检测到导入文件,请先导入文件!");
}

S_FILE_OPEN_FLAG==1,是定义的一个全局整形变量,用来判断是否导入文件,导入文件之前值为0
导入之后值为1
主要代码是 五百多次 循环中
在导入文件那一功能中说过的,temp_str 这一全局字符串
GetAt是数组类变量的方法,在字符串数组中同样适用,不熟悉的同学将他看作成
int a「10」,a[0],a[1],a[2]…等,效果一样,会用就行

	CString longitude;  //经纬度串以及经纬度数值
	CString latitude;

是来暂时存放经纬度对应字符串的字符串
atof(CString);将一字符串转换为其对应双精度浮点数
longitude_num=((42.1-atof(longitude))/0.4)*580;
latitude_num=((atof(latitude)+87.8000)/0.2506)275;
580
275 是图片的分辨率,也是通过如此将经纬度坐标转换成图片上的像素点并绘图。
and图片的来源是经度-87.8到-87.5694
纬度41.7到42.1这一区间(作业要求)
根据图片长短宽高580 与 275两个数值需要更改,是图片的像素大小
大概差不多就行

选择一个站点,并单独标记

void CCS20180710128Dlg::OnButton_point_diaplay()  //选择一个点,这个点的颜色变红
{	if(S_FILE_OPEN_FLAG==1)	
	{
	//颜色部分
	COLORREF red; 
	red=RGB(255,0,0);

	//计算部分
	int nIndex = m_combox1.GetCurSel();//组合框内选择的选项,传递到这里
	CStringArray* result = DivString(temp_str.GetAt(nIndex));
	CString longitude;  //经纬度串以及经纬度数值
	CString latitude;
	double longitude_num;
	double latitude_num;
	longitude=result->GetAt(6);  //经度
	latitude=result->GetAt(7);  //纬度
	longitude_num=((42.1-atof(longitude))/0.4)*580;
	latitude_num=((atof(latitude)+87.8000)/0.2506)*275;	

	//绘图部分
	//CDC *pDC; 	//绘图控件
	//pDC = m_map01.GetDC();
	//pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_MemDC,0,0,SRCCOPY); 

	DrawEllipse(int(latitude_num),int(longitude_num),5,red);
	}
	else
		MessageBox("未检测到导入文件,请先导入文件!");
}

int nIndex = m_combox1.GetCurSel();组合框的选项传递给nindex,再从串数组中获取该段,再获取该段对应的经纬度部分,转换成数值,绘制再地图上,没有什么花里胡哨的操作

清空画布

void CCS20180710128Dlg::OnBUTTOwithdraw()   
{
Invalidate(TRUE);
}

没啥好说的,一行代码搞定,直接ctrl cv,换都不用换

用户方面

关于用户,至少先要有个用户结构体或者类吧?
是的肯定要,我定义了一个用户类(这tm就是结构体吧?)

class User
{	public:
	CString name;  //名
	CString sex;	//性别
	CString age;   //年龄
	CString startpoint_name; //起始点名称
	CString endpoint_name; //
	CString startpointll; //起始点经纬度
	CString endpointll;
	int starttime;  //起始时间
	int endtime;
	double distance;//距离
	double long_start;
	double long_end;
	double lati_start;
	double lati_end;
};

以上是用户类的声明,至于为什么这么繁琐,可能需要看到下面的代码才知道,当然很多地方都是可以优化的,但是本人比较懒

//显示用户列表//显示用户列表//显示用户列表//显示用户列表//显示用户列表//显示用户列表//显示用户列表//显示用户列表
void CCS20180710128Dlg::OnButton_user_list()   
{if(U_FILE_OPEN_FLAG==1){MessageBox("用户列表已经打开了哦。\n请勿重复打开"); return ;}  //打开一次后就不能再打开(否则异常)
	if(S_FILE_OPEN_FLAG==1)
	{	
		CString fileName ;			//默认打开的文件名
		fileName = _T("C:\\Users\\70426\\Desktop\\user.csv");  //文件的绝对路径,使用相对路径会崩溃 why?
	
		CStdioFile f;
		CFileException e;
		if(!f.Open(fileName, CFile::modeRead, &e))
			{
			TRACE(_T("File could not be opened %d\n"), e.m_cause);
			}
		else{
		
			U_FILE_OPEN_FLAG=1;  //文件打开成功,可以进行后续的一些操作
			}
		CString str="";
		f.ReadString(str); //两次实现,过滤掉第一行
		f.ReadString(str);
		int i=0;

		while(str!="" && i<USER_NUM)
		{	CString temp;
			user_str.Add(str);
			CStringArray* user_info = DivString(user_str.GetAt(i));	  //这里是按user.csv文件赋值 user_information用户信息
			CStringArray* station_start =DivString(temp_str.GetAt((rand() % (585-1+1))+ 1));  //随机取一个站点信息给这个b,起点
			CStringArray* station_end =DivString(temp_str.GetAt((rand() % (585-1+1))+ 1));   //随机取一个站点给这个b,终点
			user[i].name=user_info->GetAt(0);  //name
			user[i].age=user_info->GetAt(1);	//age
			user[i].sex=user_info->GetAt(2);		//sexual
			user[i].startpoint_name=station_start->GetAt(1);  //startpoint_name
			user[i].endpoint_name = station_end->GetAt(1);		//endpoint
			user[i].starttime = ((rand() % (3-1+0))+ 0)*10;		//starttimr created by a random-function
			user[i].endtime=((rand() % (15-1+4))+ 4)*10;		//same as shangmian
			user[i].startpointll=station_start->GetAt(8)+","+station_start->GetAt(9);  //start-longitude&latitude	
			user[i].endpointll=  station_end  ->GetAt(8)+","+station_end->GetAt(9);	//end-longitude&latitude
			user[i].long_start=atof(station_start->GetAt(6));
			user[i].long_end=  atof(station_end->GetAt(6));
			user[i].lati_start=atof(station_start->GetAt(7));
			user[i].lati_end  =atof(station_end->GetAt(7));
			double long_dis=user[i].long_start-user[i].long_end;  //计算经纬度代表的真实距离
			double lati_dis=user[i].lati_start-user[i].lati_end;
			double temp_distance=sqrt((long_dis*long_dis)+(lati_dis*lati_dis))*1110000;  //1经纬度111公里
			user[i].distance=temp_distance;		
			//以上为信息赋值部分,以下是列表框的添加以及循环
			m_list_user.InsertString(i,user[i].name);    //把名字按顺序显示在列表框上
			i++;
			f.ReadString(str);	
		}
	}
	else
		MessageBox("未检测到地区数据文件,请先导入文件!");
	
}

整体操作都比较简单,如果看得懂前面的功能,那么这个用户信息赋值所用到的语句,那肯定也都能看得懂
((rand() % (max-1+min))+ min)*10;
代码段中出现这样的语句是随机数(其实并不是随机的,但由于种种因素可以将它看成是随机的)
作用是在min到max之间选取一个整数,闭区间
关于随机函数的使用,CSDN上有很多优质的解析,点击搜索就能出很多,这里就深入说。
distance最后乘以111000,是百度上说的比例,真实值如何不得而知,但是作为作业,能用就行了对嘛!~
可以看到代码中有涉及到文件的读写,
fileName = _T(“C:\Users\70426\Desktop\user.csv”);
这需要改成压缩包内user.csv的路径,或许采取相对路径会好很多,但是不知道为什一直都会崩溃?

显示用户信息以及轨迹查询

//显示用户信息//显示用户信息//显示用户信息//显示用户信息//显示用户信息//显示用户信息//显示用户信息//显示用户信息
void CCS20180710128Dlg::OnBUTTONuser()  
{CString str_strattime,str_endtime,str_distance;
int index=-1;
index=m_list_user.GetCurSel();  
if(index!=(-1))
{						//辅助变量
						str_strattime.Format("%d",user[index].starttime);
						str_endtime.Format("%d",user[index].endtime);
						str_distance.Format("%lf",user[index].distance);

						//前方施工^_^
						CString information="姓名:"+user[index].name+"\n"+
						"性别:"+user[index].sex+"\n"+
						"年龄:"+user[index].age+"\n"+
						"骑行起点:"+user[index].startpoint_name+"\n"+
						"骑行终点:"+user[index].endpoint_name+"\n"+
						"起点坐标:"+user[index].startpointll+"\n"+
						"终点坐标:"+user[index].endpointll+"\n"+
						"骑行开始时间:"+str_strattime+"\n"+
						"骑行结束时间:"+str_endtime+"\n"+
						"骑行距离:"+str_distance+"米";	
						//施工完成^_^

		if( MyMessageBox(NULL, information, "用户个人信息", MB_OKCANCEL)==1)//点击查看返回1 ,取消返回2
		{//	Invalidate(TRUE);
			int i;
	//		CString str1,str2,str3,str4;
			double x,y,k,x0,y0;
			double longitude_num;
			double latitude_num;
			COLORREF color;	
			color=RGB(30,144,255);
			CString start_str=user[index].name+" 的起点在这",end_str=user[index].name+" 的终点在这";
			CString journey_str;
				CDC *pDC;
			
				pDC = m_map01.GetDC(); // m_map是picture控件对应的变量 
			//将兼容dc上的绘图,拷贝到目标控件上
				pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_MemDC,0,0,SRCCOPY); 

			for(i=0;i<6;i++)
			{	//虚假的斜截式
				k=(user[index].long_end-user[index].long_start)/(user[index].lati_end-user[index].lati_start);
				x=user[index].lati_start+(i/5.0)*(user[index].lati_end-user[index].lati_start);
				x0=user[index].lati_start;
				y0=user[index].long_start;
				y=k*(x-x0)+y0;  //真正的斜截式
				
			journey_str=user[index].name+"的旅途经过这里~";

		
			longitude_num=((42.1-y)/0.4)*580;  //经纬度转换坐标
			latitude_num=((x+87.8000)/0.2506)*275;
		/*  检验代码
			str1.Format("%lf",longitude_num);
			str2.Format("%lf",latitude_num);
			str3.Format("%lf",user[index].lati_end);
			str4.Format("%lf",user[index].lati_start);

			str1=str1+"\n"+str2+"\n"+str3+"\n"+str4;
			MessageBox(str1);
		*/

			//绘图部分
			
			DrawEllipse(int(latitude_num),int(longitude_num),5,color);  
			//画圆成功,但是要去改一下原函数(或者将00坐标设置为左上角)
			/*	pDC->SetPixel(latitude_num, longitude_num, color);
			
*/			//文本的制作
			if(0<i&&i<5) pDC->DrawText(journey_str,CRect(int(latitude_num)+6, int(longitude_num)+50,int(latitude_num)+200,int(longitude_num)-50),DT_SINGLELINE|DT_LEFT|DT_VCENTER);
			else if(i==0)pDC->DrawText(start_str,CRect(int(latitude_num)+6, int(longitude_num)+50,int(latitude_num)+200,int(longitude_num)-50),DT_SINGLELINE|DT_LEFT|DT_VCENTER);
			else {pDC->DrawText(end_str,CRect(int(latitude_num)+6, int(longitude_num)+50,int(latitude_num)+200,int(longitude_num)-50),DT_SINGLELINE|DT_LEFT|DT_VCENTER);
			}
			Sleep(1000);

			}
		} 		
}
else 
MessageBox("请选择一个用户。");
}

原理很简单第一个MFC程序——自行车站点查询系统_第5张图片在listbox框内选择一个,传入一个index值,并把对应的用户信息调出来,以messagebox的方法显示出来,有更好的方法可以显示出来,但笔者是直男,直男审美嘛,就不要太在意了
调出信息后可以点击查看用户轨迹,原理是两点成线,已知两点坐标,可以求出这条直线,再在起始点之间分隔出几个点,进行标明或者连线就可以了(我这里使用的方法是用点来标明)

查找站点

void CCS20180710128Dlg::OnBUTTON_search() 
{if(S_FILE_OPEN_FLAG==1){
	if(m_search_num==0)  //num按钮选中时为0,name按钮选中时为1,其余状况是没选到
	{
		int nIndex=half_saerch(&station_num,_ttoi(m_search));
		if(nIndex!=(-1))
		{
		UpdateData(true);
		CStringArray* result = DivString(temp_str.GetAt(nIndex)); 
		m_no=result->GetAt(0);  //站点序号
		m_name1	=result->GetAt(1);  //站点名
		m_adress1=result->GetAt(2);  //站点地址
		m_sum1=result->GetAt(3);  //站点单车总数
		m_useful1=result->GetAt(4);  //可用单车数目
		m_serviec1=result->GetAt(5);	//服务状态
		m_long1=result->GetAt(6);  //经度
		m_lati1=result->GetAt(7);  //纬度
		m_longlati1=result->GetAt(8)+","+result->GetAt(9);  //经纬度
		UpdateData(false);
		MessageBox("查找成功,该站点信息已在右侧呈现。");
		}
		else
		{
			MessageBox("差找不到该序号站点,请检查导入文件是否包含该站点。");
		}

	}
	else if(m_search_num==1)
	{	int IS_FIND=0;
		int nIndex;
		for(nIndex=0;nIndex<station_name.GetSize();nIndex++)
		{
			if((station_name.GetAt(nIndex)).Find(m_search,0)!=(-1))
			{
				IS_FIND=1;
				break;
			}

			
		}
		if(IS_FIND) 
		{
			UpdateData(true);
			CStringArray* result = DivString(temp_str.GetAt(nIndex)); 
			m_no=result->GetAt(0);  //站点序号
			m_name1	=result->GetAt(1);  //站点名
			m_adress1=result->GetAt(2);  //站点地址
			m_sum1=result->GetAt(3);  //站点单车总数
			m_useful1=result->GetAt(4);  //可用单车数目
			m_serviec1=result->GetAt(5);	//服务状态
			m_long1=result->GetAt(6);  //经度
			m_lati1=result->GetAt(7);  //纬度
			m_longlati1=result->GetAt(8)+","+result->GetAt(9);  //经纬度
			UpdateData(false);
			MessageBox("查找成功,该站点信息已在右侧呈现。");	
		}
		else
		{
			MessageBox("差找不到该站点名,请检查导入文件是否包含该站点。");
		}
	}
	else MessageBox("请选择一个选项哦。");
	}
else MessageBox("未检测到导入文件,请先导入文件");
}

half_search();折半查找,因为查找对象是有序的,用折半查找法进行查找,是最优查找方法
其定义:

int half_saerch(CArray <int,int> *C, int num) //者半查找
{
	int low=0,high=C->GetSize()-1;
	int mid=(low+high)/2;
	while(low<=high)
	{
		if(C->GetAt(mid)==num) return mid;
		else if(num>C->GetAt(mid)) low=mid+1;
		else if(num<C->GetAt(mid)) high=mid-1;
		mid=(low+high)/2;
	}
	return -1;
}

第一个MFC程序——自行车站点查询系统_第6张图片
station_num和station_name是两个全局的数组,用来储存站点的序号和名称数据,其存入数据的过程再文件导入那一部分中
根据名称查找用的方法则是字符串匹配,名字的话没有什么规律,所以只能通过循环一个个的看一遍

输入框输入变量m_search和按钮变量m_search_num,当选中“序号”按钮的时候返回值为0,选择“名称”的时候按钮返回值为1,搭配if语句来选择是以什么形式进行查询
者半查找法是数据结构的内容,搬过来改了一下,原理是比较简单的,不懂得小伙伴可以参考一下论坛上的文章,通俗易懂。

好像没了?

功能的话,好像就没了吧,虽然作为一个新手,不怎么会做是正常,但花了四五天的时间才弄成一个这个东西,效率是不太好的,毕竟摸鱼是人的本性。
文章的初心是记录自己的学习经验经历,讲解的东西看不懂也很正常,只能说是一个思路吧(一周的制作经历告诉我,思路比实现过程重要得多,有思路,大概率就能将实现过程实现,但是没有思路,就可能要一个地方试错很多次)这对效率来说是一个很大的分界。
希望能帮到有兴趣或者正在制作作业的你,这是我的第一篇文章。
好吧,看我这篇没营养的东西估计没什么用,倒不如贴点实在的链接:
MFC中 CDC类祥解
combox(组框)的运用
如何将CString类型转换成int整形?
实现CDC绘图重绘
关于CList的用法
数组CArray操作方法
Listbox控件的使用
0基础MFC如何建立第一个对话框?
emmm,就酱吧“_^

你可能感兴趣的:(C++)