Currently I'm designing an application that should at one point be able to create a report from a Xaml template File using Data Binding (involving a FlowDocument
).
The idea was to simply convert a dynamically loaded control via BlockUIContainer to be printable in a FlowDocument.
As long as I load an entire file into a single FrameworkElement
and set the DataContext
property, the Data Binding works like a charm.
foreach (Order order in orders)
{
BlockUIContainer container = new BlockUIContainer();
container.Child = (FrameworkElement)GetOrderControl();
(container.Child as FrameworkElement).DataContext = order;
document.Blocks.Add(container);
}
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.SystemIdle,
new Action(() => { return; }));
All the GetOrderControl()
method does is read from a FileStream
a parse the content via XamlReader.Load()
. The file is structured like this:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
...
<TextBlock Text="{Binding Path=Country}" />
...
</Gird>
Now the application should add BlockUIContainer
s dynamically according to the dataset. I need to do it in code behind to implement custom pagination, because the reports might get longer than one page.
Since I only want a single template file, I've packed my header, footer and grouping controls all in a single xaml file like this:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BlockUIContainer Name="PageHeader">
<Grid ... />
</BlockUIContainer>
<BlockUIContainer Name="Element">
<Grid ... />
</BlockUIContainer>
</FlowDocument>
The <Grid ... />
control inside the "Element" named BlockUIContainer is just exactly the Grid control used in the example before.
Now all I do is get the child of the BlockUIContainer and create a copy of that by saving it to a string and back to a FrameworkElement
and set the DataContext
.
foreach (Order order in orders)
{
BlockUIContainer container = new BlockUIContainer();
container.Child = (FrameworkElement)XamlReader.Parse(XamlWriter.Save(elementControl));
(container.Child as FrameworkElement).DataContext = order;
document.Blocks.Add(container);
}
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.SystemIdle,
new Action(() => { return; }));
Here however the data binding is not evaluating. I tried calling the UpdateLayout()
method on the FrameworkElement
after setting the DataContext
.
That does evaluate at least some Bindings in a <DataTrigger Binding="{Binding Path=DangerousGoods}" />
for a <Style>
Element setting the Visibility of some child controls, but none of the Bindings like <TextBlock Text="{Binding Path=Country}" />
are not evaluated.
I'm at a loss here. How do I get the remaining bindings to work again after parsing them? I don't really want to create several files for one document.
Nevermind, I found the error... the Bindings get evaluated the first time the control is created.
The XamlWriter
then "destroys" the Binding by evaluating the text and writing the original text output (which was empty) into the element's Text
property.