WPF FlowDocuments - What, When, Why, How?
As I learn more and more about WPF, I am continually amazed at how much I don't know. I recently discovered FlowDocuments (look here for more info on the basics of FlowDocuments) and how wonderful they are. Okay, I knew about them a little, but I ignored them because I didn't think you could do very much with them other than present text to the user. Well how wrong I was!
I found my self using lots and lots of textblocks to display some information to the user. I was struggling with several limitations of textblocks: how they wrap depends on the container, databinding part of the text is not possible without databinding all of it, and there is no way to allow the user to select the text for copying to the clipboard. These limitations caused me to write lots of code to work around the issues and provide the user experience that I wanted. I had dozens of ValueConverters to format the bound data into the correct strings and even more code and XAML to get the text blocks to wrap appropriately.
A colleague suggested that FlowDocuments might provide some answers, So dug into them and discovered a world of solutions to the problems that I faced. Flow Documents are kind of like HTML. In fact you can find lots of sites that compare the two if you search for it. Here are the features that drew me in and forced me to rewrite my entire interface to use FlowDocuments:
- Selection and copying to the clipboard support is natural and free.
- The memory foot print compared to all the controls that I was using before was a huge improvement and likewise the performance was better as well (although I didn't measure it).
- For displaying text there is nothing better. You can easily indent, bold, align, wrap, etc.
- Hyperlinks work properly only inside a FlowDocument.
- If you use a FlowDocumentScrollViewer to display your flow document (this is what I used), you get Scrolling, Find, Print, and Zoom capabilities.
The one downside is that like textblocks there is not a simple way to bind data to the document. But for a read-only view of data, it is perfect.
Based on my experience with FlowDocument so far, I would recommend using them anytime the number of textblocks outnumber the other controls on your window. You can also embed a flow document within another container control like a Grid. But I wouldn't recommend that unless you really need it.
I will write more posts on how I am using flowdocuments and the special things I have learned, but here is a quick synopsis of how I used them in this particular case:
I replaced the TreeView that I had bound to my hierarchical data with a recursive method that added indented paragraphs to a section. After it's done, I simply add the section to the flowdocument. Indenting a paragraph is a little tricky, but that's a later post. Of course, the tree is no longer collapsible, but I didn't really need it to be anyway. The indentation was all I needed. I created my own value converters that could convert by data into paragraphs. That allowed my recursive function to be very generic.
I used Figures to have a right justified column of numbers associated with some but not all of the paragraphs in my hierarchy. There's not much info about Figures so I will probably make that one of my first posts after this one.
Paragraphs can have borders, so I used that fact to add some nice horizontal lines to the output that lined up the numbers on the right with there headers and grouped some of the paragraphs together.
One of the last things I did was to add in a way for the user to add additional text to certain areas. In one instance I added a button that simply created paragraphs for some objects in the hierarchy that are skipped by default. In other areas, I created my own kind of expander that has the look of a tree view item and adds additional paragraphs below itself. I called this last one an CollapsibleSection because I derived it from Section. I will definitely have a post on that one.