C# FlowDocument 到 HTML 的转换

2023-11-27

基本上,我有一个 RichTextBox,我想将其格式化内容转换为 HTML,以便它可以作为电子邮件发送。

我当前使用的方法根本不提供任何格式:

string message = new TextRange(messageTextBox.Document.ContentStart,
                               messageTextBox.Document.ContentEnd).Text;

所以我四处寻找并发现this然而,它已经存在超过 5 年了,并且在评论中一位 MSFT 用户评论说它不再受支持 -"This sample has been removed from our sample set and is no longer supported",并且它生成的 HTML 格式比现代 HTML 或 XHTML 更旧,最好使用现代 HTML 或 XHTML。

谁能告诉我如何转换格式化的RichTextBox 的内容转换为 HTML?

(因此,当电子邮件发送时,收件人会看到带格式的电子邮件)


一般技术是使用XamlWriter来转换FlowDocument内容转换为 XML 流,然后使用 XSLT 转换将 XML 转换为 HTML。这并不是一个很好的答案,但那是因为任何给定的 FlowDocument 都有大量可能的 HTML 表示形式。

例如,此转换将每个顶级转换为Section to a div, every Paragraph to a p,以及每一个Run to a span谁的类告诉您它是斜体、粗体、下划线还是以上的任意组合。它对于我编写它的目的很有用,但称其为有损转换还算轻描淡写:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="msxsl x">

  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <xsl:template match="x:Section[not(parent::x:Section)]">
    <div>
      <xsl:apply-templates select="node()"/>
    </div>
  </xsl:template>

  <xsl:template match="x:Section">
    <xsl:apply-templates select="node()"/>
  </xsl:template>

  <xsl:template match="x:Paragraph">
    <p>
      <xsl:apply-templates select="node()"/>
    </p>
  </xsl:template>

  <xsl:template match="x:Run">
    <xsl:variable name="class">
      <xsl:if test="@FontStyle='Italic'">
        <xsl:text>i </xsl:text>
      </xsl:if>
      <xsl:if test="@FontWeight='Bold'">
        <xsl:text>b </xsl:text>
      </xsl:if>
      <xsl:if test="contains(@TextDecorations, 'Underline')">
        <xsl:text>u </xsl:text>
      </xsl:if>
    </xsl:variable>
    <span>
      <xsl:if test="normalize-space($class) != ''">
        <xsl:attribute name="class">
          <xsl:value-of select="normalize-space($class)"/>
        </xsl:attribute>
      </xsl:if>
      <xsl:value-of select="text()"/>
    </span>
  </xsl:template>

</xsl:stylesheet>

这是我编写的用于进行转换的值转换器 - 请注意,为了使用值转换器,您还必须破解并实现一个版本RichTextBox将内容公开为依赖属性。确实,整个项目很痛苦。

public class FlowDocumentToHtmlConverter : IValueConverter
{
    private static XslCompiledTransform ToHtmlTransform;
    private static XslCompiledTransform ToXamlTransform;

    public FlowDocumentToHtmlConverter()
    {
        if (ToHtmlTransform == null)
        {
            ToHtmlTransform = LoadTransformResource("Converters/FlowDocumentToXhtml.xslt");
        }
        if (ToXamlTransform == null)
        {
            ToXamlTransform = LoadTransformResource("Converters/XhtmlToFlowDocument.xslt");
        }
    }
    private static XslCompiledTransform LoadTransformResource(string path)
    {
        Uri uri = new Uri(path, UriKind.Relative);
        XmlReader xr = XmlReader.Create(Application.GetResourceStream(uri).Stream);
        XslCompiledTransform xslt = new XslCompiledTransform();
        xslt.Load(xr);
        return xslt;
    }

