Applications like Word and Excel let you copy some text or tabular data to the clipboard in HTML-format, so that the formatting and visual style doesn’t change when pasted. At work, I had to do something similar: our application uses a lot of grid-controls to display data, and we wanted the users to be able to select data from a grid, and copy it to the clipboard, in a format that would keep the tabular structure of the data. After some research, I found out that you can copy data to the clipboard in HTML-format, and I thought: this is it. Piece of cake. But it wasn’t. Apparently, you have to pass a bit more information than just the HTML-code. Googling around gave me some hints how it should be done, but nothing really specific. So I started experimenting, until it worked correctly.
Tip: use a StringBuilder to build the HTML-code, instead of concatenating strings. StringBuilders are very efficient for building long strings (hence the name StringBuilder): they are a lot faster than strings, and allocate much less memory.
Possible applications
There are a lot of situations where you could use this:
- Copying tabular data (and pasting it in Excel, Word or Outlook).
- Copying formatted, visually styled data.
- …
The Code
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
internal static class Utilities
{
public static void CopyHtmlToClipBoard(string htmlFragment)
{
string headerFormat
= "Version:0.9\r\nStartHTML:{0:000000}\r\nEndHTML:{1:000000}"
+ "\r\nStartFragment:{2:000000}\r\nEndFragment:{3:000000}\r\n";
string htmlHeader
= "<html>\r\n<head>\r\n"
+ "<meta http-equiv=\"Content-Type\""
+ " content=\"text/html; charset=utf-8\">\r\n"
+ "<title>HTML clipboard</title>\r\n</head>\r\n<body>\r\n"
+ "<!--StartFragment-->";
string htmlFooter = "<!--EndFragment-->\r\n</body>\r\n</html>\r\n";
string headerSample = String.Format(headerFormat, 0, 0, 0, 0);
Encoding encoding = Encoding.UTF8;
int headerSize = encoding.GetByteCount(headerSample);
int htmlHeaderSize = encoding.GetByteCount(htmlHeader);
int htmlFragmentSize = encoding.GetByteCount(htmlFragment);
int htmlFooterSize = encoding.GetByteCount(htmlFooter);
string htmlResult
= String.Format(
CultureInfo.InvariantCulture,
headerFormat,
/* StartHTML */ headerSize,
/* EndHTML */ headerSize + htmlHeaderSize + htmlFragmentSize + htmlFooterSize,
/* StartFragment */ headerSize + htmlHeaderSize,
/* EndFragment */ headerSize + htmlHeaderSize + htmlFragmentSize)
+ htmlHeader
+ htmlFragment
+ htmlFooter;
DataObject obj = new DataObject();
obj.SetData(DataFormats.Html, new MemoryStream(encoding.GetBytes(htmlResult)));
Clipboard.SetDataObject(obj, true);
}
}
Example: copying a DataTable
StringBuilder html = new StringBuilder();
html.Append("<table>");
foreach(DataRow row in table.Rows)
{
html.Append("<tr>");
foreach(object o in row)
{
html.AppendFormat("<td>{0}</td>", o);
}
html.Append("</tr>");
}
html.Append("</table>");
Utilities.CopyHtmlToClipboard(html.ToString());