Sunday, September 6, 2009

Bind HTML To Textblock with HTML to XAML Converter

I was recently looking for an easy way to get some existing HTML content resources into a WPF / Silverlight application. Now, in retrospect, it looks like I could have done something with a FlowDocument Viewer that might have been a little easier, but I figured I would post my solution in case it helps out someone else.

There are basically 3 pieces to binding HTML to a Textblock in WPF.
  1. Some HTML and a TextBlock to bind the HTML to.
  2. An HTML to XAML Converter (Provided by MSDN, adapted slightly)
  3. An Attached Property for the TextBlock.
The HTML to XAML Converter comes from a sort of proof of concept over on MSDN. I basically took the converter sample project and combined it all into one file so I could easily incorporate it into my project (or any other project for that matter; you can download the code below). The converter will parse the HTML, tokenize it, and convert it to a collection of Inline XAML elements that we can add to the TextBlock Inlines property.

All that's left is adding an Attached Property for us to bind to and replace the TextBlock's Inline elements. The way this works is to use a standard Attached Property and whenever our Attached Property changes, convert the bound HTML to a collection of Inline elements and add them to our TextBlock. The majority of the hard stuff is done by the HTML to XAML converter, but here is the code for the Attached Property and an example of the XAML to use it in your application.


Download Sample App / Code




// Attached Property to facilitate Binding HTMLto a TextBlock
    public static class HtmlTextBoxProperties 
    {         
        public static string GetHtmlText(TextBlock wb) 
        { 
            return wb.GetValue(HtmlTextProperty) as string; 
        } 
        public static void SetHtmlText(TextBlock wb, string html) 
        { 
            wb.SetValue(HtmlTextProperty, html); 
        } 
        public static readonly DependencyProperty HtmlTextProperty = 
            DependencyProperty.RegisterAttached("HtmlText", typeof(string), typeof(HtmlTextBoxProperties), new UIPropertyMetadata("", OnHtmlTextChanged)); 

        private static void OnHtmlTextChanged( 
            DependencyObject depObj, DependencyPropertyChangedEventArgs e) 
        { 
            // Go ahead and return out if we set the property
on something other than a textblock, or set a value that is not a string.
            var txtBox = depObj as TextBlock; 
            if (txtBox == null) 
                return; 
            if (!(e.NewValue is string)) 
                return; 
            var html = e.NewValue as string; 
            string xaml; 
            InlineCollection xamLines; 
            try 
            { 
                xaml = HtmlToXamlConverter.ConvertHtmlToXaml(html,
false);
                xamLines = ((Paragraph)((Section)System.Windows.Markup.XamlReader.Parse(xaml)).Blocks.FirstBlock).Inlines; 
            } 
            catch 
            { 
                // There was a problem parsing the html, return
out.
                return; 
            } 
            // Create a copy of the Inlines and add them to the
TextBlock.
            Inline[] newLines = new Inline[xamLines.Count]; 
            xamLines.CopyTo(newLines, 0); 
            txtBox.Inlines.Clear(); 
            foreach (var l in newLines) 
            { 
                txtBox.Inlines.Add(l); 
            } 
        } 
    } 




<TextBlock 
   x:Name="MyHtmlBlock"
   behaviors:HtmlTextBlockProperties.HtmlText="{Binding Path=MyHtml}"
   />




Now Playing - Matt & Kim - Daylight

12 comments:

  1. Works like a charm!
    Binding doesnt seem to be supported in another way.
    Your sample is great, thanks!

    ReplyDelete
  2. create work!!! helped me alot! thank you.

    ReplyDelete
  3. Thanks does what it says on the tin :D

    ReplyDelete
  4. Exists there a "standard way" in WPF (and Silverlight) for using HTML codes for displaying them to the user without writing a converter for it?

    e.g. Text="Abc &ndash; 123"

    I am a WPF newbie and I am wondering if there nothing already in place from Microsoft?

    ReplyDelete
  5. @hfrmobile As far as I know, there still isn't an easy way to do this even in the latest updates to Silverlight and WPF.

    ReplyDelete
  6. Thank you! Very useful and well done.

    ReplyDelete
  7. Где бинарник?

    ReplyDelete
  8. Excellent, thank you Jacob!

    ReplyDelete
  9. Very good!

    I know it is an old thread but I very new to WPF, so is there *any* chance you could possibly give some pointers on how to modify your code without using the databinding concept?, i.e. use it in a more procedural way where a user clicks the button, and the app assigns the InLines to a particular TextBlock?

    Thank you for any help/pointers you can share.

    ReplyDelete
    Replies
    1. I am good, I just figured it out for myself. Thank you for the great article! :)

      Delete
  10. Get Assignment help from the expert. Assignment homework help,just question answer is one of the best online homework market place, where you can tutor for any subject

    ReplyDelete