    #region IValueConverter Members

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value is FlowDocument))
        {
            return null;
        }
        if (targetType == typeof(FlowDocument))
        {
            return value;
        }

        if (targetType != typeof(string))
        {
            throw new InvalidOperationException(
                "FlowDocumentToHtmlConverter can only convert back from a FlowDocument to a string.");
        }

        FlowDocument d = (FlowDocument)value;

        using (MemoryStream ms = new MemoryStream())
        {
            // write XAML out to a MemoryStream
            TextRange tr = new TextRange(
                d.ContentStart,
                d.ContentEnd);
            tr.Save(ms, DataFormats.Xaml);
            ms.Seek(0, SeekOrigin.Begin);

            // transform the contents of the MemoryStream to HTML
            StringBuilder sb = new StringBuilder();
            using (StringWriter sw = new StringWriter(sb))
            {
                XmlWriterSettings xws = new XmlWriterSettings();
                xws.OmitXmlDeclaration = true;
                XmlReader xr = XmlReader.Create(ms);
                XmlWriter xw = XmlWriter.Create(sw, xws);
                ToHtmlTransform.Transform(xr, xw);
            }
            return sb.ToString();
        }
    }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
        {
            return new FlowDocument();
        }
        if (value is FlowDocument)
        {
            return value;
        }
        if (targetType != typeof(FlowDocument))
        {
            throw new InvalidOperationException(
                "FlowDocumentToHtmlConverter can only convert to a FlowDocument.");
        }
        if (!(value is string))
        {
            throw new InvalidOperationException(
                "FlowDocumentToHtmlConverter can only convert from a string or FlowDocument.");
        }

        string s = (string)value;

        FlowDocument d;

        using (MemoryStream ms = new MemoryStream())
        using (StringReader sr = new StringReader(s))
        {
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.OmitXmlDeclaration = true;
            using (XmlReader xr = XmlReader.Create(sr))
            using (XmlWriter xw = XmlWriter.Create(ms, xws))
            {
                ToXamlTransform.Transform(xr, xw);
            }
            ms.Seek(0, SeekOrigin.Begin);

            d = XamlReader.Load(ms) as FlowDocument;
        }
        XamlWriter.Save(d, Console.Out);
        return d;
    }

    #endregion
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# FlowDocument 到 HTML 的转换 的相关文章

