views:

464

answers:

3

I have some data that I want to present in a FlowDocument. This will basically be a view that explains the data in a friendly way, with section headers, paragraphs of text, etc., and which I will display in a FlowDocumentScrollViewer.

To this end, I would like to create a bulleted list (<List>) out of the contents of an ObservableCollection. I know how to do that with ItemsControl, but how do I do it for ListItem elements in a FlowDocument, since they're part of the TextElement class hierarchy rather than the Control hierarchy? Is there an equivalent of ItemsControl for text content inside a TextBlock or FlowDocument?


Edit: The article Sergey linked to is the perfect starting point. The only problem is that the article's code can only use a Section or a TableRowGroup as the items panel, and doesn't yet support using a <List>. But that was trivial to fix -- just a matter of adding this code at the end of ItemsContent.GenerateContent, just before the final else:

else if (panel is List)
    ((List) panel).ListItems.Add((ListItem) element);
A: 

Instead of using a FlowDocument, you can use an ItemsControl and change the panel used to display items to a WrapPanel. This will allow you use the ItemsControl as you want, but change its display semantics to a WrapPanel (which I believe functions like a FlowDocument. You'd do it something like this:

<ItemsControl>
    <ItemsControl.ItemsPanelTemplate>
        <WrapPanel />
    </ItemsControl.ItemsPanelTemplate>
</ItemsControl>

You can set any properties on the inner WrapPanel as you desire.

Andy
I see where you're going, but no, that wouldn't use a WrapPanel, it would use a standard StackPanel (since list items are one above the other). Yes, it has crossed my mind to simply abandon FlowDocument and use normal controls, but FlowDocument has its benefits (select+copy, printing), so I'm still curious if there's a way to actually databind inside a FlowDocument.
Joe White
I think you missed his point (or i misunderstood your comment), you can replace the StackPanel by setting the ItemsPanelTemplate property, in his example the stackpanel would be replaced by a WrapPanel. You're right though, this doesn't accomplish what you want.
Bubblewrap
Sure, I got that. But if I'm generating a bulleted list, I don't *want* to replace the StackPanel with a WrapPanel.
Joe White
A: 

I think you are looking for the List element: http://msdn.microsoft.com/en-us/library/system.windows.documents.list.aspx

Bubblewrap points out a few more details. You'd likely bind to the ListItems property and need to use a ValueConverter to convert your source list to a list of type ListItemsCollection.

Bubblewrap points out that this is readonly and that the ListItemsCollection has an internal constructor. So...

I think what you'd have to do is this:

<ContentControl Content="{Binding TheArrayOfText, Converter={StaticResource StringsToListConverter}" />

This is pretty unfortunate, but I think it would work. You'd have to write a converter to create a new List object and call .Add( on each item.

Anderson Imes
But the List element doesnt support binding to an arbitrary collection to generate it's ListItems, does it?
Bubblewrap
The ListItems collection should. You'd have to convert whatever collection it was to a ListItemCollection (via a ValueConverter), but it would give you output in a completely supported and FlowDocumentesque way.
Anderson Imes
Except that the ListItems property is readonly and that one is not meant to create a ListItemsCollection themselves. (http://msdn.microsoft.com/en-us/library/system.windows.documents.listitemcollection.aspx)
Bubblewrap
+5  A: 

What you are looking for is possible, but requires significant amount of coding. Fortunately, Vincent Van Den Berghe posted a nice article on the MSDN describing how to Create Flexible UIs With Flow Documents And Data Binding , including the code!

Sergey Aldoukhov
Awesome. The article won't work with <List> but it only took a small tweak to extend it -- see my edit to the question. Thanks!
Joe White
Unfortunately you can print then the flowdocument directly. You must render it in the flowdocument reader first.
Shurup