Windows 开发之VC++垃圾清理程序软件
概述
详细
一、简要概述
先来上个图吧:
下面就是详细介绍开发这个小程序的大体方法和步骤吧。程序是基于VC++程序设计与开发为背景,着重分析了垃圾文件清理原理和对话框程序界面的设计与开发原理,首先简要介绍VC++程序设计开发的基本方法;然后说明垃圾文件清理的原理和以绘制位图技术为背景的对话框绘制界面技术,最后重点介绍了垃圾清理程序的设计与实现。
1:概述:
本程序的主要实现垃圾文件清理的功能,即对指定的文件格式的临时文件或垃圾文件进行遍历、扫描、显示、删除清理等功能。在程序界面设计方面,对默认对话框重新自定义绘制,主要包括标题栏的重绘、对话框边框的重绘、对话框背景重绘、以及最小化按钮、最大化按钮和关闭按钮等的重绘实现。经过界面的设计和功能的实现开发,从而开发出一款具有实用意义的垃圾清理工具。
2:开发环境:
程序开发平台是基于Microsoft Visual Studio 2008 集成开发环境,编程技术采用Visual C++(MFC) 编程技术,以及相关的开发软件如Photoshop CS5等。
3:需求分析:
本程序的设计与开发主要分为两大模块,功能的设计开发和应用程序界面的设计开发。
功能的分析与设计:垃圾清理功能主要包括文件遍历扫描、显示已扫描到的文件以及垃圾文件的删除清理等。用户需要一边进行文件扫描,另一边可以对已经扫描到的垃圾文件进行清理操作。文件扫描通常会占用大量的时间,为了提高垃圾清理的可靠性和效率,应该使用多线程开发技术,即将文件扫描的任务放置在一个单独的线程中即可。
应用程序界面设计:在对话框重绘中,使用的主要技术有两个,一个是绘制对话框的背景位图,在对话框大小改变时能够输出位图,使位图能够适应对话框的大小。另一个是在对话框的指定区域输出位图。
二、核心功能设计、开发和实现
下面我们就是开始代码实现了,下面先开始实现功能部分,即垃圾文件的扫描、显示和清理。
功能的分析与设计:垃圾清理功能主要包括文件遍历扫描、显示已扫描到的文件以及垃圾文件的删除清理等。用户需要一边进行文件扫描,另一边可以对已经扫描到的垃圾文件进行清理操作。文件扫描通常会占用大量的时间,为了提高垃圾清理的可靠性和效率,应该使用多线程开发技术,即将文件扫描的任务放置在一个单独的线程中即可
(1)创建一个基于对话框的工程,工程名称为“ClearTmpFile”。
(2)向对话框中添加静态文本框、按钮、组合框、列表框、进度条等控件,效果如下:
控件布局图
(3)在对话框类CClearTmpFileDlg中添加共有的主要数据成员,各成员功能见注释部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 | CList<CString, CString> m_fileExtList; //记录需要查找临时文件扩展名 bool m_bThreadExit; //线程是否退出 bool m_bFinding; //是否查找进行中 HANDLE m_hThread; //查找文件的线程句柄 HANDLE m_hThread2; //bmp旋转线程句柄 CString m_szCurDisk; //查找的磁盘 HANDLE m_hEvent; //事件对象,在对话框关闭时将提前结束查找 bool m_bContinue; //判断暂停或继续按钮操作 DWORD GetDiskSize( char * strPath); //获取磁盘容量(已使用的) DWORD m_dwDiskVol; //磁盘总容量大小,单位为KB DWORD m_dwScanedVol; //已扫描的文件的容量 DWORD m_dwScanedTmpFileVol; //扫描到的临时文件的容量大小 DWORD m_dwScanedTmpFileNum; //扫描到的临时文件的容量大小 |
(4)向对话框类中添加ResearchFile方法,判读指定的目录,将指定的垃圾文件类型显示在扫描结果列表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | void CClearTmpFileDlg::ResearchFile( char * pszPath) { char szTmp[MAX_PATH]={0}; //定义一个临时字符数组 strcpy(szTmp,pszPath); if(szTmp[strlen(szTmp)-1]!='\\')//将目录以“\\*.*”形式结尾 { strcat (szTmp, "\\*.*" ); //连接字符串 } else { strcat (szTmp, "*.*" ); //连接字符串 } WIN32_FIND_DATA findData; //定义一个文件查找数据结构 memset (&findData,0, sizeof (WIN32_FIND_DATA)); HANDLE hFind = FindFirstFile(szTmp,&findData); //开始查找文件 //由于查找是在线程中进行的,这里判读用户是否退出线程,如果是则提前结束线程函数 if(m_bThreadExit) { FindClose(hFind); //关闭查找句柄 SetEvent(m_hEvent); //设置事件为有信号 return ; } if (hFind != INVALID_HANDLE_VALUE) //文件查找成功 { while(FindNextFile(hFind,&findData)==TRUE)//查找下一个文件 { //由于查找是在线程中进行的,这里判读用户是否退出线程,如果是则提前结束线程函数 if (m_bThreadExit) { FindClose(hFind); //关闭查找句柄 SetEvent(m_hEvent); //设置事件为有信号 return ; } //如果文件不是一个目录 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { DWORD dwFileSize = (findData.nFileSizeHigh* (MAXDWORD+1) + findData.nFileSizeLow)/(1024); //获取文件大小,单位为KB m_dwScanedVol += dwFileSize; //累计已扫描文件的容量大小,单位为KB //m_dwScanedVol = m_dwScanedVol/1024;//单位转换为:MB //设置进度条进度 m_progressCtl.SetPos((m_dwScanedVol/1024)); char szFileName[MAX_PATH] = {0}; //定义字符数组,存储完整的文件名 strcpy (szFileName,pszPath); //获取完整文件名 if (szFileName[ strlen (szFileName)-1] != '\\' ) { strcat (szFileName, "\\" ); } strcat (szFileName,( char *)findData.cFileName); if (IsTmpFile(szFileName)) //判断szFileName是否是临时文件 { m_dwScanedTmpFileVol += dwFileSize; //累计扫描到的临时文件容量大小,单位为KB m_dwScanedTmpFileNum ++; //累计扫描到的临时文件的数目 m_listBoxResults.AddString((LPCTSTR)szFileName); } } else //如果文件是一个目录,则递归遍历该目录 { if((strcmp((const char *)&findData.cFileName,"...")!=0) && ( strcmp (( const char *)&findData.cFileName, ".." )!=0)&& ( strcmp (( const char *)&findData.cFileName, "." )!=0)) { char szFileName[MAX_PATH]={0}; strcpy (szFileName,pszPath); //获取完整文件名 if (szFileName[ strlen (szFileName)-1]!= '\\' ) { strcat (szFileName, "\\" ); } strcat (szFileName,( char *)findData.cFileName); //由于查找是在线程中进行的,这里判读用户是否退出线程,如果是则提前结束线程函数 if (m_bThreadExit) { FindClose(hFind); //关闭查找句柄 SetEvent(m_hEvent); //设置事件为有信号 return ; } ResearchFile(szFileName); //递归调用 } } } } FindClose(hFind); //关闭文件查找句柄} |
(5)定义线程函数,用来单独执行扫描查找垃圾文件任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | DWORD _stdcall FindTmpFile( LPVOID lpParameter) { CClearTmpFileDlg* pDlg = (CClearTmpFileDlg*) lpParameter; //获取线程参数 WaitForSingleObject(pDlg->m_hEvent,INFINITE); //等待事件有信号 CString dir = pDlg->m_szCurDisk.GetBuffer(); //根据当前盘符目录磁盘目录 char *s = ( LPSTR )( LPCTSTR )dir; pDlg->ResearchFile(s); pDlg->Restore(); pDlg->ShowResultText(); //显示扫描临时文件的数目和大小 //恢复数据为初始状态 pDlg->m_dwScanedTmpFileVol = 0; pDlg->m_dwScanedTmpFileNum = 0; pDlg->m_fileExtList.RemoveAll(); return 0; } |
(6)处理“立即扫描”或“开始”按钮的单击事件,创建一个新的线程执行扫描文件的任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //如果查找没有结束,则不允许开始新的文件查找 GetDlgItem(IDC_BEGIN)->ShowWindow(SW_HIDE); if (!m_bFinding && GetTmpExtName()) //获取文件扩展名 { GetDlgItem(IDC_PROGRESS1)->ShowWindow(TRUE); //显示进度条 GetDlgItem(IDC_LIST1)->ShowWindow(SW_SHOW); m_bThreadExit = FALSE; m_bFinding = TRUE; m_combox.GetWindowText(m_szCurDisk); //获取当前盘符 // 初始化进度条相关数据 m_dwDiskVol =GetDiskSize(( LPSTR )( LPCTSTR )m_szCurDisk); //获取当前磁盘的容量大小(已使用的) CString str; str.Format(_T( "%d" ),m_dwDiskVol/(1024)); double iSize = atoi (str); m_progressCtl.SetRange32(0,m_dwDiskVol/1024); //初始化进度条,设置进度条的范围,范围为MB的数量 if (m_hEvent!=NULL) { CloseHandle(m_hEvent); //关闭事件对象 m_hEvent = NULL; } m_listBoxResults.ResetContent(); //清空查找结果列表 m_hEvent = CreateEvent(NULL,FALSE,TRUE,_T( "Event" )); //创建事件对象 //创建一个线程,开始执行线程函数 m_hThread = CreateThread(NULL,0,FindTmpFile, this ,0,NULL); m_hThread2 = CreateThread(NULL,0,RotatingImg, this ,0,NULL); UpdateData(FALSE); |
(7)清理已扫描到的垃圾文件,即采用删除文件策略,使用DeleteFile()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void CClearTmpFileDlg::OnBnClickedDelall() { //删除已扫描到的垃圾文件 CString strDel; CFile file; for ( int i=0;i<m_listBoxResults.GetCount();i ++) { m_listBoxResults.GetText(i,strDel); GetDlgItem(IDC_TEST)->SetWindowText(strDel); DeleteFile(strDel); //删除指定路径的文件 } m_listBoxResults.ResetContent(); GetDlgItem(IDC_TEST)->SetWindowText(_T( "清理完毕!" )); GetDlgItem(IDC_LIST1)->ShowWindow(SW_HIDE); GetDlgItem(IDC_BEGIN)->ShowWindow(SW_SHOW); GetDlgItem(IDC_BEGIN)->SetWindowText(_T( "重新扫描" )); } |
好了,到此差不多软件的主要功能部分都已经开发完毕,大家已经看出来了,功能部分很简单,就是对指定的文件格式进行扫描、显示和清理。下面将进行对软件界面的设计与开发。
三、程序软件的UI界面设计与实现
对应用程序界面的设计包括两部分,一部分是对话框自身的重设计,二是对话框控件的重绘,本程序主要对按钮控件进行重绘设计。
1. 绘制对话框的背景位图
绘制对话框背景位图本文采用的是处理对话框的WM_PAINT消息,该消息初始化时候对对话框进行绘制,从而绘制背景位图。绘制背景位图的主要代码如下:
1 2 3 4 | CRect rect; CPaintDC dc( this ); GetClientRect(&rect); //获取客户区 //设置对话框背景颜色 dc.FillSolidRect(rect,RGB(14,94,157)); //设置为窗口背景 |
2. 在指定的区域中输出位图
为了能够在指定的区域中输出位图,需要使用设备上下文CDC类的StretchBlt方法。由于我们需要在窗口的非客户区域绘制位图,因此需要使用CWindowDC类的StretchBlt方法, CWindowDC类派生与CDC类,它提供了在窗口非客户区域绘制位图的功能。该方法数从源矩形中复制一个位图到目标矩形,必要时按目前目标设备设置的模式进行图像的拉伸或压缩。输出位图的主要实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | CRect winRC; CDC* pDC=GetWindowDC(); //获取窗口设备上下文 CDC memDC; memDC.CreateCompatibleDC(pDC); //创建兼容内存位图 BITMAPINFO bmpInfo; CBitmap bmp; //定义位图对象 GetWindowRect(&winRC); bmp.LoadBitmap(nID); //加载位图 bmp.GetObject( sizeof (BITMAPINFO),&bmpInfo); //获取位图信息 int nBmpCX = bmpInfo.bmiHeader.biWidth; //获取位图宽度 int nBmpCY = bmpInfo.bmiHeader.biHeight; //获取位图高度 memDC.SelectObject(bmp); //选中位图对象 pDC->StretchBlt(x,y,w,h, &memDC,0,0,nBmpCX,nBmpCY,SRCCOPY); //在窗口中绘制位图 bmp.DeleteObject(); //释放位图对象ReleaseDC(pDC);//释放DC |
3. 对话框界面设计与绘制的实现:
在对话框重绘的设计与实现过程中,一般需要绘制的对话框区域主要有标题部分、边框部分和客户区部分。具体的区域划分如下图所示。
既然要对多个区域进行位图显示输出,所以我们先封装一个bmp位图显示输出函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void CClearTmpFileDlg::DisplayBmp( int x, int y, int w, int h, int nID) { //nID 表示位图资源的ID CRect winRC; CDC* pDC=GetWindowDC(); CDC memDC; memDC.CreateCompatibleDC(pDC); BITMAPINFO bmpInfo; CBitmap bmp; GetWindowRect(&winRC); bmp.LoadBitmap(nID); bmp.GetObject( sizeof (BITMAPINFO),&bmpInfo); int nBmpCX = bmpInfo.bmiHeader.biWidth; int nBmpCY = bmpInfo.bmiHeader.biHeight; memDC.SelectObject(bmp); pDC->StretchBlt(x,y,w,h, &memDC,0,0,nBmpCX,nBmpCY,SRCCOPY); //在窗口中绘制位图 bmp.DeleteObject(); ReleaseDC(pDC); } |
然后就是对各个区域进行位图输出重绘。由于标题栏以及边框主要都是非客户区域绘制,因此应该在WM_NCPAINT 消息中绘制。当然得先通过添加资源的方式将所用到的bmp位图资源导入到项目中。
在 WM_NCPAINT消息对于的 方法OnNcPaint()中调用对话框绘制方法DrawDialog()。该方法的功能就是绘制对话框各个区域的位图。主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | void CClearTmpFileDlg::DrawDialog() { //重绘对话框标题栏、边框、最小化按钮、最大化按钮和关闭按钮等界面 m_nFrameCY = GetSystemMetrics(SM_CYFIXEDFRAME); //获取对话框边框的高度 m_nFrameCX = GetSystemMetrics(SM_CXDLGFRAME); //获取对话边框的宽度 if (GetStyle()&WS_BORDER) //获取对话框是否有边框 { m_nBorderCY = GetSystemMetrics(SM_CYBORDER) + m_nFrameCY; m_nBorderCX = GetSystemMetrics(SM_CXBORDER) +m_nFrameCX; } else { m_nBorderCY = m_nFrameCY; m_nBorderCX = m_nFrameCX; } m_nTitleBarCY = GetSystemMetrics(SM_CYCAPTION) + m_nBorderCY; //计算标题栏高度 m_nTitleBarCX =m_nBorderCX; CRect winRect,factRect; GetWindowRect(&winRect); factRect.CopyRect(CRect(0,0,winRect.Width(),winRect.Height())); CWindowDC windowsDC( this ); //获取窗口设备上下文 //获取整个MFC窗口的高度和宽度 m_nWinWidth = winRect.Width(); //=781 m_nWinHeight = winRect.Height(); //=459 //绘制对话框左标题栏位图 DisplayBmp(0,0,100,m_nTitleBarCY,IDB_LEFTTITLE); //绘制对话框标题栏左端的logo图标 DisplayBmp(3,0,26,m_nTitleBarCY,IDB_APPICON); //绘制对话框右标题栏位图 DisplayBmp(m_nWinWidth-100,0,100,m_nTitleBarCY,IDB_RIGHTTITLE); //绘制对话框中标题栏位图 DisplayBmp(100,0,m_nWinWidth-200,m_nTitleBarCY,IDB_MIDTITLE); //绘制对话框左边框位图 DisplayBmp(0,m_nTitleBarCY,m_nBorderCX,m_nWinHeight-m_nBorderCY,IDB_LEFTBAR); //绘制对话框底边框位图 DisplayBmp(m_nBorderCX,m_nWinHeight-m_nBorderCX,m_nWinWidth-2*m_nBorderCX,m_nBorderCX,IDB_BOTTOMBAR); //绘制对话框左边框位图 DisplayBmp(m_nWinWidth-m_nBorderCX,m_nTitleBarCY,m_nBorderCX,m_nWinHeight-m_nBorderCY,IDB_RIGHTBAR); //给对话框绘制最小化按钮 DisplayBmp(m_nWinWidth-26*3-5,0,26,26,IDB_MINBTN1); //给对话框绘制最大化按钮 DisplayBmp(m_nWinWidth-26*2-5,0,26,26,IDB_MAXBTN1); //给对话框绘制关闭按钮 DisplayBmp(m_nWinWidth-26-5,0,26,26,IDB_CLOSEBTN1); ReleaseDC(&windowsDC); //ReleaseDC(&memDC); DrawTitleBarText(); //输出标题栏文本} |
上面代码中最后的绘制对话框标题文本的方法DrawTitleBarText(),主要是用来显示标题栏的文本,其主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | CString strTitle = "小蔡垃圾清理器3.0" ; CDC* pDC= GetWindowDC(); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(255,255,255)); pDC->SetTextAlign(TA_CENTER); CRect rect; GetClientRect(&rect); CSize szText = pDC->GetTextExtent(strTitle); CFont* font,*fOldFont; font = new CFont; font->CreateFont(12,0,0,0,FW_BOLD,FALSE,FALSE,0,ANSI_CHARSET, OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_SWISS,_T( "宋体" )); fOldFont = pDC->SelectObject(font); pDC->TextOut(100,6.5,strTitle,18); pDC->SelectObject(fOldFont); ReleaseDC(pDC); |
在完成对话框相应区域的位图后,并没有完成任务,还需要处理标题栏按钮的热点效果,以及按钮的单击事件。首先得处理鼠标在非客户区域移动时的事件,即WM_NCMOUSEMOVE消息,在其消息处理函数中判断当前的鼠标点是否位于标题栏的按钮区域,如果是则设置按钮的热点效果,并且记录当前的按钮状态,及鼠标点在哪个按钮上。同样的,处理对话框非客户区域的单击事件,即WM_NCLBUTTONDOWN消息,在其消息处理函数中完成单击事件操作。这部分的代码比较简单,在此不予显示。
4.按钮控件UI界面设计与实现
在MFC下编程,很多时候对于标准的按钮控件不是很满意,想要弄的美观些。这就需要按钮重绘。重绘按钮一般的实现方法就是重写CButton类。
首先给工程添加一个自绘按钮类MyDrawButton,基类为CButton。要想让按钮具备自绘功能,就要为按钮添加BS_OWNERDRAW属性。为类CButton重载PreSubclassWindow虚函数。在该函数中添加如下一行代码:
SetButtonStyle(GetButtonStyle() | BS_OWNERDRAW);
当按钮控件具有了自绘功能之后,每次控件状态改变都会触发DrawItem函数,在该函数中来绘制按钮的形态外观,所以第二步就要重载DrawItem虚函数。在这个函数中就可以自由发挥了,比如绘制背景,底色,按钮标题,绘制文本字体样式等等。
一般都会为按钮定义几种不同状态时的外观,比如光标滑过时的状态,按钮按下时的状态,按钮禁用时的状态,以及按钮的正常状态等等。这就要为新的按钮添加几种重要的消息响应。比如WM_MOUSELEAVE消息,WM_MOUSEHOVER消息和WM_MOUSEMOVE消息等等,值得一提的是前两个消息的响应函数需要自己手动添加,微软提供了一个TrackMouseEvent函数在光标离开一个窗口时投递WM_MOUSELEAVE消息,光标滑过窗口时投递WM_MOUSEHOVER消息。一般来说可以在WM_MOUSEMOVE消息响应函数中调用TrackMouseEvent函数来投递WM_MOUSELEAVE消息和WM_MOUSEHOVER消息。然后在WM_MOUSELEAVE消息的响应函数中标记“光标已经离开按钮”,然后调用InvalidateRect函数让按钮重绘。在WM_MOUSEHOVER消息的响应函数中标记“光标正在按钮上方”,并调用InvalidateRect函数让按钮重绘。
在本文中,重绘按钮分为3个部分。
(1)绘制按钮背景样式,即绘制背景bmp位图,使得按钮具有自定义的样式,同时在绘制按钮背景的输出位图时采用TransparentBlt()函数,该函数的作用是使窗体上显示位图的背景与窗体背景色融为一体,不仅可以显示按钮bmp位图样式,而且还可以使背景透明。
(2)就是绘制按钮上的文本。主要绘制按钮上文本的样式,包括字体大小,字体样式,字体颜色等属性。
(3)实现不同状态下的按钮的外观样式,主要包括WM_MOUSEMOVE和WM_MOUSELEAVE两个消息的消息处理函数。分别实现鼠标在按钮区域上和不在按钮区域上的状态。为了标记鼠标移动到按钮区域内停留,需要用到一个定时器来标记鼠标是否还在按钮区域内停留。在WM_MOUSEMOVE内启动定时器,触发WM_MOUSELEAVE消息时结束定时器即销毁定时器。定时器的主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void MyDrawButton::OnTimer( UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (nIDEvent != 24) return ; CPoint point; CRect rect; GetWindowRect(&rect); GetCursorPos(&point); // 如果鼠标离开按钮区域,重绘按钮 if (!rect.PtInRect(point) && m_bMove) { KillTimer (24); m_DrawState=ST_MOVEOUT; m_bMove=FALSE; Draw(); } CButton::OnTimer(nIDEvent); } |
重绘按钮类MyDrawButton的主要实现代码如下:
定义的一些重绘用到的变量:
1 2 3 4 5 6 | #define ST_MOVEIN 0//绘制状态—在按钮区域上#define ST_MOVEOUT 1 //绘制状态—不在按钮区域上int m_DrawState;//绘制状态 int m_nBmpID; //当前显示的背景bmp位图的资源ID bool m_bMove; //鼠标是否进入按钮区域 COLORREF m_clText; //当前文本颜色 COLORREF m_clActiveText; //鼠标进入按钮区域时文本颜色 COLORREF m_clNormalText; //鼠标离开按钮区域时文本颜色 |
消息处理函数和定义的函数以及实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | void MyDrawButton::PreSubclassWindow() { SetButtonStyle(GetButtonStyle() | BS_OWNERDRAW); } void MyDrawButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { Draw(); //绘制按钮}void MyDrawButton::Draw()//绘制按钮{ DrawBackground(); //绘制按钮bmp位图,并使背景透明化 DrawText(); //绘制按钮上的文本}void MyDrawButton::DrawText() { //绘制按钮上的文本的字体大小、样式等 CString itemString; CRect clientRect; CClientDC dc( this ); GetClientRect(&clientRect); GetWindowText(itemString); if (itemString) { CSize size=dc.GetTextExtent (itemString); //获得所选字体中指定字符串的高度和宽度 int rectwidth=clientRect.Width(); int rectheight=clientRect.Height(); int textwidth=size.cx ; int textheight=size.cy ; int x,y; // 文本的位置 // 计算文本的输出位置 x=(rectwidth-textwidth)/2; //水平居中 y=(rectheight-textheight)/2; //垂直居中 switch (m_DrawState) { case ST_MOVEIN: //鼠标进入按钮区域 m_clText=m_clActiveText; break ; case ST_MOVEOUT: //鼠标离开按钮区域 m_clText=m_clNormalText; break ; default : m_clText=m_clNormalText; break ; } dc.SetTextColor(m_clText); dc.SetBkMode(TRANSPARENT); CFont *font ; font = new CFont(); int fontSize = 14; font->CreateFont(fontSize,0,0,0,FW_BOLD,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_SWISS,_T( "宋体" )); dc.SelectObject(font); dc.TextOut (x,y,itemString); } } void MyDrawButton::SetBkBmp( int nBmpID) { //设置按钮bmp位图样式 m_nBmpID = nBmpID; } void MyDrawButton::DrawBackground() { //绘制按钮bmp位图,并使背景透明化 CRect winRC; CDC* pDC=GetWindowDC(); CDC memDC; memDC.CreateCompatibleDC(pDC); BITMAPINFO bmpInfo; CBitmap bmp; GetWindowRect(&winRC); bmp.LoadBitmap(m_nBmpID); bmp.GetObject( sizeof (BITMAPINFO),&bmpInfo); int nBmpCX = bmpInfo.bmiHeader.biWidth; int nBmpCY = bmpInfo.bmiHeader.biHeight; memDC.SelectObject(bmp); pDC->TransparentBlt(0,0,nBmpCX,nBmpCY,&memDC,0,0, nBmpCX,nBmpCY,RGB(14,94,157)); //在窗口中绘制位图,RGB(14,94,157)是透明色 bmp.DeleteObject(); ReleaseDC(pDC); } |
到此,按钮的自定义重绘完成了,接下来就可以使用自己重绘的按钮类MyDrawButton了。首先往对话框中添加一个按钮控件(以立即扫描按钮为例),假设它的ID值为IDC_TEST。进入类向导(Class Wizard)的成员变量属性页,为IDC_ BEGIN添加一个变量m_btnBegin。如下:
MyDrawButton m_btnBegin;
然后就可以调用MyDrawButton的方法来设置按钮的样式了。如下:
m_btnBegin.SetBkBmp(IDB_BTN210x95,IDB_BTN210x95_3);//IDB_BTN210x95,IDB_BTN210x95分别为默认位图和鼠标在按钮区域时的位图。
到现在为止,按钮类的重绘完成了,可以随意定义自己喜欢的样式的按钮了。现在相对完善成形的一个垃圾清理工具软件就开发完了。
三、运行效果
最后软件的主要成果界面如下:
软件打开准备就绪界面
正在运行截面图
扫描完成界面图:
清理完成界面图
四、项目结构图
这里补上,项目代码结构图
项目结构图
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?