如何解决WebView2 - 选择文本并复制到剪贴板
使用 CHtmlView
类,我可以像这样选择所有文本并复制到剪贴板:
void CChristianLifeMinistryHtmlView::copyToClipboard()
{
ExecWB(OLECMDID_copY,OLECMDEXEcopT_DONTPROMPTUSER,nullptr,nullptr);
}
void CChristianLifeMinistryHtmlView::SelectAll()
{
ExecWB(OLECMDID_SELECTALL,nullptr);
}
我正在尝试了解如何使用新的 WebView2 API 做同样的事情。
更新
WebView2 控件设计支持:
- CTRL + A 选择所有内容。
- CTRL + C 将所选文本复制到剪贴板。
我找到了一个 VB.NET 解决方案,以编程方式将所有页面复制到剪贴板:
Sub Async GetText()
v = Await wv.ExecuteScriptAsync("document.body.innerText")
End Sub
但我使用的是 Visual C++,我没有看到这种方法公开。另外,我不确定这是我想要的,因为我不想复制为纯文本数据,而是像热键一样复制 HTML 数据(适合在 Word 中粘贴)。我也为此提出了 GitHub 问题。
更新
所以我现在尝试了这段代码,但它似乎没有做任何事情:
void CWebbrowser::copyToClipboard()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(_T("document.body.innerText"),nullptr);
}
}
更新
根据此 article 声明:
或者,对于 ICoreWebView2::ExecuteScript
,您提供一个具有 Invoke
方法的实例,该方法为您提供 ExecuteScript
请求的成功或失败代码。还提供第二个参数,即运行脚本的结果的 JSON
。
试图找到例子。
更新
我现在明白我需要做这样的事情:
void CWebbrowser::copyToClipboard()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(L"document.body.innerText",Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[](HRESULT error,PCWSTR result) -> HRESULT
{
if (error != S_OK) {
ShowFailure(error,L"ExecuteScript Failed");
}
SetClipboardText(result);
AfxMessageBox(L"HTML copied to clipboard!",MB_OK);
return S_OK;
}).Get());
}
}
变量 result
包含 HTML 页面的内容。但它现在不喜欢我对 SetClipboardText
的调用。这是错误:
这是函数:
void CWebbrowser::SetClipboardText(CString strText)
{
BYTE* pbyText;
TCHAR* pcBuffer;
HANDLE hText;
UINT uLength;
//USES_CONVERSION ;
if (::OpenClipboard(nullptr))
{
// Empty it of all data first.
::EmptyClipboard();
// Replace prevIoUs text contents.
uLength = strText.GetLength();
//pcBuffer = T2A( (LPTSTR)(LPCTSTR)strText);
pcBuffer = strText.GetBuffer(uLength);
if (pcBuffer != nullptr)
{
hText = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,(uLength + 1) * sizeof(TCHAR));
if (hText != nullptr)
{
pbyText = (BYTE*)::GlobalLock(hText);
if (pbyText != nullptr)
{
// Deliberately not _tcscpy().
//strcpy_s( (char *)pbyText,uLength+1,pcBuffer);
_tcscpy_s((TCHAR*)pbyText,uLength + 1,pcBuffer);
::GlobalUnlock(hText);
::SetClipboardData(CF_UNICODETEXT,hText);
// Don't free this memory,clipboard owns it Now.
}
}
}
::CloseClipboard();
strText.ReleaseBuffer(uLength);
}
}
这是我知道(迄今为止)复制到剪贴板的唯一方法。所以我在这里有两个问题:
- 它不会让我使用这个功能。
- 我希望它只会复制文本 (
CF_UNICODETEXT
),但我不知道这是否足以将数据作为 HTML 粘贴到 Word 中?
关于问题 1,我需要创建方法 static
。然后它编译并将信息复制到剪贴板。
关于问题 2,正如预期的那样,粘贴的数据被剥离了 HTML,我只剩下文本。并且换行符在文本中显示为"\n"
。这是一个巨大的段落。所以我确实需要 CF_HTML
格式。
遗憾的是,WebView2
没有公开它已经具有的复制到剪贴板功能。
更新
这是我在互联网上为 CF_HTML
找到的 clipboard method:
// copyHtml() - copies given HTML to the clipboard.
// The HTML/BODY blanket is provided,so you only need to
// call it like copyHtml("<b>This is a test</b>");
void copyHTML(char* html)
{
// Create temporary buffer for HTML header...
char* buf = new char[400 + strlen(html)];
if (!buf) return;
// Get clipboard id for HTML format...
static int cfid = 0;
if (!cfid) cfid = RegisterClipboardFormat("HTML Format");
// Create a template string for the HTML header...
strcpy(buf,"Version:0.9\r\n"
"StartHTML:00000000\r\n"
"EndHTML:00000000\r\n"
"StartFragment:00000000\r\n"
"EndFragment:00000000\r\n"
"<html><body>\r\n"
"<!--StartFragment -->\r\n");
// Append the HTML...
strcat(buf,html);
strcat(buf,"\r\n");
// Finish up the HTML format...
strcat(buf,"<!--EndFragment-->\r\n"
"</body>\r\n"
"</html>");
// Now go back,calculate all the lengths,and write out the
// necessary header information. Note,wsprintf() truncates the
// string when you overwrite it so you follow up with code to replace
// the 0 appended at the end with a '\r'...
char* ptr = strstr(buf,"StartHTML");
wsprintf(ptr + 10,"%08u",strstr(buf,"<html>") - buf);
*(ptr + 10 + 8) = '\r';
ptr = strstr(buf,"EndHTML");
wsprintf(ptr + 8,strlen(buf));
*(ptr + 8 + 8) = '\r';
ptr = strstr(buf,"StartFragment");
wsprintf(ptr + 14,"<!--StartFrag") - buf);
*(ptr + 14 + 8) = '\r';
ptr = strstr(buf,"EndFragment");
wsprintf(ptr + 12,"<!--EndFrag") - buf);
*(ptr + 12 + 8) = '\r';
// Now you have everything in place ready to put on the clipboard.
// Open the clipboard...
if (OpenClipboard(0))
{
// Empty what's in there...
EmptyClipboard();
// Allocate global memory for transfer...
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,strlen(buf) + 4);
// Put your string in the global memory...
char* ptr = (char*)GlobalLock(hText);
strcpy(ptr,buf);
GlobalUnlock(hText);
::SetClipboardData(cfid,hText);
CloseClipboard();
// Free memory...
GlobalFree(hText);
}
// Clean up...
delete[] buf;
}
但它与 PCWSTR
变量不兼容。
更新
我现在在调试后意识到,如果我使用 ExecuteScript
,HTML
会返回实际的 document.body.innerText
数据。结果字符串只是带有 \n
字符的文本作为新行。它不是 HTML
格式。所以我又回到了原点。
解决方法
只需使用 ICoreWebView2::ExecuteScript
。
我将其作为替代方案展示,因为当前的方法不包括实际的 HTML 内容。我在互联网上发现的有关使用 document.body.innerText
的信息具有误导性。
对此的一种解决方案是使用 document.execCommand()
路线。我在浏览器类中添加了以下方法:
void CWebBrowser::SelectAll()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(L"document.execCommand(\"SelectAll\")",nullptr);
}
}
void CWebBrowser::CopyToClipboard2()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(L"document.execCommand(\"Copy\")",nullptr);
}
}
然后我将以下按钮处理程序添加到我的对话框中进行测试:
void CMFCTestWebView2Dlg::OnBnClickedButtonSelectAll()
{
if (m_pWebBrowser != nullptr)
{
m_pWebBrowser->SelectAll();
}
}
void CMFCTestWebView2Dlg::OnBnClickedButtonCopyToClipboard()
{
if (m_pWebBrowser != nullptr)
{
m_pWebBrowser->CopyToClipboard2();
}
}
这确实有效,而且似乎是目前最简单的解决方案。我看到对 Calendar API
的引用作为替代方案,但我不确定如何实现该方法。
我对 execCommand
的唯一担忧是我看到它被记录为已弃用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。