02实现了基本的Txt操作功能,现在还想加一个拖拽功能,即把一个Txt文本拖拽进来,然后自动打开该文本。(参考Txt拖拽,不理解的话就试一下,就是把另一个Txt拖到一个已打开的Txt中)


 (★★★★这里请千万注意,我们使用的RichEdit是视图继承的CRichEditView,而不是一个RichEdit控件,所以你会发现很多资料介绍的不适用,区别还是有的★★★★)


 知道需求了,那就去查资料呗,嘿,还真有这种功能,WM_DROPFILES消息------>>>>OnDropFiles函数;

这不就简单了,直接解决问题,开写代码,先写一个MessageBox来测试一下。。。。。显然失败了,无法弹出这个用做提示的对话框。。。。怎么办,难道不是这样的吗?---  CRichEditView如何实现文件的拖拽功能?

1 void CTxt0721View::OnDropFiles(HDROP hDropInfo)
2 {
3     // TODO: 在此添加消息处理程序代码和/或调用默认值
4     MessageBox(L"CTxt0721View    +     这不是为难老实人?");
5     CRichEditView::OnDropFiles(hDropInfo);
6 }

。。。难搞啊,然后又发现了一个EN_DROPFILES,这简直是救星,山重水复疑无路,屁用没有,还是不会响应。。。看一下这个消息对应的程序,它的使用提示告诉我们什么

void CTxt0721View::OnEnDropfiles(NMHDR *pNMHDR, LRESULT *pResult)
{
    ENDROPFILES *pEnDropFiles = reinterpret_cast<ENDROPFILES *>(pNMHDR);
    // TODO:  控件将不发送此通知,除非您重写
    // CRichEditView::OnInitDialog() 函数,以将 EM_SETEVENTMASK 消息发送
    // 到该控件,同时将 ENM_DROPFILES 标志“或”运算到 lParam 掩码中。

    // TODO:  在此添加控件通知处理程序代码

    *pResult = 0;
}

CRichEditView::OnInitDialog() 函数,OnInitDialog() 这是什么函数,。。。这不是对话框的初始化函数吗,。。在View怎么重写这个函数。。。。欸,又是走不通的路,真的是没办法。(这里反而透露出了如果在对话框中使用RichEdit控件该如何实现这个拖拽功能,但是对本文无用)


 后来我想,View不行,那我去MainFrame中试试,看看它能不能响应,

好家伙,CMainFrame中真的可以响应WM_DROPFILES消息,不过存在一点点BUG,那就是只能在框架处响应这个消息,在客户区还是无法响应,不过好歹算是解决了一部分问题,(关于怎么写函数OnDropFiles,以及怎么得到文件路径,自行百度,一堆),由于设计的文本路径 ,文本内容这些变量都是View中的成员变量,所以需要在MainFrame中获取到View指针,对这些数据进行操作,恰好有这个函数(CFrameWnd::GetActiveView ,  调用此成员函数以获取指向附加到框架窗口 (CFrameWnd) 的活动视图(如果有)的指针。)

 1 void CMainFrame::OnDropFiles(HDROP hDropInfo)
 2 {
 3     // TODO: 在此添加消息处理程序代码和/或调用默认值
 4 
 5 //    MessageBox(L"CMainFrame::      +     这不是为难老实人?");    
 6 //  获取文件路径
 7     wchar_t szFilePathName[_MAX_PATH + 1] = { 0 }; 
 8     UINT nNumOfFiles = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); //得到文件个数 
 9     if (nNumOfFiles > 1)
10     {
11         MessageBox(L"Only the first file is loaded!");
12     }
13     DragQueryFile(hDropInfo, 0, szFilePathName, _MAX_PATH);
14 //    MessageBox(szFilePathName);
15 
16     CTxt0721View * pView = (CTxt0721View *)GetActiveView();
17 //    MessageBox(L"我是文件内容" + pView->TxtFileString);
18     DragFinish(hDropInfo);
19 // ------------------------根据得到的文件路径调用 打开 函数即可
20     pView->DragAndDropOpenFile(szFilePathName);
21     pView->Number_QueryAccept = 0;            // 控制拖拽事件响应函数,框架处响应不一致,故而直接置零,控制视图处响应
22 
23     CFrameWndEx::OnDropFiles(hDropInfo);
24 }

。。。现在问题是怎么处理客户区拖拽问题。。。。。

找资料(虽然不知道怎么找资料,。。。。也不知道具体该查找什么关键字。。。反正就是找资料,广泛找,。。。皇天不负有心人,总算找到了(主要是不懂,就算找到了也可能因为看不懂而错过。。。。))

为什么CRichEditView 类无法响应OnDrop() OnDragOver()等拖放函数 急!通宵等答案 (和我一样的问题)

 既然提到了这个函数,那我们就去试试,反正什么思路都没有。。。发现问题了,这个函数确实在这中间起到了作用,拖拽过去会产生一个图标,而这个图标的产生就由QueryAcceptData控制,那是不是可以重写这个函数,截断它显示图标,改让他显示Txt的文本内容呢?理论上来说是可以的,于是从这个函数入手

通过研究,发现在一次拖拽过程中,这个函数会响应两次(当鼠标把文件拖到客户区时响应一次,当鼠标左键松开时,响应第二次)但是我们只需要它响应一次,这只能手动控制了,没办法

因此,在View.h中添加了一个变量int  Number_QueryAccept;用来专门记录调用了QueryAcceptData函数多少次,并设定,只有当Number_QueryAccept是偶数时,才做出响应,打开Txt文本。

问题又来了,前面不是在CMainFrame中响应了WM_DROPFILES消息,发现,如果是把文件拖拽到框架上,那么只会响应一次QueryAcceptData函数,这又给我们写代码的人添加工作了,由此根据这个特性,设定每次调用QueryAcceptData函数,标记Number_QueryAccept就自动加一,而当调用了CMainFrame::OnDropFiles函数时,标记Number_QueryAccept重置为0,这样解决了两者调用次数不同的问题。

事到如今,还有一个问题,怎么提取出文件路径,目前最有用的参数就是LPDATAOBJECT lpdataobj了,怎么提取出文件路径来呢?(查资料呗,我又不会)(求教怎么通过DoDragDrop参数获取源文件路径

照着抄当然时无法提取到文件路径的,其实当初这个地方查了很久,总是不知道查什么关键词。。。。不过最后还是找到了,然后我现在忘记了。。。。(真的找不到了。。。好难过。。。所以没有链接了,不是我不想给原作者链接。。。。。)(某个例子

 QueryAcceptData函数:(调用DragAndDropOpenFile函数直接打开文本)

 1 /*
 2 重载函数CRichEditView::QueryAcceptData   
 3 QueryAcceptData由框架调用以将对象粘贴到丰富的编辑中。(文件拖拽功能
 4 1.文件拖到框架上,响应此处,然后会自动调用CMainFrame中OnDropFiles(HDROP hDropInfo)函数处理
 5 2.文件拖到视图上,响应此处,但不会调用函数处理
 6 */
 7 HRESULT CTxt0721View::QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT* lpcfFormat, DWORD dwReco, BOOL bReally, HGLOBAL hMetaFile)
 8 {
 9     // TODO: 在此添加专用代码和/或调用基类
10 
11     // 控制拖拽响应 Number_QueryAccept 偶数响应
12     Number_QueryAccept++;
13 
14     // 获取当前界面文字
15     pRichEdit->GetWindowText(TxtFileString);
16 
17     // 以下用于获取拖拽文件的文件路径
18     FORMATETC fmt = { CF_HDROP,0,DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
19     STGMEDIUM stgmed = { 0 };
20     if (SUCCEEDED(lpdataobj->GetData(&fmt, &stgmed)))
21     {
22         HDROP hDropInfo = (HDROP)::GlobalLock(stgmed.hGlobal);
23         wchar_t szFilePathName[_MAX_PATH + 1] = { 0 };
24         UINT nNumOfFiles = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); //得到文件个数 
25         DragQueryFile(hDropInfo, 0, szFilePathName, _MAX_PATH);
26         // 打开文件 与否
27         if (Number_QueryAccept % 2 == 0)                    // 控制一下,QueryAcceptData 会被调用两次,
28         {
29             if (DragAndDropOpenFile(szFilePathName) == false)
30             {
31                 // 是否正确打开拖拽文件 -- false 没有打开
32                 // 重新显示Txt文本 -- 干掉出现的那个图标
33                 pRichEdit->SetSel(0, -1);                    // 文本全选
34                 // 使用下面两行的目的是为了替换的时候不至于出现 文字全选而出现的颜色,
35                 pRichEdit->Clear();
36                 pRichEdit->SetSel(0, -1);
37                 // 替换选中的文本,即输入读取的文本信息
38                 pRichEdit->ReplaceSel(TxtFileString);
39 //                MessageBox(L"这么晚?");
40             }
41         }
42         GlobalUnlock(stgmed.hGlobal);
43         ReleaseStgMedium(&stgmed);
44 //        return S_OK;        // 会出现图标资源
45         return S_FALSE;        // 不会出现图标资源
46     }
47     else
48     {
49         // 当拖拽其他,如文字时,调用其原本的函数,用于响应,拖拽文件,手写函数响应
50         return CRichEditView::QueryAcceptData(lpdataobj, lpcfFormat, dwReco, bReally, hMetaFile);
51     }
52 }
 1 /*
 2 功能:实现文件拖拽时的打开文本的功能 --> 实现拖拽文本所需功能
 3 1.获取 并 保存--文件路径
 4 2.保存当前文本 -- 询问 保存与否 ,取消 -- 退出文件拖拽这个事件
 5 3.直接打开拖拽而来的文件
 6 */
 7 bool CTxt0721View::DragAndDropOpenFile(CString FileLoad)
 8 {
 9     // TODO: 在此添加命令处理程序代码
10     // CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
11     // 保存当前文本
12     if (CTxt0721View::AskToSaveOrNot(pRichEdit) == false)
13         return false;
14 
15     CTxt0721View::InitCTxt0721View();            // 重置各种参数
16     TxtFileLoad = FileLoad;                        // 保存拖拽文件路径
17     IsFileNew = false;                            // 非新建文件
18     CTxt0721View::ReadFileContents();            // TxtFileString -- 读取文件内容,
19 
20     // 显示Txt文本
21     pRichEdit->SetSel(0, -1);                    // 文本全选
22     // 使用下面两行的目的是为了替换的时候不至于出现 文字全选而出现的颜色,
23     pRichEdit->Clear();
24     pRichEdit->SetSel(0, -1);
25     // 替换选中的文本,即输入读取的文本信息
26     pRichEdit->ReplaceSel(TxtFileString);
27     // 用于记录文本是否修改过,FALSE未修改,刚打开的文档默认未修改
28     pRichEdit->SetModify(FALSE);
29     return true;
30 }
View Code

关于从LPDATAOBJECT lpdataobj中获取到文件路径,真的找了很久,现在竟然找不到了。。。。真难过。。

不过至此,问题差不多就解决了,能够在整个界面上都可以实现拖拽,

解决完问题之后,才发现好多资料。。。真需要的时候找不到。。。这也是没办法,关键在于不知道应该查找什么内容,只知道大概查找方向而不是精确的点,这点就是问题所在了,可是没有广泛的了解又哪里找得到关键点。。。没办法喽,学习的必经之路


 总结:在CRichEditView中,无法使用WM_DROPFILES消息,但是在CMainFrame中还是可以正常使用的,但是功能只限于框架部分,不包括客户区;
可以通过调用QueryAcceptData函数来实现客户区的拖拽响应,利用LPDATAOBJECT lpdataobj来获取文件路径。

 下一篇04:MFC实现Txt04之打开各种编码文本(ANSI,UTF-8,UTF-16)

2022-08-17(0726.。。)

posted on 2022-08-17 10:50  夜_归_人  阅读(396)  评论(0编辑  收藏  举报