第5章FTP客户端实现之二•前面的章节4实现过一个FTP客户端程序,那么为什么这里又要实现呢?区别有二,FTP客户端所基于的应用程序框架不同,第4章基于对话框,本章将基于SDI开发;开发时的精力分配不同,第4章的精力主要集中在与FTP服务器的“交流”上,本章将把这种底层的工作交给MFC封装的类来实现,主要精力会集中在界面的美化上。5.1FTP客户端简介•这节将会带领大家快速了解下本章将要实现的FTP客户端的各种功能。包括:以树形视图浏览本地文件夹资源、列表方式显示FTP服务器上的文件资源、用过拖动文件的方式实现文件的上传和下载。5.1.1树形结构的应用•在主窗体的左侧视图中显示选定本地文件夹内的所有文件资源,结构为树形,可以动态的改变本地文件夹的选择,如图5.1所示。前方有加号说明路径中还有子路径,单击加号打开此路径,加号变减号,子文件将显示在子树中。鼠标移过此视图时树子项会加亮显示。图标H表示文件夹、图标F表示文件。图5.1.本地文件夹资源显示加亮显示的文件夹5.1.2列表结构的应用•在主窗体的右侧视图中,将以列表图标的形式显示FTP服务器下的所有文件资源,如图5.2所示。图5.2FTP服务器上文件资源5.1.3信息框的应用•在主窗体的正中央有个信息的显示框,是用于描述用户的一些操作,如图5.3所示。图5.3信息显示框5.1.4浮动对话框的应用•主程序的最顶端是用来填写本地文件夹路径和连接FTP服务器的浮动对话框,如图5.4所示。图5.4浮动对话框•那么,最后来看一下本章FTP客户端的全貌吧,如图5.5所示。图5.5FTP客户端全貌5.2关键技术讲解•这节主要介绍本章要用到的所有关键技术,包括如何制作浮动对话框,然后像工具栏一样加到菜单之下;如何将客户区分栏;如何实现对树形和列表视图项目的拖动。•我们创建的工程是基于SDI的,命名为FtpClient。在向导的第6步,我们选择CFtpClientView基于CListView类,如图5.6所示。图5.6修改CFtpClientView的基类5.2.1制作、使用浮动对话框•浮动对话框,顾名思义就是可以浮动在主窗体之上。制作方法如下:•(1)在资源视图中插入对话框资源,修改ID为IDD_FLOAT_DLG,修改属性:去掉对话框的边缘,将style改为child,如图5.7所示。图5.7对话框属性设置•(2)为对话框拖放控件,然后进行设计,怎么好看怎么摆放吧!我的设计如图5.8所示。你们在前面见过了,但还是想让大家看看,因为我增加了新内容。图5.8浮动对话框界面设计•(3)在CMainFrame中添加一个浮动对话框的变量m_myDlg。•在CMainFrame的OnCreate()成员函数中完成两个任务:去掉由向导为我们添加的工具栏和状态栏,它们影响到了我们程序的美观;添加显示我们刚才设计的对话框的代码。IDC_DISCIDC_IPIDC_NAMEIDC_PASSWORDIDC_CHOOSEDISCIDC_PORTIDC_CONNECT•程序中去掉了工具栏和状态栏的功能,所以你可以将代表两个工具的对象m_wndStatusBar、m_wndToolBar也注释掉,它们就定义在类CMainFrame的头文件中,如下:•不注释掉也并不会影响程序的编译执行,读者可自由处理。•通过调用类CDialogBar的成员函数Create(),装载我们设计的对话框资源模版、创建对话框窗口、设置它的样式,最后关联窗口到CDialogBar对象m_myDlg上。函数原型如下:•virtualBOOLCreate(•CWnd*pParentWnd,•UINTnIDTemplate,•UINTnStyle,•UINTnID•);•参数含义如下:•pParentWnd:指向装载浮动对话框的父窗口的指针,我们直接使用了this。•其它成员函数如EnableDocking()的使用很简单,代码中已经加入了注释,就不做详解讲解了。•那么,编译运行程序就会发现工具栏、状态栏消失了,取而代之的是我们自己设计的浮动对话框,用鼠标尝试拖动它,会有如图5.9所示效果。图5.9拖动浮动对话框•你当然可以任意移动它,甚至是单击它上面的关闭按钮来关闭它,不过关闭了以后就比较麻烦了,因为我们需要重新启动程序来让它再次显示。本程序没有实现再次显示浮动窗口功能,读者可以自己实现下。5.2.2客户区的分割•我们需要将整个客户区分割为3个部分:用于显示用户操作的信息窗口、用于显示本地文件夹资源的树形视图窗口和用于显示FTP服务器上文件资源的列表视图窗口。效果如图5.10所示。图5.10窗口分割效果图•为类CMainFrame添加两个成员变量,如下:•类CSplitterWnd提供了分割窗口的功能,就是一个窗口包含多个窗格。•为类CMainFrame添加虚函数OnCreateClient()的实现,程序编写如下:用户操作信息窗口树形视图窗口列表视图窗口•调用类CSplitterWnd的成员函数CreateStatic()创建静态的分割窗口,函数原型如下:•virtualBOOLCreateStatic(•CWnd*pParentWnd,•intnRows,•intnCols,•DWORDdwStyle=WS_CHILD|WS_VISIBLE,•UINTnID=AFX_IDW_PANE_FIRST•);•参数含义如下:•pParentWnd:分割窗口的父框架窗口。•第一次分割窗口时,父窗口是框架CMainFrame,分割成2行1列。第二次分割窗口时,是嵌入在第一次分割的窗口中的,所以父窗口为m_splitter1,分割为1行2列,嵌套的窗口ID通过类CSplitterWnd的成员函数IdFromRowCol()获得。•类CSplitterWnd的成员函数CreateView()为静态分割窗口创建窗格,原型如下:•virtualBOOLCreateView(•introw,•intcol,•CRuntimeClass*pViewClass,•SIZEsizeInit,•CCreateContext*pContext•);•参数含义如下:•row:指定放置新视图的窗口行。•调用类CSplitterWnd的成员函数SetRowInfo()和SetColumnInfo()分别设置分割窗口的行高取值范围、列宽取值范围。函数原型如下:•voidSetRowInfo(•introw,•intcyIdeal,•intcyMin•);•voidSetColumnInfo(•intcol,•intcxIdeal,•intcxMin•);•参数含义如下:•row、col:指定分割窗口的行、列,用于定位。•在函数OnCreateClient()中,我们将3个视图CMsgShow、CFileTree和CFtpClientView指定到相应的分割窗格中。前两个是我们利用类向导添加的新类,分别基于类CEditView和CTreeView,最后一个是我们创建工程时由向导为我们创建的视图类,基于类CListView。至此客户区分割的操作代码填加完毕。5.2.3树形视图项目拖动效果•我们可以通过捕获3个事件来添加拖动效果的代码,他们是:鼠标左键选中项目并且开始拖动、鼠标移动和鼠标左键抬起。1.选中视图项•我们需要用类向导添加一个新类CFileTree,基于CTreeView,如图5.11所示。图5.11添加新类CFileTree•在类CFileTree的实现文件,添加文件包含指令如下:•#includeMainFrm.h•#includeFtpClientView.h•在利用类向导为它添加函数OnBegindrag(),如图5.12所示。图5.12为类CFileTree添加消息响应•为函数OnBegindrag()添加代码,如下:•函数OnBegindrag()中的变量是类CFileTree的公有成员变量,定义如下:•成员变量在类CFreeTree的构造函数初始化如下:•类CTreeCtrl的成员函数GetTreeCtrl()返回树视图控件的引用。函数OnBegindrag()中结构NM_TREEVIEW定义如下:•typedefstruct_NM_TREEVIEW{•NMHDRhdr;•UINTaction;•TV_ITEMitemOld;•TV_ITEMitemNew;•POINTptDrag;•}NM_TREEVIEW;•参数含义如下:•hdr:另一个包含通知消息信息的结构NMHDR。•我们要从这个结构中获取两个信息:itemNew.hItem和ptDrag。前者是个结构TV_ITEM,用来指定或返回树视图项的属性。结构TV_ITEM的字段hItem放的是这个结构指向树视图项的句柄HTREEITEM,被保存在了m_hItemDragS变量中。•然后用到类CTreeCtrl的1个成员函数和CImageList的2个成员函数完成图像拖动的准备工作,它们是:CreateDragImage()用来为指定的树视图项创建拖动时的位图、BeginDrag()标志拖动位图操作的开始、DragEnter()用来在拖动操作期间在指定的位置显示位图和锁定更新。•函数BeginDrag()的原型如下:•BOOLBeginDrag(•intnImage,•CPointptHotSpot•);•参数含义如下:•nImage:索引号从0开始的位图号,用来指定位图。•函数DragEnter()的原型如下:•staticBOOLPASCALDragEnter(•CWnd*pWndLock,•CPointpoint•);•参数含义如下:•pWndLock:指向拥有拖动图像的窗口指针。若参数赋值为NULL,这个函数拖动图像的坐标是相对于桌面窗口的,即屏幕坐标的左上角。•所以我们在使用函数DragEnter()的时候,用到类CWnd的成员函数ClientToScreen(),将给定的客户区点坐标转换为屏幕点坐标。最后我们同样调用类CWnd的成员函数SetCapture(),以后不管鼠标的位置在哪里,所有的鼠标后续输入都会被送到当前的窗口处理。•至此,鼠标左键选中项目并且开始拖动事件的捕捉和处理代码填写和解释完毕。2.图像随鼠标移动•利用类向导为类CFileTree添加下一个事件:鼠标移动。如图5.13所示。图5.13添加鼠标移动事件•为函数OnMouseMove()添加代码,如下:•函数OnMouseMove()首先调用AfxGetMainWnd(),获取指向当前程序主框架CMainFrame的指针,保存在变量mFrm中,通过此变量调用其成员变量m_splitter2的成员函数,既类CSplitterWnd的成员函数GetPane(),得到指定行列窗格的指针。这里获取的是列表视图窗格的指针,保存在变量pEView中。•我们要在类CFileTree中添加一个自定义的成员函数GetCtrlRect(),如图5.14所示。图5.14添加自定义的成员函数•添加如下很简单的一段代码就行:•简单的封装了两个函数,分别完成功能:获取树视图窗口的矩形大小,即窗口大小。然后将坐标转换为相对屏幕的坐标值。同样需要在类CFtpClientView中添加这样一个自定义的函数GetCtrlRect(),代码一样,功能当然也一样,如下:•函数OnMouseMove()接下来完成的功能是:创建两个“区域”。实例化两个类CRgn的对象listRgn和treeRgn,调用类CRgn的成员函数CreateRectRgn()创建两个矩形区域,分别覆盖了树形结构视图、列表结构视图。•函数CreateRectRgn()的原型如下:•BOOLCreateRectRgn(•intx1,•inty1,•intx2,•inty2•);•参数含义如下:•x1、y1:指定矩形区域左上角点的坐标位置。•函数OnMouseMove()最后会判断:鼠标是否处于拖动的状态,是在树形结构视图区域或者列表结构视图区域中吗.通过类CRgn的成员函数PtInReg