HOW-TO: Creating a simple UserControl for displaying a FlowDocument

September 12, 2007

Hello. This is my first post about WPF.

This how-to will show you how to create a UserControl in which you can show a FlowDocument by passing a string containing the FlowDocument. It also allows Data Binding.

Lets start.

1. Create a UserControl called FlowDocumentViewer in Expression Blend (you can also do this in Visual Studio if you want) and add a FlowDocumentScrollViewer to LayoutRoot. Name it flowDocument (you can name it like you want, but I will be using this name in this guide). Your structure should be as follows:

Structure

2. Now, open the project in Visual Studio and add the following code to your FlowDocumentViewer.xaml.cs:

        public static readonly DependencyProperty ContentFlowDocumentStringProperty = DependencyProperty.Register(
            "ContentFlowDocumentString",
            typeof(string),
            typeof(FlowDocumentViewer),
            new FrameworkPropertyMetadata(null,
                FrameworkPropertyMetadataOptions.AffectsRender,
                new PropertyChangedCallback(DescriptionFlowDocumentUI.OnContentChanged)
            )
        );

        private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDocumentViewer control = (FlowDocumentViewer)d;
            control.flowDocument.Document =
                (System.Windows.Documents.FlowDocument)System.Windows.Markup.XamlReader.Load(
                    new MemoryStream(System.Text.UTF8Encoding.Default.GetBytes((string)e.NewValue))
                );
        }

        public string ContentFlowDocumentString
        {
            get { return (string)GetValue(ContentFlowDocumentStringProperty); }
            set { SetValue(ContentFlowDocumentStringProperty, value); }
        }

Lets see what this code does.

a) We added a DependencyProperty called ContentFlowDocumentStringProperty to our class. This DependencyProperty “points” at ContentFlowDocumentString property, as well as it tells, that this property of type string and we registered a callback function (OnContentChanged), which will be triggered when ContentFlowDocumentString is changed. Note that FrameworkPropertyMetadataOptions.AffectsRender piece of code. This tells WPF, that when this property is changed it affects the UI looks, so that WPF knows he has to render something.

b) The OnContentChanged function is called whenever our DependencyProperty is changed. Here we do the actual work of changing the FlowDocument in our control. We get the instance as an argument (DependencyObject d, which we cast to FlowDocumentViewer explicilty) and we create a new FlowDocument from the newly assigned string ((string)e.newValue).

c) Finally we added the actual property – ContentFlowDocumentString. See those GetValue and SetValue? Don’t be surprized, it is how DependencyProperties work. You don’t have a special (private) field in your class for them. WPF does all the work for you.

Well, that is pretty much it.

The usage is simple:

FlowDocumentViewer fdv = new FlowDocumentViewer();
fdv.ContentFlowDocumentString = stringContainingFlowDocumentXAML;

Or if you want to use data binding, it is absolutely similar to casual binding in WPF:

Binding b = new Binding();
b.Source = myBindingSource;
b.Path = new PropertyPath("PathToProperty");
FlowDocumentViewer fdv = new FlowDocumentViewer();
fdv.SetBinding(FlowDocumentViewer.ContentFlowDocumentStringProperty, b);

If you have questions / remarks / etc. feel free to post them. I will try to answer.

Advertisements