如何在C#中将HTML设置到剪贴板?

19

我希望将富文本放在HTML剪贴板上,这样当用户粘贴到Word中时,它将包括源HTML格式。

使用Clipboard.SetText方法无法实现。

另外,如果用户将其粘贴到像Word这样的富文本编辑器中,它将粘贴格式化的文本,如果他们将其粘贴到像记事本这样的纯文本编辑器中,它将粘贴为纯文本。

5个回答

28

当设置HTML文本时,您需要提供一个标题,其中包含有关您要粘贴的html片段的附加信息,同时能够在其周围提供额外的样式:

Version:0.9
StartHTML:000125
EndHTML:000260
StartFragment:000209
EndFragment:000222
<HTML>
<head>
<title>HTML clipboard</title>
</head>
<body>
<!–StartFragment–><b>Hello!</b><!–EndFragment–>
</body>
</html>
使用正确的索引和标题,使用Clipboard.SetText并指定TextDataFormat.Html即可轻松处理HTML格式的复制粘贴。
要同时处理HTML和纯文本的粘贴,不能使用Clipboard.SetText方法,因为每次调用该方法都会清除剪贴板。您需要创建一个DataObject实例,并分别使用HTML和纯文本调用其SetData方法一次,然后使用Clipboard.SetDataObject将该对象设置到剪贴板中。 更新 了解更多详情和ClipboardHelper实现,请参见“重温设置HTML/Text到剪贴板”。

3
阅读链接的帖子,它很值得! - user1068352
很棒的东西,但是它和我发现的其他几个解决方案似乎都没有将任何内容放入HTML格式的剪贴板中。即使数据对象看起来具有正确的HTML格式,剪贴板仍然为空。 - Douglas Gaskell
3
标题中的实际数字似乎完全错误。我完全不清楚这些数字是如何计算的,包括本文在内的所有文章都没有解释清楚。我找到的最好的信息是一些晦涩的代码,对理解也没有帮助。 - ygoe
@ygoe -- Chris Thornton所写的链接页面似乎解答了如何设置这些标题编号的问题。 - Michael
@ygoe -- 实际上,我刚刚尝试了使用这种技术,数字只是从文本开头的字节偏移量。建议在左侧填充数字,以便在设置值时偏移量保持正确。也就是说,使用像012345这样的占位符,然后在评估最终偏移量之后替换这些占位符,例如使用IndexOf()或类似的函数,但一定要使用相同数量的字符用于值,因此需要用零进行填充。 - Michael

7

我找到了一些代码:https://www.experts-exchange.com/questions/21966855/Create-a-hyperlink-in-VB-net-copy-to-clipboard-Should-be-able-to-paste-hyperlink-in-Microsoft-Word-Excel.html

这段代码解决了更新起始索引和结束索引的问题。

已转换为 C#:

public void AddHyperlinkToClipboard(string link, string description)
{
    const string sContextStart = "<HTML><BODY><!--StartFragment -->";
    const string sContextEnd = "<!--EndFragment --></BODY></HTML>";
    const string m_sDescription = "Version:1.0" + Constants.vbCrLf + "StartHTML:aaaaaaaaaa" + Constants.vbCrLf + "EndHTML:bbbbbbbbbb" + Constants.vbCrLf + "StartFragment:cccccccccc" + Constants.vbCrLf + "EndFragment:dddddddddd" + Constants.vbCrLf;

    string sHtmlFragment = "<A HREF=" + Strings.Chr(34) + link + Strings.Chr(34) + ">" + description + "</A>";

    string sData = m_sDescription + sContextStart + sHtmlFragment + sContextEnd;
    sData = sData.Replace("aaaaaaaaaa", m_sDescription.Length.ToString().PadLeft(10, '0'));
    sData = sData.Replace("bbbbbbbbbb", sData.Length.ToString().PadLeft(10, '0'));
    sData = sData.Replace("cccccccccc", (m_sDescription + sContextStart).Length.ToString().PadLeft(10, '0'));
    sData = sData.Replace("dddddddddd", (m_sDescription + sContextStart + sHtmlFragment).Length.ToString().PadLeft(10, '0'));
    sData.Dump();
    Clipboard.SetDataObject(new DataObject(DataFormats.Html, sData), true );
}

