Insert separator (blank line) in databound flowdocument
In my MVVM WPF application I'm using a databound flowdocument. I used the technique described here to be able bind my data to the flowdocument. My flowdocument is bound to a public property in my viewmodel. The property is an ienumerable of a custom type, like this:
public IEnumerable<Person> Persons
{
get { return _persons; }
set { _persons = value; }
}
The flowdocument is shown in a FlowDocumentScrollViewer control. The document looks like:
code name lastname
---- ----- ---------
1 john johnson
1 peter peterson
2 jane jane
3 john doe
The binding works fine however I want to add a blank line after each distinct code:
code name lastname
---- ----- ---------
1 john johnson
1 peter peterson
2 jane jane
3 john doe
My Xaml in the view is:
<FlowDocumentScrollViewer>
<FlowDocument>
<flowdoc:ItemsContent ItemsSource="{Binding Path=Persons}">
<flowdoc:ItemsContent.ItemsPanel>
<DataTemplate>
<flowdoc:Fragment>
<Table BorderThickness="1" BorderBrush="Black">
<TableRowGroup flowdoc:Attached.IsItemsHost="True">
<TableRow Background="LightBlue">
<TableCell>
<Paragraph>ronde</Paragraph>
</TableCell>
<TableCell>
<Paragraph>hal</Paragraph>
</TableCell>
<TableCell>
<Paragraph>datum</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</flowdoc:Fragment>
</DataTemplate>
</flowdoc:ItemsContent.ItemsPanel>
<flowdoc:ItemsContent.ItemTemplate>
<DataTemplate>
<flowdoc:Fragment>
<TableRow>
<TableCell>
<Paragraph>
<flowdoc:BindableRun BoundText="{Binding Path=Code}" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph>
<flowdoc:BindableRun BoundText="{Binding Path=Name}" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph>
<flowdoc:BindableRun BoundText="{Binding Path=LastName}" />
</Paragraph>
</TableCell>
</TableRow>
</flowdoc:Fragment>
</DataTemplate>
</flowdoc:ItemsContent.ItemTemplate>
</flowdoc:ItemsContent>
Any suggestions on how to do this without breaking the MVVM rules? It seems like databinding a flowdocument comes with the price of inflexible layout.
Thanks in advance
In order to group your data, use a CollectionViewSource
like this:
<Window.Resources>
<CollectionViewSource x:Key="groupView" Source="{Binding Path=Persons}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Code"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<FlowDocumentScrollViewer>
<FlowDocument>
<flowdoc:ItemsContent ItemsSource="{Binding Source={StaticResource groupView}}">
....
</flowdoc:ItemsContent>
</FlowDocument>
</FlowDocumentScrollViewer>
</Grid>
The CollectionViewSource
element above sorts the Persons
list by code and determines the groups.
Furthermore, you must adapt the ItemsContent
class, because the one from the MSDN article does not yet support grouping. Here is a very simple example how you could achieve that:
private void GenerateContent(DataTemplate itemsPanel, DataTemplate itemTemplate, IEnumerable itemsSource)
{
Blocks.Clear();
if (itemTemplate != null && itemsSource != null)
{
FrameworkContentElement panel = null;
if (panel == null)
{
if (itemsPanel == null)
{
panel = this;
}
else
{
FrameworkContentElement p = Helpers.LoadDataTemplate(itemsPanel);
if (!(p is Block))
{
throw new Exception("ItemsPanel must be a block element");
}
Blocks.Add((Block)p);
panel = Attached.GetItemsHost(p);
if (panel == null)
{
throw new Exception("ItemsHost not found. Did you forget to specify Attached.IsItemsHost?");
}
}
}
// *** START NEW CODE ***
ICollectionView view = itemsSource as ICollectionView;
if (view != null)
{
foreach (object group in view.Groups)
{
GenerateContentForUngroupedItems(itemsPanel, itemTemplate, ((CollectionViewGroup)group).Items, panel);
if (panel is TableRowGroup)
{
TableRow row = new TableRow();
row.Cells.Add(new TableCell());
((TableRowGroup)panel).Rows.Add(row);
}
}
}
else
{
GenerateContentForUngroupedItems(itemsPanel, itemTemplate, itemsSource, panel);
}
// *** END NEW CODE ***
}
}
private void GenerateContentForUngroupedItems(DataTemplate itemsPanel, DataTemplate itemTemplate,
IEnumerable itemsSource, FrameworkContentElement panel)
{
foreach (object data in itemsSource)
{
FrameworkContentElement element = Helpers.LoadDataTemplate(itemTemplate);
element.DataContext = data;
Helpers.UnFixupDataContext(element);
if (panel is Section)
{
((Section) panel).Blocks.Add(Helpers.ConvertToBlock(data, element));
}
else if (panel is TableRowGroup)
{
((TableRowGroup) panel).Rows.Add((TableRow) element);
}
else
{
throw new Exception(String.Format("Don't know how to add an instance of {0} to an instance of {1}",
element.GetType(), panel.GetType()));
}
}
}
Most of the code above is from the original MSDN article; the only changes I made are these:
foreach
loop to the new method GenerateContentForUngroupedItems
. if (panel==null)
") outside the loop. NEW CODE
. What the modified code does is this: if the items source is not a collection view, it works exactly like the original MSDN code. If the items source is a collection view, two nested loops are called:
The inner loop effectively is the same as the original foreach loop from the MSDN article.
In the added code above, the empty table row is added by these lines:
TableRow row = new TableRow();
row.Cells.Add(new TableCell());
((TableRowGroup)panel).Rows.Add(row);
This code, of course, is not very generic. For a generic solution, add a new dependency property to the ItemsContent
control which contains a data template, just like the ItemsPanel
or ItemTemplate
properties. Then, you have the possibility to insert arbitrary text at the end of the group.
The result looks like this:
What you need is grouping. It stays within the MVVM rules.
Having said that, a quick google for grouping on flowdocuments didnt immediately show me anything. Maybe have a look around for flowdocument wpf grouping.
One solution may be to use a datagrid. you can definitely group on that with mvvm and make it look like the layout you have described above.
链接地址: http://www.djcxy.com/p/50468.html上一篇: 将WPF只读视图绑定到视图模型