I have a FlowDocument containing stuff bound to my ViewModel like this:
<FlowDocumentReader>
<FlowDocument>
<Paragraph>
<Run Text="{Binding MyTextProperty}"/>
</Paragraph>
</FlowDocument>
</FlowDocumentReader>
Now I want to display a List of class using some kind of DataTemplate, but got no idea how to start. Say I got a class like:
public MyClass
{
String Title {get;set;}
String FlowText {get;set;}
}
public List<MyClass> MyList {get;set;}
And I'd like to bind this to the FlowDocument List, like this:
<FlowDocumentReader>
<FlowDocument>
<List Items="{Binding MyList}">
<Bold><Run Text="{Binding Title}"/></Bold>
<LineBreak/>
<Run Text="{Binding FlowText}"/>
</Paragraph>
</FlowDocument>
</FlowDocumentReader>
Of course this does not work - but I can't find any explanation how to bind Lists in a FlowDocument using Templates - is this possible?
See this question.
I think you have two options
- Using a ItemsControl
- Using an attached property
Update
A more Dynamic solution using two attached properties. A Resource with the Template is added to the Paragraph (which must have the x:Shared="False"
attribute set, otherwise we'll just keep adding the same elements over and over). Then the Source List and the name of the template resource is set as attached properties.
In the PropertyChanged callback, a check is made that both properties are set and then a Span
element is created for each item in the List. The span elements DataContext
is set to the current item to make the Bindings work
<FlowDocumentReader xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib">
<FlowDocument>
<Paragraph behaviors:ParagraphInlineBehavior.ParagraphInlineSource="{Binding MyList}"
behaviors:ParagraphInlineBehavior.TemplateResourceName="inlineTemplate">
<Paragraph.Resources>
<Collections:ArrayList x:Shared="False" x:Key="inlineTemplate">
<Bold>
<Run Text="{Binding Title}"/>
</Bold>
<LineBreak/>
<Run Text="{Binding FlowText}"/>
<LineBreak/>
</Collections:ArrayList>
</Paragraph.Resources>
</Paragraph>
</FlowDocument>
</FlowDocumentReader>
ParagraphInlineBehavior
public class ParagraphInlineBehavior : DependencyObject
{
public static readonly DependencyProperty TemplateResourceNameProperty =
DependencyProperty.RegisterAttached("TemplateResourceName",
typeof(string),
typeof(ParagraphInlineBehavior),
new UIPropertyMetadata(null, OnParagraphInlineChanged));
public static string GetTemplateResourceName(DependencyObject obj)
{
return (string)obj.GetValue(TemplateResourceNameProperty);
}
public static void SetTemplateResourceName(DependencyObject obj, string value)
{
obj.SetValue(TemplateResourceNameProperty, value);
}
public static readonly DependencyProperty ParagraphInlineSourceProperty =
DependencyProperty.RegisterAttached("ParagraphInlineSource",
typeof(IEnumerable),
typeof(ParagraphInlineBehavior),
new UIPropertyMetadata(null, OnParagraphInlineChanged));
public static IEnumerable GetParagraphInlineSource(DependencyObject obj)
{
return (IEnumerable)obj.GetValue(ParagraphInlineSourceProperty);
}
public static void SetParagraphInlineSource(DependencyObject obj, IEnumerable value)
{
obj.SetValue(ParagraphInlineSourceProperty, value);
}
private static void OnParagraphInlineChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Paragraph paragraph = d as Paragraph;
IEnumerable inlines = ParagraphInlineBehavior.GetParagraphInlineSource(paragraph);
string templateName = ParagraphInlineBehavior.GetTemplateResourceName(paragraph);
if (inlines != null && templateName != null)
{
paragraph.Inlines.Clear();
foreach (var inline in inlines)
{
ArrayList templateList = paragraph.FindResource(templateName) as ArrayList;
Span span = new Span();
span.DataContext = inline;
foreach (var templateInline in templateList)
{
span.Inlines.Add(templateInline as Inline);
}
paragraph.Inlines.Add(span);
}
}
}
}