WPF Flow Document For Beginners

As part of an article that I am creating for www.codeproject.com I decided to look into using the System.Windows.Documents namespace and have a look at seeing if I could make a semi-cool’ish looking document.

Now when you use FlowDocuments, there are several container WPF container controls which you may host a FlowDocument in. These WPF container controls vary in what they provide. Lets see the difference shall we

  • FlowDocumentScrollViewer : Simply displays the entire document and provides a scroll bar. Like a web page
  • FlowDocumentPageViewer : Shows document as individual pages, and allows user to adjust zoom level.
  • FlowDocumentReader : Combines FlowDocumentScrollViewer and FlowDocumentPageViewer into a single control, and exposes text search facilities.

For example a FlowDocumentPageViewer is as shown below

flowdocumentpageviewer.png

For those of who have not come across the FlowDocument, here is a list of some of the things that can be done with it

  • Allow paragraphing
  • Allow anchoring of images
  • Allow hyperlinks
  • Allow text blocks
  • Allow tables
  • Allow subscript/superscript text
  • Allow UIElements (such as Button etc etc)
  • Allow text effects

Think of FlowDocument(s) as a mini desktop publishing type interface. Though I’m sure things like Quark are going to yield more flexability. Never the less, the results of FlowDocument(s) could be thought as be able to create that sort of page publishing type layout.

What I’m going to do now is show you how to create a few of the various FlowDocument elements both in XAML and in code as they are little different actually.

PARAGRAPH

In XAML

<!-- FLOW DOCUMENT VIEWER START -->    
<FlowDocumentPageViewer x:Name="flowDocViewer"  Margin="0,0,0,0" Background="#FF414141" Zoom="80" >         
<!-- FLOW DOCUMENT START -->
<FlowDocument x:Name="flowDoc" Foreground="White" FontFamily="Arial"  >
<Paragraph x:Name="para1" FontSize="11">
The following details have been obtained from Amazon to match your initial query.
Some of the returned values may have been empty, so have been ommitted from theresults shown here.
Also where there have been more than one value returned viathe Amazon Details, these to have been
omitted for the sake of keeping things simplefor this small demo application. Simple is good,
when trying to show how something works
</Paragraph>
</FlowDocument>
</FlowDocumentPageViewer>


In C# code behind

Paragraph paraHeader = new Paragraph();
paraHeader.FontSize = 12;
paraHeader.Foreground = headerBrsh;
paraHeader.FontWeight = FontWeights.Bold;
paraHeader.Inlines.Add(new Run("Paragraph Text"));
flowDoc.Blocks.Add(paraHeader);




HYPERLINKS

In XAML

<Paragraph FontSize=”11″>

     <Hyperlink  Click="hl_Click" NavigateUri="www.google.com">Click Here</Hyperlink>
</Paragraph>



In C# code behind

Paragraph paraValue = new Paragraph();Hyperlink hl = new Hyperlink(new Run("Click Here To View The Link Data"));

hl.FontSize = 11;hl.NavigateUri = new Uri(nonNullprop.PropertyValue);

hl.Click += new RoutedEventHandler(hl_Click);paraValue.Inlines.Add(hl);
flowDoc.Blocks.Add(paraValue);




EMBEDDING UI ELEMENTS

In XAML

 

<BlockUIContainer>

    <Button Width="60" Height="60" Click="Button_Click">

        Click me

    </Button>

</BlockUIContainer>


In C# code behind

 

BlockUIContainer uiCont = new BlockUIContainer();

Button b = new Button();

b.Width = b.Height = 60;

b.Click += new RoutedEventHandler(Button_Click);

b.Content = "Click me";

flowDoc.Blocks.Add(uiCont);

Of course this is only touching the surface of what can be done with FlowDocuments. But it gives you an idea of how flexable the formatting of documents is with WPF.

The screen shot below shows an example with some Paragraphs/Tables/Hyperlinks and UIElements in place

flowdocumentexample.png



					
Advertisements

