Binding a table in a FlowDocument
Inspired by a post from Paul here providing a method to bind data to a FlowDocument Run object, I extended the idea to bind to a dynamic Table. I prefer the attached property syntax in Paul's approach to a "BindableRun" object that Filipe used here. I didn't change the name from Paul's code (the name was fine for my purposes).
public static class BindableExtender
{public static Table GetBindableTable(DependencyObject obj)
{ return (Table)obj.GetValue(BindableTableProperty);}
public static void SetBindableTable(DependencyObject obj, Table tbl)
{obj.SetValue(BindableTableProperty, tbl);
}
public static string GetBindableText(DependencyObject obj)
{return (string)obj.GetValue(BindableTextProperty);
}
public static void SetBindableText(DependencyObject obj,
string value)
{ obj.SetValue(BindableTextProperty, value);}
public static readonly DependencyProperty BindableTextProperty =
DependencyProperty.RegisterAttached("BindableText" ,typeof(string) ,
typeof(BindableExtender),new UIPropertyMetadata(null,
BindableProperty_PropertyChanged));
public static readonly DependencyProperty BindableTableProperty =
DependencyProperty.RegisterAttached("BindableTable", typeof(Table), typeof(BindableExtender),new UIPropertyMetadata(null,
BindableProperty_PropertyChanged));
private static void BindableProperty_PropertyChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{if (dependencyObject is Run)
{ ((Run)dependencyObject).Text = (string)e.NewValue;}
if (dependencyObject is FlowDocument)
{((FlowDocument)dependencyObject).Blocks.Clear();
if (e.NewValue != null)
{((FlowDocument)dependencyObject).Blocks.Add((Block)e.NewValue);
}
}
}
}
I added all of the code that handles Tables. Not too hard to find. The important stuff regarding binding happens in the PropertyChanged event. If the object passed is a FlowDocument, the code clears the current blocks that might be in the existing FlowDocument and then adds the Table.
How did I build the table? Using code like this to build the table:
private static Table BuildTable()
{ Table tbl = new Table(); TableRowGroup rowGrp = new TableRowGroup();tbl.RowGroups.Add(rowGrp);
TableRow row = new TableRow();rowGrp.Rows.Add(row);
// you can style a row ... // row.Style = Application.Current.TryFindResource("HeaderRowStyle") as Style;row.Cells.Add(new TableCell(new Paragraph(new Run("DataLabel:"))));
Paragraph pg = new Paragraph(); // or you can style a Paragraph // I couldn't get pg.SetResourceReference(Paragraph.StyleProperty, "DataStyle") to work // it seems to never locate the Style resource properly ... the other approach below works // however // pg.Style = Application.Current.TryFindResource("DataStyle") as Style;pg.Inlines.Add(new Run("Some data"));
row.Cells.Add(pg);
return tbl;}
I added a property to the class bound by the UI:
public Table Metadata { get { return BuildTable(); }}
Then, in the XAML, I bound to the property:
<FlowDocument local:BindableExtender.BindableTable="{Binding Path=Metadata}">
<Paragraph/>
</FlowDocument>
The important thing to note here is the use of the attached property: local:BindableExtender.BindableTable.
The local namespace refers back to the host namespace (and since it's in the same assembly, the syntax just includes the namespace):
xmlns:local="clr-namespace:PhotoScroll"
Comments
Good post! I have some drafts sitting in my blog for a whole suite of elements/properties that add binding within FlowDocument.
I'll publish them soon, promise :)
Posted by: Filipe Fortes | April 9, 2007 3:58 PM
I look forward to seeing them!
Posted by: Aaron | April 9, 2007 7:32 PM