📄 adv_copypaste.shtml.htm
字号:
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="Author" CONTENT="Zafir Anjum">
<TITLE>Clipboard - Advanced Copy/Paste & Drag/Drop Support</TITLE>
</HEAD>
<body background="../fancyhome/back.gif" tppabs="http://www.codeguru.com/fancyhome/back.gif" bgcolor="#FFFFFF" link="#B50029" vlink="#8E2323" alink="#FF0000">
<table WIDTH="100%">
<tr WIDTH="100%">
<td><td>
</tr>
</table>
<CENTER>
<H3>
<FONT COLOR="#AOAO99">Advanced Copy/Paste & Drag/Drop Support</FONT></H3></CENTER>
<CENTER>
<H3>
<HR></H3></CENTER>
<P>This article was contributed by <A HREF="mailto:keithr@europa.com">Keith Rule</A>. </P>
<I><P>Copyright © 1998, Keith Rule -This information is intended for personal use. It may not be reprinted or republished without permission of the author.</P>
</I>
<h3>Introduction</h3>
One of the intentions of the clipboard infrastructure in MS-Windows is to allow nearly seamless interaction between applications. However, neither Microsoft nor the reams of programming books available, seriously discuss the issue of seamless interchange of data with MS-Office products. I personally feel this is an unfortunate omission, which requires developers to spend a fair amount of time hunting for information, and/or doing trial-and-error experiments.
<P>This article address several issues around interchanging data with MS-Office Products. The major topics addressed are:
<ul>
<li>Placing Multiple formats on the Clipboard.
<li>Delayed Rendering.
<li>A survey of common clipboard.
<li>The preference of several common target applications (MS-Word, MS-Excel, and MS-PowerPoint).
</ul>
<P>This article assumes a basic understanding of using the clipboard with MFC. If you are unfamiliar with the basic use of the clipboard, please read the article Basic Copy/Paste & Drag/Drop Support
<H3>Placing Multiple Formats on the Clipboard</H3>
<P>A fundamental issue when initiating a drag/cut/copy is that there is no way to know what target application will be the destination. This is a problem when the information you would like MS-Word to accept is different from the information you would like MS-Excel to accept.
<P>This issue is resolved in two ways:
<ul>
<li>The application that initiates the drag/cut/copy may place more than one clipboard format on the clipboard.
<li>The target often accepts more than one format, but has a preference (meaning a acceptance order) for the formats it accepts.
</ul>
<P>Table 6 shows the acceptance order for Cut/Copy/Paste and Drag/Drop in MS-Excel, MS-Word, and MS-PowerPoint. MS-Excel accepts the format "Csv", while neither MS-Word or MS-PowerPoint accept that format.
<P>If you would like MS-Word to accept data that is different from what you would like to MS-Excel to accept, all you need to do is place both an RTF representation and a Csv representation on the clipboard. Since MS-Excel doesn't accept RTF it will get the Csv data, and since MS-Word doesn't accept Csv it will get the RTF data.
<P>You can often exploit these differences to get the Cut/Copy/Paste and Drag/Drop behavior you desire.
<P>The code example in Table 1 shows how both RTF and Csv formats can be placed on the clipboard at once. Table 2 and 3 shows the result of using this code when this data is dragged to MS-Word and MS-Excel.
<P><b>Table 1 - Source for initiating RTF and Csv clipboard formats</b>
<PRE><TT><FONT COLOR="#990000">
void CClipExamView::Word2Clipboard(COleDataSource * pSource)
{
UINT format = ::RegisterClipboardFormat(_T("Rich Text Format"));
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
CString text = _T("{\\rtf1 {1\\tab 2\\tab 3\\par 4\\tab }{\\b\\i 5}{\\tab 6\\par}}");
sf.Write(text, text.GetLength());
HGLOBAL hMem = sf.Detach();
if (!hMem) return;
pSource->CacheGlobalData(format, hMem);
}
void CClipExamView::Excel2Clipboard(COleDataSource * pSource)
{
UINT format = ::RegisterClipboardFormat(_T("Csv"));
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
CString text = _T("6,5,4\n2,=1+1,1");
sf.Write(text, text.GetLength());
HGLOBAL hMem = sf.Detach();
if (!hMem) return;
pSource->CacheGlobalData(format, hMem);
}
void CClipExamView::OnEditCopy()
{
COleDataSource source;
Excel2Clipboard(&source);
Word2Clipboard(&source);
source.SetClipboard();
}
void CClipExamView::OnLButtonDown(UINT nFlags, CPoint point)
{
COleDataSource* pSource = new COleDataSource();
Excel2Clipboard(pSource);
Word2Clipboard(pSource);
pSource->DoDragDrop();
}
</FONT></TT></PRE>
<P><b>Table 2 - Excel as the Drop Target</b>
<P><img src="adv_copypaste1.gif" tppabs="http://www.codeguru.com/clipboard/adv_copypaste1.gif">
<P><b>Table 3 - Word as the Drop Target</b>
<P><img src="adv_copypaste2.gif" tppabs="http://www.codeguru.com/clipboard/adv_copypaste2.gif">
<P>It's obvious from looking at the screen shots that the text placed in Word was different than the text placed in Excel. You can exploit the clipboard format preferences of the target applications to get the behavior you wish on a drop.
<H3>Delayed Rendering</H3>
Delayed Rendering does what the name implies. It defers creating the data that goes on the clipboard until it is specifically asked from by the destination application.
<P>The code examples shown so far have placed the entire format on the clipboard when the source application initiates the cut/copy or drag. This can needlessly consume a larger amount of shared memory and can consume time creating unused data that is placed on the clipboard.
<P>If you plan to use the clipboard to move large amounts of data, you should use Delayed Rendering. This will make the performance of cut/copy/paste and drag/drop appear much better to the user.
<H4>Modifying COleDataSource for Delayed Rendering</H4>
To use Delayed Rendering you need to override one or more member functions in the COleDataSource class. By overriding the OnRenderGlobalData() member function you can intercept the target applications request for data and return whatever data you wish.
<P>You check the requested format by looking at the lpFormatEtc->cfFormat item. If you find a format you accept you simply write your data to shared memory and place the handle in *phGlobal. Return TRUE if you've honored the request for data and FALSE if you didn't.
<P>To specify the supported format when you initiate the cut/copy/drag you replace the CacheGlobalData() call with the DelayRenderData() call. This specifies the supported format without having to place the data for that format on the clipboard until requested by the target application.
<P><b>Table 3 - A Derived COleDataSource class that supports Delayed Rendering</b>
<PRE><TT><FONT COLOR="#990000">
class CMyOleDataSource : public COleDataSource
{
public:
// Called when target application requests data
BOOL OnRenderGlobalData(LPFORMATETC lpFormatEtc, HGLOBAL* phGlobal)
{
if (lpFormatEtc->cfFormat == ::RegisterClipboardFormat("Csv")) {
// Handle Csv format
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
CString text = _T("Excel2Clipboard\n6,5,4\n3,2,1");
sf.Write(text, text.GetLength());
HGLOBAL hMem = sf.Detach();
*phGlobal = hMem;
return (hMem != NULL);
} else if (lpFormatEtc->cfFormat == ::RegisterClipboardFormat(CF_RTF)) {
// Handle Rich Text Format
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
CString text = _T("{\\rtf1 {Word2Clipboard\\par 1\\tab 2\\tab 3\\par 4\\tab 5\\tab 6}}");
sf.Write(text, text.GetLength());
HGLOBAL hMem = sf.Detach();
*phGlobal = hMem;
return (hMem != NULL);
}
return FALSE;
}
void SetClipboard()
{
// Place available formats on the clipboard
DelayRenderData(::RegisterClipboardFormat("Csv"));
DelayRenderData(::RegisterClipboardFormat(CF_RTF));
// Initiate a cut/copy to the clipboard
COleDataSource::SetClipboard();
}
DROPEFFECT DoDragDrop(DWORD dwEffects = DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK, LPCRECT lpRectStartDrag = NULL, COleDropSource* pDropSource = NULL)
{
// Place available formats on the clipboard
DelayRenderData(::RegisterClipboardFormat("Csv"));
DelayRenderData(::RegisterClipboardFormat(CF_RTF));
// Initiate a drag to the clipboard
return COleDataSource::DoDragDrop(dwEffects, lpRectStartDrag, pDropSource);
}
};
</FONT></TT></PRE>
<P>In the example in Table 2, I've chosen to override the SetClipboard() and DoDragDrop() member functions so that I can specify the supported formats. This isn't necessary, but simplifies the usage of this class. As can be seen in Table 3.
<P><b>Table 3 - An example using CMyOleDataSource.</b>
<PRE><TT><FONT COLOR="#990000">
void CClipExamView::OnEditCopy()
{
CMyOleDataSource source;
source.SetClipboard();
}
void CClipExamView::OnLButtonDown(UINT nFlags, CPoint point)
{
CMyOleDataSource source;
source.DoDragDrop();
}
</FONT></TT></PRE>
<H3>Common Clipboard Formats</H3>
This section describes several commonly used clipboard formats. This is not intended to be an exhaustive list. However, it should be enough to get you started if you are not already familiar with these formats.
<H4>CF_TEXT</H4>
The CF_TEXT format is probably the most used of the clipboard formats commonly used by developers. This format simply defines a text string that is placed on the clipboard.
<P>See <A HREF="copypaste_dragdrop.shtml.htm" tppabs="http://www.codeguru.com/clipboard/copypaste_dragdrop.shtml">http://www.codeguru.com/clipboard/copypaste_dragdrop.shtml</A> for a simple example of how to read and write CF_TEXT from the clipboard.
<P>If you plan to use the format with MS-Excel, keep in mind that tabs ('\t') separate the columns and new-lines ('\n') start a new row. In MS-Word new-lines denote the beginning of a new paragraph.
<H4>Csv</H4>
The "Csv" format stands for Comma Separated Values. "Csv" files are easy to export from an application and most Spreadsheets and Databases can import this format.
<P>When using the "Csv" format with MS-Excel remember that commas (',') separate columns and new-lines ('\n') start a new row.
<P>The actual format value is found using the following code:
<PRE><TT><FONT COLOR="#990000">UINT csvformat = ::RegisterClipboardFormat("Csv");
</FONT></TT></PRE>
<P>Other than the special format value, this format is a text string and can be created in the same manner as the CF_TEXT format.
<H4>Rich Text Format (CF_RTF)</H4>
This Rich Text Format is intended as an interchange format for Word-processing applications. Because of that, it is a rather large and feature rich file format. Luckily, it is possible to describe a minimal RTF command set for creating simple formatted documents.
<P>An RTF document consists of a group with the following format:
<PRE><TT><FONT COLOR="#990000">
{\rtf1 document contents }
</FONT></TT></PRE>
<P>The document content is one or more groups that contain plain text and control words. There are many more control words that can be documented here, so I will hit the high points.
<ul>
<li><b>\par</b> - Starts a new paragraph.
<li><b>\tab</b> - A tab.
<li><b>\b</b> - Enable Bold (scoped within a group)
<li><b>\i</b> - Enable Italics (scoped within a group)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -