Created
March 7, 2017 14:40
-
-
Save SuavePirate/240cf86777fe5e03abc9595a627bded7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class HtmlTextBehavior : Behavior<TextBlock> | |
{ | |
protected override void OnAttached() | |
{ | |
base.OnAttached(); | |
AssociatedObject.Loaded += OnAssociatedObjectLoaded; | |
AssociatedObject.LayoutUpdated += OnAssociatedObjectLayoutUpdated; | |
} | |
protected override void OnDetaching() | |
{ | |
base.OnDetaching(); | |
AssociatedObject.Loaded -= OnAssociatedObjectLoaded; | |
AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated; | |
} | |
private void OnAssociatedObjectLayoutUpdated(object sender, object o) | |
{ | |
UpdateText(); | |
} | |
private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs routedEventArgs) | |
{ | |
UpdateText(); | |
AssociatedObject.Loaded -= OnAssociatedObjectLoaded; | |
} | |
private void UpdateText() | |
{ | |
if (AssociatedObject == null) return; | |
if (string.IsNullOrEmpty(AssociatedObject.Text)) return; | |
string text = AssociatedObject.Text; | |
// Just incase we are not given text with elements. | |
string modifiedText = string.Format("<div>{0}</div>", text); | |
// reset the text because we will add to it. | |
AssociatedObject.Inlines.Clear(); | |
try | |
{ | |
var element = XElement.Parse(modifiedText); | |
ParseText(element, AssociatedObject.Inlines); | |
} | |
catch (Exception) | |
{ | |
// if anything goes wrong just show the html | |
AssociatedObject.Text = text; | |
} | |
AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated; | |
AssociatedObject.Loaded -= OnAssociatedObjectLoaded; | |
} | |
/// <summary> | |
/// Traverses the XElement and adds text to the InlineCollection. | |
/// </summary> | |
/// <param name="element"></param> | |
/// <param name="inlines"></param> | |
private static void ParseText(XElement element, InlineCollection inlines) | |
{ | |
if (element == null) return; | |
InlineCollection currentInlines = inlines; | |
var elementName = element.Name.ToString().ToUpper(); | |
switch (elementName) | |
{ | |
case ElementA: | |
var link = new Hyperlink(); | |
var href = element.Attribute("href"); | |
if(href != null) | |
{ | |
try | |
{ | |
link.NavigateUri = new Uri(href.Value); | |
} | |
catch (System.FormatException) { /* href is not valid */ } | |
} | |
inlines.Add(link); | |
currentInlines = link.Inlines; | |
break; | |
case ElementB: | |
case ElementStrong: | |
var bold = new Bold(); | |
inlines.Add(bold); | |
currentInlines = bold.Inlines; | |
break; | |
case ElementI: | |
case ElementEm: | |
var italic = new Italic(); | |
inlines.Add(italic); | |
currentInlines = italic.Inlines; | |
break; | |
case ElementU: | |
var underline = new Underline(); | |
inlines.Add(underline); | |
currentInlines = underline.Inlines; | |
break; | |
case ElementBr: | |
inlines.Add(new LineBreak()); | |
break; | |
case ElementP: | |
// Add two line breaks, one for the current text and the second for the gap. | |
if (AddLineBreakIfNeeded(inlines)) | |
{ | |
inlines.Add(new LineBreak()); | |
} | |
Span paragraphSpan = new Span(); | |
inlines.Add(paragraphSpan); | |
currentInlines = paragraphSpan.Inlines; | |
break; | |
case ElementLi: | |
inlines.Add(new LineBreak()); | |
inlines.Add(new Run { Text = " • " }); | |
break; | |
case ElementUl: | |
case ElementDiv: | |
AddLineBreakIfNeeded(inlines); | |
Span divSpan = new Span(); | |
inlines.Add(divSpan); | |
currentInlines = divSpan.Inlines; | |
break; | |
} | |
foreach (var node in element.Nodes()) | |
{ | |
XText textElement = node as XText; | |
if (textElement != null) | |
{ | |
currentInlines.Add(new Run { Text = textElement.Value }); | |
} | |
else | |
{ | |
ParseText(node as XElement, currentInlines); | |
} | |
} | |
// Add newlines for paragraph tags | |
if (elementName == ElementP) | |
{ | |
currentInlines.Add(new LineBreak()); | |
} | |
} | |
/// <summary> | |
/// Check if the InlineCollection contains a LineBreak as the last item. | |
/// </summary> | |
/// <param name="inlines"></param> | |
/// <returns></returns> | |
private static bool AddLineBreakIfNeeded(InlineCollection inlines) | |
{ | |
if (inlines.Count > 0) | |
{ | |
var lastInline = inlines[inlines.Count - 1]; | |
while ((lastInline is Span)) | |
{ | |
var span = (Span)lastInline; | |
if (span.Inlines.Count > 0) | |
{ | |
lastInline = span.Inlines[span.Inlines.Count - 1]; | |
} | |
} | |
if (!(lastInline is LineBreak)) | |
{ | |
inlines.Add(new LineBreak()); | |
return true; | |
} | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment