Преобразование C# FlowDocument в HTML

в основном, у меня есть RichTextBox, и я хочу преобразовать форматированное содержимое в HTML, чтобы его можно было отправить по электронной почте.

метод, который я сейчас использую, не дает никакого форматирования вообще:

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

Так что я искал вокруг и нашел этой, однако, он старше 5 лет, и в комментариях пользователь MSFT прокомментировал, что он больше не поддерживается -"This sample has been removed from our sample set and is no longer supported", и HTML он генерирует в более старом формате, чем современный HTML или XHTML, который было бы лучше иметь.

может кто-нибудь показать мне, как я могу преобразовать в формате содержимое RichTextBox в HTML?

(поэтому, когда письмо отправляется, получатель видит письмо с форматированием)

1 ответов


общий метод-использовать XamlWriter преобразование FlowDocument содержимое в поток XML, а затем использовать преобразование XSLT для преобразования XML в HTML. Это не большой ответ, но это потому, что существует огромный диапазон возможных HTML-представлений любого данного FlowDocument.

это преобразование, например, преобразует каждый верхний уровень Section до div, каждый Paragraph до p, и все Run до 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
}