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 的转换 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 对类 static constexpr 结构的未定义引用,g++ 与 clang

    这是我的代码 a cp p struct int2 int x y struct Foo static constexpr int bar1 1 static constexpr int2 bar2 1 2 int foo1 return
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat

随机推荐

  • 当实现你自己的 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