随机推荐

  • 当实现你自己的 IUserStore 时,类上的“可选”接口实际上是可选的吗?

    我正在使用 Microsoft 的 Asp Net Identity 框架版本 2 并正在实现我自己的 IUserStore 我的新班级MyUserStore实施IUserStore
  • 位运算符在 Java 中到底是如何工作的?

    我目前正在尝试了解 Java 中的按位和位移运算符 尽管它们在简化的玩具示例 基本上是正整数 中对我来说是有意义的 但一旦涉及负数 以及在其他一些情况下 我的理解就会崩溃 我尝试用两个搜索引擎在互联网上进行搜索 甚至检查了Java规范 我找
  • 在轨道中销毁之前检查所有关联

    我的应用程序中有一个重要的模型 有很多关联 如果我想检查 before destroy 回调中的所有引用 我必须执行以下操作 has many models 1 has many models 2 mas many models 3 has
  • 避免将 master 合并到开发分支中

    我从每个冲刺开始就一直在监控两个分支 Release and Master Master分支是开发人员创建新分支 特定于任务 实现更改并创建合并到分支中的拉取请求的地方Master Release分支是特定于冲刺的 始终可提交给生产 我们只
  • 2-SUM 的线性时间算法

    给定一个整数 x 和一个由 N 个不同整数组成的排序数组 a 设计一个线性时间算法来确定是否存在两个不同的索引 i 和 j 使得 a i a j x 这是类型子集和问题 这是我的解决方案 不知道是不是早知道了 想象一下两个变量 i 和 j
  • 您推荐哪些 Javascript 模板引擎? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 我想知道您对javascr
  • 使用 CarrierWave 混合文件类型

    我有一个 CarrierWave 上传器 可以接受各种文件类型 有些是图像类型 例如 jpg png 有些则不是 我想创建上传文件的中等版本 version medium do process resize to fit gt 300 30
  • 使用类似 SQL 的 IN 子句过滤 Pyspark DataFrame

    我想用类似 SQL 的方法过滤 Pyspark DataFrameIN子句 如 sc SparkContext sqlc SQLContext sc df sqlc sql SELECT from my df WHERE field1 IN
  • 如何通过电子邮件将我正在开发的 Android 应用程序发送给某人?

    这是我的第一个 Android 应用程序 我需要将迄今为止的内容通过电子邮件发送给某人进行测试 我应该如何导出应用程序并附加它 以免它被视为垃圾 更简单的方法 将 apk 放在您的网络服务器上 使用以下命令创建 QR 条形码图像 然后通过电
  • 为什么 CAP 定理中的 C 与 ACID 中的 C 不同?

    我的问题很简单 正在寻找一个更简单的答案 为什么 CAP 定理中的 C 与 ACID 中的 C 不同 Read thisHN 螺纹 Update NOSQL v1 0 搭便车指南 幻灯片 71 说 CAP 中的 C A C 原子一致性 两个
  • 跟踪数据库模式更改的机制[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 跟踪和 或自动化数据库架构
  • 计算两个 Pandas 列之间的时间差(以小时和分钟为单位)

    我有两列 fromdate and todate 在数据框中 import pandas as pd data todate pd Timestamp 2014 01 24 13 03 12 050000 pd Timestamp 2014
  • 将 std::experimental::filesystem 与 Xcode 9 链接

    我正在使用 std experimental filesystem 和 Xcode 9 0 beta 编译器阶段完成正常 但链接器抱怨未定义的符号 std experimental filesystem v1 path filename c
  • 创建大量线程时出现.Net 内存泄漏

    我有一个随着时间的推移创建大量线程的应用程序 我注意到内存使用量随着它的运行而增加 并最终耗尽内存 但相同的代码在我同事的环境中不会泄漏内存 我们都有相同的 net 版本 我能够使用以下示例代码重现该问题 该代码不会在我同事的笔记本电脑上泄
  • 为什么最好从方法类的实例中静态调用静态方法?

    如果我在 Java 中创建类的实例 为什么最好静态调用同一类的静态方法 而不是使用 this method 当我尝试通过 this staticMethod 从自定义类的构造函数中调用静态方法 staticMethod 时 我收到来自 Ec
  • 获取客户端当前在断开连接事件中所在的房间列表

    我正在尝试查找客户端当前在断开连接事件中所在的房间列表 关闭浏览器 重新加载页面 互联网连接已断开 我需要它的原因如下 用户已进入几个房间 然后其他人也做了同样的事情 然后他关闭了浏览器选项卡 我想通知他所在房间里的所有人他离开了 所以我需
  • pyside qtreewidget 约束拖放

    我试图向 QTreeWidget 拖放功能添加约束 以防止分支进入另一个根中的另一个分支 这是一个让事情更清楚的例子 我有 4 个对象 我们称它们为苹果 香蕉 胡萝卜 榴莲 这棵树看起来像这样 isDelicious Root Backgr
  • Xcode 11.4 beta 在 @Published 属性订阅上崩溃。这是怎么回事?

    我不知道为什么 但我的代码在这个 searchTerm 发布者上崩溃了 我的代码中有很多这样的发布者 其他一切都正常 它仅在这个新的 Xcode 版本中不起作用 而在以前的版本中起作用 如果我评论这一行并将其替换为 searchTerm p
  • 将信息从 javascript 传递到 django 应用程序并返回

    所以我试图基本上建立一个网页 用户在其中选择一个id 然后该网页将id信息发送到python 其中python使用该id来查询数据库 然后将结果返回到网页进行显示 我不太确定该怎么做 我知道如何使用 ajax 调用来调用 python 生成
  • C# FlowDocument 到 HTML 的转换

    基本上 我有一个 RichTextBox 我想将其格式化内容转换为 HTML 以便它可以作为电子邮件发送 我当前使用的方法根本不提供任何格式 string message new TextRange messageTextBox Docum