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

一个自行车站点查询系统

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

说说功能

在这里插入图片描述目前是长这样的,没有设计天赋,所以外观样式就看且看吧。
先说一下怎么使用这个东西,在文件包里有个 公共自行车站点信息.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); 用于负责信息的更新显示在静态文本控件上在这里插入图片描述对应代码段分割的9段
在这里插入图片描述
类向导中的变量设置,设置完成后,摆放到特定的位置,就能在点击响应后获取信息并更新

坐标绘制

坐标点的着色绘制涉及到两个功能,显示站点、查询用户轨迹,原理都很简单
首先要声明绘图函数 在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("请选择一个用户。");
}

原理很简单在这里插入图片描述在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;
}

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

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

好像没了?

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

发布了5 篇原创文章 · 获赞 1 · 访问量 375

猜你喜欢

转载自blog.csdn.net/weixin_43218670/article/details/103853574