我还在SetDataObject中添加了“true”,以便在线程退出后保留剪贴板上的内容。 - Derek
1
计算在基本ASCII字符集下运行良好,但需要使用Encoding.UTF8.GetByteCount(sData).ToString().PadLeft(10, '0')而不是sData.Length.ToString().PadLeft(10, '0'),以避免在使用更宽的字符时出现损坏。 - zuraff

3

让我分享一个设置剪贴板数据为HTML格式的助手,这是我为我的小项目#DevComrade刚刚想出来的:


var dataObject = new DataObject();
dataObject.SetData(DataFormats.Html, ClipboardFormats.ConvertHtmlToClipboardData(html);
Host.SetClipboardDataObject(dataObject);

internal static class ClipboardFormats
{
    static readonly string HEADER = 
        "Version:0.9\r\n" +
        "StartHTML:{0:0000000000}\r\n" +
        "EndHTML:{1:0000000000}\r\n" +
        "StartFragment:{2:0000000000}\r\n" +
        "EndFragment:{3:0000000000}\r\n";

    static readonly string HTML_START =
        "<html>\r\n" +
        "<body>\r\n" +
        "<!--StartFragment-->";

    static readonly string HTML_END =
        "<!--EndFragment-->\r\n" +
        "</body>\r\n" +
        "</html>";

    public static string ConvertHtmlToClipboardData(string html)
    {
        var encoding = new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
        var data = Array.Empty<byte>();

        var header = encoding.GetBytes(String.Format(HEADER, 0, 1, 2, 3));
        data = data.Concat(header).ToArray();

        var startHtml = data.Length;
        data = data.Concat(encoding.GetBytes(HTML_START)).ToArray();

        var startFragment = data.Length;
        data = data.Concat(encoding.GetBytes(html)).ToArray();

        var endFragment = data.Length;
        data = data.Concat(encoding.GetBytes(HTML_END)).ToArray();

        var endHtml = data.Length;

        var newHeader = encoding.GetBytes(
            String.Format(HEADER, startHtml, endHtml, startFragment, endFragment));
        if (newHeader.Length != startHtml)
        {
            throw new InvalidOperationException(nameof(ConvertHtmlToClipboardData));
        }

        Array.Copy(newHeader, data, length: startHtml);
        return encoding.GetString(data);
    } 
}


我使用了这个这个参考资料。同时,感谢@DaveyBoy发现了一个bug。

1
我认为这行代码有误: var newHeader = encoding.GetBytes( String.Format(HEADER, startHtml, startFragment, endFragment, endHtml));我很确定应该是这样的: var newHeader = encoding.GetBytes( string.Format(cbHeader, startHtml, endHtml, startFragment, endFragment));如果不修复此问题,��无法将其粘贴到Outlook或Word中(但某种原因MSTeams可以)。当我查看实际构建的字符串时,我意识到 endHtml 值太小了。 - DaveyBoy
回到这个问题..哦,是的,我现在明白你的意思了 - 已经修复了! - noseratio - open to work

1
Arthur关于标题的说法是正确的,但这里需要注意的重要事项是数据不会以纯文本形式保存在剪贴板上。您必须使用CF_HTML。您可以在MSDN上阅读有关此内容的信息:http://msdn.microsoft.com/en-us/library/aa767917(v=vs.85).aspx为了正确,您需要显示一个简单的CF_TEXT:“Hello!”,然后使用HTML标题和数据的CF_HTML,就像Arthur的示例一样。

-1
如Arthur所提到的,我使用了Setting HTML/Text to Clipboard revisited中的代码。
我不得不在标题中添加换行符才能使其正常工作(在这种情况下是VB)。
Private Const Header As String = "Version:0.9" & vbCrLf & "StartHTML:<<<<<<<<1" & vbCrLf & "EndHTML:<<<<<<<<2" & vbCrLf & "StartFragment:<<<<<<<<3" & vbCrLf & "EndFragment:<<<<<<<<4" & vbCrLf & "StartSelection:<<<<<<<<3" & vbCrLf & "EndSelection:<<<<<<<<4"

希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接