Introduction
I recently had need to display part of some bound read-only text with some of the text bolded. I could have used multiple controls, but that would reduce maintainability. I wanted to use HTML because that is well understood by many people, and it seemed like it would be useful in other situations.
The RichTextBox Design
I used a behavior to do this, but it could have been class derived from the RichTextBox
.
public class HtmlRichTextBoxBehavior : DependencyObject
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached("Text", typeof(string),
typeof(HtmlRichTextBoxBehavior), new UIPropertyMetadata(null, OnValueChanged));
public static string GetText(RichTextBox o) { return (string)o.GetValue(TextProperty); }
public static void SetText(RichTextBox o, string value) { o.SetValue(TextProperty, value); }
private static void OnValueChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
var richTextBox = (RichTextBox)dependencyObject;
var text = (e.NewValue ?? string.Empty).ToString();
var xaml = HtmlToXamlConverter.ConvertHtmlToXaml(text, true);
var flowDocument = XamlReader.Parse(xaml) as FlowDocument;
HyperlinksSubscriptions(flowDocument);
richTextBox.Document = flowDocument;
}
private static void HyperlinksSubscriptions(FlowDocument flowDocument)
{
if (flowDocument == null) return;
GetVisualChildren(flowDocument).OfType<Hyperlink>().ToList()
.ForEach(i => i.RequestNavigate += HyperlinkNavigate);
}
private static IEnumerable<DependencyObject> GetVisualChildren(DependencyObject root)
{
foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
{
yield return child;
foreach (var descendants in GetVisualChildren(child)) yield return descendants;
}
}
private static void HyperlinkNavigate(object sender,
System.Windows.Navigation.RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
}
This code depends on code that is available from Microsoft; the HtmlToXamlConverter
. All this Microsoft code is included in the folder HtmlConverter in the sample project.
Using the Behavior with a RichTextBox
The most straight forward way to use the behavior is as follows:
<RichTextBox Grid.Row="1"
local:HtmlRichTextBoxBehavior.Text="{Binding Text}" />
You could have some unintended issues with this because you can still edit the RichTextBox
. There are a few other issues also. To fix at least most of these issues I did the following:
<RichTextBox Background="Transparent"
BorderThickness="0"
behaviors:RichTextBoxHelper.Text="{Binding Name}"
IsDocumentEnabled="True"
IsReadOnly="True" />
This makes the RichTextBox
look a lot more like a TextBox
. The IsDocumentEnabled="True"
is required to enable the hyperlinks. the IsReadOnly="True"
ensures that the content cannot be changed.
The WebBrowser Design
Used a behavior also for binding a string
containing the HTML to the contents of a WebBrowser
:
public class WebBrowserBehavior
{
public static readonly DependencyProperty BodyProperty =
DependencyProperty.RegisterAttached("Body", typeof(string), typeof(WebBrowserBehavior),
new PropertyMetadata(OnChanged));
public static string GetBody(DependencyObject dependencyObject)
{
return (string)dependencyObject.GetValue(BodyProperty);
}
public static void SetBody(DependencyObject dependencyObject, string body)
{
dependencyObject.SetValue(BodyProperty, body);
}
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((WebBrowser)d).NavigateToString((string)e.NewValue);
}
This is about a simple a behavior as can be created
Using the Behavior with a WebBrowser
The most straight forward way to use the behavior is as follows:
<WebBrowser local:WebBrowserBehavior.Body="{Binding Text}" />
Points of Interest
Right now the Hyperlinks do not work for the RichTextBox
. Apparently this is an issue with the WPF RichTextBox
. If something similar is done with the WinForm RichTextBox
, "DetectUrls="True"
" will enable Hyperlinks.
When a Hyperlink is clicked in the WebBrowser
control, the URI is immediately opened. This may not be particularly desirable, so may want to disable it.
One of the advantages of using the RichTextBox
is that the behavior can be updated to do a conversion back. Actually some of the key strokes work in the RichTextBox
so that you can bold an entry with the "ctrl + b" keystrokes. I did not create the ability to convert back, but would be an easy task.
History
- 05/02/2016: Initial version.
- 05/03/2016: Added
WebBrowser
version to sample