forked from VincentH-Net/WinRT-RichTextBlock.Html2Xaml
-
Notifications
You must be signed in to change notification settings - Fork 2
/
RichTextBlockProperties.cs
200 lines (167 loc) · 7.44 KB
/
RichTextBlockProperties.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Windows.Data.Xml.Dom;
using Windows.Data.Xml.Xsl;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Markup;
namespace html2xaml
{
/// <summary>
/// Usage:
/// 1) In a XAML file, declare the above namespace, e.g.:
/// xmlns:common="using:html2xaml"
///
/// 2) In RichTextBlock controls, set or databind the Html property, e.g.:
///
/// <RichTextBlock common:Properties.Html="{Binding ...}"/>
///
/// or
///
/// <RichTextBlock>
/// <common:Properties.Html>
/// <![CDATA[
/// <p>This is a list:</p>
/// <ul>
/// <li>Item 1</li>
/// <li>Item 2</li>
/// <li>Item 3</li>
/// </ul>
/// ]]>
/// </common:Properties.Html>
/// </RichTextBlock>
/// </summary>
public sealed class Properties : DependencyObject
{
public static DependencyProperty HtmlProperty { get { return DependencyProperty.RegisterAttached("Html", typeof(string), typeof(Properties), new PropertyMetadata(null, HtmlChanged)); } }
public static void SetHtml(DependencyObject obj, string value)
{
obj.SetValue(HtmlProperty, value);
}
public static string GetHtml(DependencyObject obj)
{
return (string)obj.GetValue(HtmlProperty);
}
private static async void HtmlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Get the target RichTextBlock
RichTextBlock richText = d as RichTextBlock;
if (richText == null) return;
// Wrap the value of the Html property in a div and convert it to a new RichTextBlock
string xhtml = string.Format("<div>{0}</div>", e.NewValue as string);
xhtml = xhtml.Replace("\r", "").Replace("\n", "<br />");
//Decode the xhtml to take care of special character entity references e.g. © €
//which are not supported by Xml.
xhtml = System.Net.WebUtility.HtmlDecode(xhtml);
RichTextBlock newRichText = null;
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
// In design mode we swallow all exceptions to make editing more friendly
string xaml = "";
try
{
xaml = await ConvertHtmlToXamlRichTextBlock(xhtml);
newRichText = (RichTextBlock)XamlReader.Load(xaml);
}
catch (Exception ex)
{
string errorxaml = string.Format(@"
<RichTextBlock
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
>
<Paragraph>An exception occurred while converting HTML to XAML: {0}</Paragraph>
<Paragraph />
<Paragraph>HTML:</Paragraph>
<Paragraph>{1}</Paragraph>
<Paragraph />
<Paragraph>XAML:</Paragraph>
<Paragraph>{2}</Paragraph>
</RichTextBlock>",
ex.Message,
EncodeXml(xhtml),
EncodeXml(xaml)
);
newRichText = (RichTextBlock)XamlReader.Load(errorxaml);
} // Display a friendly error in design mode.
}
else
{
// When not in design mode, we let the application handle any exceptions
string xaml = "";
try
{
xaml = await ConvertHtmlToXamlRichTextBlock(xhtml);
newRichText = (RichTextBlock)XamlReader.Load(xaml);
}
catch (Exception ex)
{
string errorxaml = string.Format(@"
<RichTextBlock
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
>
<Paragraph>Cannot convert HTML to XAML. Please ensure that the HTML content is valid.</Paragraph>
<Paragraph />
<Paragraph>HTML:</Paragraph>
<Paragraph>{0}</Paragraph>
</RichTextBlock>",
EncodeXml(xhtml)
);
newRichText = (RichTextBlock)XamlReader.Load(errorxaml);
} // Display a friendly error in design mode.
}
// Move the blocks in the new RichTextBlock to the target RichTextBlock
richText.Blocks.Clear();
if (newRichText != null)
{
for (int i = newRichText.Blocks.Count - 1; i >= 0; i--)
{
Block b = newRichText.Blocks[i];
newRichText.Blocks.RemoveAt(i);
richText.Blocks.Insert(0, b);
}
}
}
private static string EncodeXml(string xml)
{
string encodedXml = xml.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("\"", """).Replace("'", "'");
return encodedXml;
}
private static XsltProcessor Html2XamlProcessor;
private static async Task<string> ConvertHtmlToXamlRichTextBlock(string xhtml)
{
// Load XHTML fragment as XML document
XmlDocument xhtmlDoc = new XmlDocument();
xhtmlDoc.LoadXml(xhtml);
if (Html2XamlProcessor == null)
{
var xsldocument = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///html2xaml/RichTextBlockHtml2Xaml.xslt"));
XmlDocument html2Xamlxsldoc = await XmlDocument.LoadFromFileAsync(xsldocument);
Html2XamlProcessor = new XsltProcessor(html2Xamlxsldoc);
//I can't get this part to work, so we've gone back to Build Action Content
// Read XSLT. In design mode we cannot access the xslt from the file system (with Build Action = Content),
// so we use it as an embedded resource instead:
/**
Assembly assembly = typeof(Properties).GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("RichTextBlockHtml2Xaml.xslt"))
{
StreamReader reader = new StreamReader(stream);
string content = await reader.ReadToEndAsync();
XmlDocument html2XamlXslDoc = new XmlDocument();
html2XamlXslDoc.LoadXml(content);
Html2XamlProcessor = new XsltProcessor(html2XamlXslDoc);
}**/
}
// Apply XSLT to XML
string xaml = Html2XamlProcessor.TransformToString(xhtmlDoc.FirstChild);
return xaml;
}
}
}