30 thoughts on “WPF Flow Document For Beginners

  1. Yes, I like the abstraction to html-esque tags, rather than explicitly laying out text boxes etc. Makes building code generators much easier, among other things.
    That screen looks slick…eagerly awaiting your next article.

    Daniel

  2. sacha says:

    Yeah I think flow documents are pretty cool.

    My new article should be up this week at some point.

    I just want to try and fix something to do with AWS beeing annoying.

    Grrr.

    I am beginning to regret using that. Its flaky

  3. Cornel says:

    Hi,
    Nice article!

    What’s the relationship between BlockUIContainer and button? The button shouldn’t be added to the BlockUIContainer or something?

    Thanks

  4. sacha says:

    Thanks Cornel

    Basically to host a UIElement in a FlowDocument, it needs to be added to a BlockUIContainer.

    Trying to add a UIElement such as button straight into a Paragraph isnt allowed.

    You must wrap it in a BlockUIContainer.

    Hope this answers you query

  5. Cornel says:

    I’m using .NET 3.5.

    I made a test project and in constructor I added the following code:

    BlockUIContainer uiCont = new BlockUIContainer();
    Button b = new Button();
    b.Width = b.Height = 60;
    b.Content = “Click me”;
    flowDoc.Document.Blocks.Add(uiCont);

    “flowDoc.Document.Blocks.Add(uiCont);” crashes my test app with the following message:

    EventType clr20r3, P1 test.exe, P2 0.0.0.0, P3 4785e052, P4 presentationframework, P5 3.0.0.0, P6 470bc696, P7 660b, P8 e1, P9 system.windows.markup.xamlparse, P10 NIL.

    flowDoc is a FlowDocumentReader control.

  6. sacha says:

    You need to make sure that you add the BlockUIContainer to an actual FlowDocument. Not a FlowDocumentReader. Have a look at the following XAML.

    <!– FLOW DOCUMENT VIEWER START –>

    <FlowDocumentPageViewer x:Name=”flowDocViewer” Margin=”0,0,0,0″ Background=”#FF414141″ Zoom=”80″ >

    <!– FLOW DOCUMENT START –>

    <FlowDocument x:Name=”flowDoc” Foreground=”White” FontFamily=”Arial” />
    </FlowDocumentPageViewer>

    Then if you have some XAML like this you will be able to add a new BlockUIContainer like you show

    BlockUIContainer uiCont = new BlockUIContainer();
    Button b = new Button();
    b.Width = b.Height = 60;
    b.Content = “Click me”;
    flowDoc.Document.Blocks.Add(uiCont);

    I know this works, as I have a test app which I am just about to publish at codeproject which is setup like this, and it works just fine.

    I think your problem is that your trying to add a BlockUIContainer to a FlowDocumentReader and not a FlowDocument control.

  7. I have the following code:
    public class IUSHyperlink:Hyperlink
    {
    public IUSHyperlink()
    {

    this.ToolTip = “Esta liga abrirá una ventana interna”;
    this.Click += new RoutedEventHandler(MyHyperlink_RequestNavigate);

    }

    void MyHyperlink_RequestNavigate(object sender, RoutedEventArgs e)
    {
    MessageBox.Show(“Liga con “+this.Tag+”Activada”);
    NavigationWindow window = new NavigationWindow();
    // window.Source = e.Uri;
    window.Show();
    }

    }
    }

    But guess what!!!
    the click does no tdo anithing, the hyperlink is shown but no response neither to click CTRL+click or SHIFT+Click..
    Any ideas??

  8. sacha says:

    I would try and put it in the XAML like

    xmlns:local=”clr-namespace:yournamspace”

    Esta liga abrirá una ventana interna

    this will give you the event in the window that contains the FlowDocument.

    Thats what Id try

  9. mmm…… that would be fine if the link i am “drawing” could be static-generated, but is dinamically via:
    private IUSHyperlink creaLiga(RelacionTO liga, string contenido)
    {
    RelacionFraseTesisTO tesis = null;
    RelacionFraseArticulosTO articulos = null;
    FachadaBusquedaTradicionalClient fachadaBusqueda = new FachadaBusquedaTradicionalClient();
    IUSHyperlink ligaNueva = new IUSHyperlink();
    ligaNueva.Inlines.Add(new Run(contenido));
    ligaNueva.IsEnabled = true;
    switch (liga.Tipo)
    {
    case “0”:
    articulos = fachadaBusqueda.getRelacionesFrasesArticulos(Int32.Parse(liga.Ius),
    Int32.Parse(liga.IdRel))[0];
    ligaNueva.Tag = “ventanaEmergente(” + articulos.IdLey +
    “,” + articulos.IdArt + “,” + articulos.IdRef + “);”;
    break;
    case “1”:
    tesis = fachadaBusqueda.getRelacionesFrasesTesis(Int32.Parse(liga.Ius),
    Int32.Parse(liga.IdRel))[0];
    ligaNueva.Tag = “Tesis(” + tesis.Ius + “)”;
    break;
    case “2”:
    tesis = fachadaBusqueda.getRelacionesFrasesTesis(Int32.Parse(liga.Ius),
    Int32.Parse(liga.IdRel))[0];
    ligaNueva.Tag = “Ejecutoria(” + tesis.IdLink + “)”;
    break;
    case “3”:
    tesis = fachadaBusqueda.getRelacionesFrasesTesis(Int32.Parse(liga.Ius),
    Int32.Parse(liga.IdRel))[0];
    ligaNueva.Tag = “Votos(” + tesis.IdLink + “)”;
    break;
    default:
    tesis = fachadaBusqueda.getRelacionesFrasesTesis(Int32.Parse(liga.Ius),
    Int32.Parse(liga.IdRel))[0];
    ligaNueva.Tag= “Acuerdos(” + tesis.IdLink + “)”;
    break;
    }
    fachadaBusqueda.Close();
    return ligaNueva;
    }

  10. sacha says:

    Mmm Ok, yeah in that case Id be doing just what you are doing, experimenting. Could you not have a DP in there that is set in code behind in the window

  11. DP??? Data Processing??? i don’t thing so, i subclassed HyperLink inorder to process the click for that specific click management and do all the process in the new class, wich i thing is the best practice.
    Greetings

  12. sacha says:

    No I mean Dependecny Property, so you could set that these Props it window, and still use the subclass in the XAML.

    Another option is to try and pass all the parameters in via XAML, have a look at

    http://www.beacosta.com/blog/?m=200603

    Which shows how to pass params in XAML.

    I cant see why you cant just use data binding also, if you bind the link to some DP and then just update the DP with the value of your dynamically created link, it should all be cool

  13. Stupidity can be my mistake… but getting back to the basics…
    EVERY time you have a hyperlink inside a RichTextEditor, or any other viewer -i guess- you must set the IsDocumentEnabledProperty as true, then the document will be enabled (wow!!), if not the default value of false will disable the doc, and -of course- the links, it doesn’t matter if the IsEnabled porperty is set on the document nor link, so one must check all the “line” for the IsEnabled path PLUS the IsDocumentEnabled one of the document container.
    Sorry for the interruption, now get back to the usual bloging reading…:D
    Greetings and thanks for the advices…

  14. sacha barber says:

    Ha Ha, now thats funny. I didnt know that, so we both learnt from that. Cool

  15. I’m developing a kinda chat application and I stumbled upon a problem of correctly displaying chat history. First, I found you chat sample on Codeproject, but it didn’t help much, since you are displaying history in a plain TextBox. But I need RichTextBox.

    Now I found this article and it’s superior. Exactly what I need. Especially code snippets on how to create document elements programmatically. Thanks so much.

  16. sacha says:

    Cool

    This other article may be useful to you also http://www.codeproject.com/KB/IP/WCFWPFChatRoot.aspx

  17. @sacha

    Yes, I found this app as well. But this article turns out to be the most helpful.

  18. Joss Attridge says:

    Nice article – Section on EMBEDDING UI ELEMENTS is missing the code to add the ui element to the BlocklUiContainer. Should include uiCont.Child = b;

    The example works then 😉

    Joss.

  19. Pramod.P.V says:

    Sacha.. Nice article..
    I have a situation where i need to be able to print the document. Now the document must always be of a fixed size. However on screen i would like to have flow documents facilities. what wuold be the best way to approach this.

  20. sacha says:

    I think there is XPS document printing facilities which may be off help.

    I have not done too much with printing, to be honest, so am unable to advise in detail. BUt I would have a look at XPS printing

    Have a look at this post which allows you to print a Visual as a document

    http://blogs.msdn.com/ptallett/archive/2006/05/11/595612.aspx

    and here is an overview of Printing

    http://msdn.microsoft.com/en-us/library/aa970449.aspx

  21. VINAYAK says:

    Hi,
    Hey was on flame and wanted to have to read images and tables from a docx using open xml sdk 2 and put it in flowdocument using c# code behind.
    A source would be a life saver.

  22. grkowalski says:

    Thank you for your article. I am just starting out with flow documents and I am struggling with how to add a hyperlink that jumps to specific section within a document, and to a specific within another xaml flow doc. The flow documents are read in by a WPF/C# app that displays the xaml flow document (which serves as a help page).

    • sacha says:

      I have to say that is not something I have done before, So I am afraid its google time for you.

  23. Rishabh says:

    Thnks Sacha for this tutorial, I have a problem in my task…..iam able to get a text file on a flow document but now i have to divide the contents in proper pagebreaks at runtime i.e if contents are huge they shud get itself in number of pages that too at runtime.

    Please Help…..

  24. Rishabh says:

    Im attaching the code…….

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    namespace TextOnFlowDoc
    {
    ///
    /// Interaction logic for Page1.xaml
    ///
    public partial class Page1 : Page
    {
    public Page1()
    {
    InitializeComponent();
    Paragraph paragraph = new Paragraph();

    paragraph.Inlines.Add(System.IO.File.ReadAllText(@”C:Lis.txt”));
    paragraph.FontFamily = new FontFamily(“CourierNew”);

    FlowDocument document = new FlowDocument(paragraph);
    // FlowDocumentReader rdr = new FlowDocumentReader();
    FlowDocScl.Document = document;

    }

    }
    }

    Now this “FlowDocScl” is now a flow document and needs to be breaked into pages AT RUNTIME.

    Please help….

  25. Rishabh says:

    Just to tell you…..
    “FlowDocScl” is a flow document scroll viewer.

    Thanks.

  26. Rishabh says:

    Hi Sacha, this time iam upto a task which requires annotating (coloring) a flow document content at alternate lines i.e. coloring only the 1st,3rd,5th…lines while leaving 2nd,4th…as it is.

    Can you help me in this regard….
    Thanks in advance.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: