Skip to content

Instantly share code, notes, and snippets.

@matelich
Last active August 29, 2015 14:16
Show Gist options
  • Save matelich/0319046157d6792caa3b to your computer and use it in GitHub Desktop.
Save matelich/0319046157d6792caa3b to your computer and use it in GitHub Desktop.
Extension of CustomDataGridDocumentPaginator.cs from http://www.codeproject.com/Articles/138233/Custom-Data-Grid-Document-Paginator
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Windows;
using System.Collections.ObjectModel;
using System.Collections;
using System.Windows.Markup;
using System.Windows.Data;
using System.ComponentModel;
namespace CustomDocumentPaginator
{
//Originally from http://www.codeproject.com/Articles/138233/Custom-Data-Grid-Document-Paginator
//Added comment from user 'Member 10789205'
//Updated with changes from http://khalidbudajaja.blogspot.com/2011/12/wpf-printing.html
//Reworked to support ListViews as well as GridViews
//Reworked to support variable height rows and text wrapping
//Could use some efficiency work, but its effective
/* Usage sample
PrintDialog printDialog = new PrintDialog();
printDialog.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
if (printDialog.ShowDialog() == false)
return;
string documentTitle = "Header Text";
Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
//styles for the printing are in the AppResourceDictionary.xaml
CustomDataGridDocumentPaginator paginator = new CustomDataGridDocumentPaginator(view, documentTitle, pageSize, new Thickness(30, 20, 30, 20), true, App.Current.Resources);
printDialog.PrintDocument(paginator, "Print Description - default filename for PDF printers");
*/
public class CustomDataGridDocumentPaginator : DocumentPaginator
{
#region Private Members
private ICollectionView _viewSource;
private List<double> _actualHeights;
private List<int> _startRows;
private List<DependencyObject> _columns;
private Collection<ColumnDefinition> _tableColumnDefinitions;
private double _availableHeight;
private int _pageCount;
#endregion
#region Constructor
public CustomDataGridDocumentPaginator(DataGrid documentSource, string documentTitle, Size pageSize, Thickness pageMargin, bool wrap_text, ResourceDictionary resources)
{
_tableColumnDefinitions = new Collection<ColumnDefinition>();
_viewSource = documentSource.Items;
_columns = documentSource.Columns.Select(c => (DependencyObject)c).ToList();
this.DocumentTitle = documentTitle;
this.PageSize = pageSize;
this.PageMargin = pageMargin;
this.PageDirection = documentSource.FlowDirection;
this.WrapText = wrap_text;
ReadStyles(resources);
if (_viewSource != null)
MeasureElements();
}
public CustomDataGridDocumentPaginator(ListView documentSource, string documentTitle, Size pageSize, Thickness pageMargin, bool wrap_text, ResourceDictionary resources)
{
_tableColumnDefinitions = new Collection<ColumnDefinition>();
_viewSource = documentSource.Items;
var view = documentSource.View as GridView;
if (view != null)
_columns = view.Columns.Select(c => (DependencyObject)c).ToList();
this.DocumentTitle = documentTitle;
this.PageSize = pageSize;
this.PageMargin = pageMargin;
this.PageDirection = documentSource.FlowDirection;
this.WrapText = wrap_text;
ReadStyles(resources);
if (view != null)
MeasureElements();
}
private void ReadStyles(ResourceDictionary resources)
{
if(resources != null)
{
this.DocumentHeaderTextStyle = (Style)resources["DocumentHeaderTextStyle"];
this.AlternatingRowBorderStyle = (Style)resources["AlternatingRowBorderStyle"];
this.DocumentFooterTextStyle = (Style)resources["DocumentFooterTextStyle"];
this.TableCellTextStyle = (Style)resources["TableCellTextStyle"];
this.TableHeaderTextStyle = (Style)resources["TableHeaderTextStyle"];
this.TableHeaderBorderStyle = (Style)resources["TableHeaderBorderStyle"];
this.GridContainerStyle = (Style)resources["GridContainerStyle"];
}
}
#endregion
#region Public Properties
#region Styling
//changing most of these requires a re-Measure(), if you make the set's non-private you'll want to address that
public Style AlternatingRowBorderStyle { get; private set; }
public Style DocumentHeaderTextStyle { get; private set; }
public Style DocumentFooterTextStyle { get; private set; }
public Style TableCellTextStyle { get; private set; }
public Style TableHeaderTextStyle { get; private set; }
public Style TableHeaderBorderStyle { get; private set; }
public Style GridContainerStyle { get; private set; }
#endregion
//changing most of these requires a re-Measure(), if you make the set's non-private you'll want to address that
public string DocumentTitle { get; private set; }
public Thickness PageMargin { get; private set; }
public FlowDirection PageDirection { get; private set; }
public override Size PageSize { get; set; }
public override bool IsPageCountValid { get { return true; } }
public override int PageCount { get { return _pageCount; } }
public override IDocumentPaginatorSource Source { get { return null; } }
public bool WrapText { get; private set; }
#endregion
#region Public Methods
public override DocumentPage GetPage(int pageNumber)
{
DocumentPage page = null;
List<object> itemsSource = new List<object>();
if (_viewSource != null)
{
foreach (object item in _viewSource)
itemsSource.Add(item);
}
if (itemsSource != null)
{
int rowIndex = 1;
int start = _startRows[pageNumber];
int end = _startRows.Count > pageNumber+1 ? _startRows[pageNumber + 1] : itemsSource.Count;
//Create a new grid
Grid tableGrid = CreateTable() as Grid;
AddGridRow(tableGrid, GridLength.Auto);
for (int index = start; index < end && index < itemsSource.Count; index++)
{
AddGridRow(tableGrid, GridLength.Auto);
if (rowIndex > 0)
{
object item = itemsSource[index];
int columnIndex = 0;
foreach (var column in _columns)
{
if (AddTableCell(tableGrid, column, item, columnIndex, rowIndex))
columnIndex++;
}
if (this.AlternatingRowBorderStyle != null && rowIndex % 2 == 0)
{
Border alernatingRowBorder = new Border();
alernatingRowBorder.Style = this.AlternatingRowBorderStyle;
alernatingRowBorder.SetValue(Grid.RowProperty, rowIndex);
alernatingRowBorder.SetValue(Grid.ColumnSpanProperty, columnIndex);
alernatingRowBorder.SetValue(Grid.ZIndexProperty, -1);
tableGrid.Children.Add(alernatingRowBorder);
}
}
rowIndex++;
}
page = ConstructPage(tableGrid, pageNumber);
}
return page;
}
#endregion
#region Private Methods
/// <summary>
/// This function measures the heights of the page header, page footer and grid header and the first row in the grid
/// in order to work out how manage pages might be required.
/// </summary>
private void MeasureElements()
{
double allocatedSpace = 0;
//Measure the page header
ContentControl pageHeader = new ContentControl();
pageHeader.Content = CreateDocumentHeader();
allocatedSpace = MeasureHeight(pageHeader);
//Measure the page footer
ContentControl pageFooter = new ContentControl();
pageFooter.Content = CreateDocumentFooter(0);
allocatedSpace += MeasureHeight(pageFooter);
//Measure the table header
ContentControl tableHeader = new ContentControl();
tableHeader.Content = CreateTable();
allocatedSpace += MeasureHeight(tableHeader);
//Include any margins
allocatedSpace += this.PageMargin.Bottom + this.PageMargin.Top;
//Work out how much space we need to display the grid
_availableHeight = this.PageSize.Height - allocatedSpace;
//Calculate the height of the first row
_actualHeights = new List<double>();
_startRows = new List<int>();
_startRows.Add(0);
double height_so_far = 0;
foreach (object item in _viewSource)
{
double h = MeasureHeight(CreateTempRow(item));
if (height_so_far + h >= _availableHeight)
{
//new page
height_so_far = h;
_startRows.Add(_actualHeights.Count);
}
else
{
height_so_far += h;
}
_actualHeights.Add(h);
}
//Count the rows in the document source
double rowCount = _actualHeights.Count;
//Calculate the number of pages that we will need
if (rowCount > 0)
_pageCount = _startRows.Count;
}
/// <summary>
/// This method constructs the document page (visual) to print
/// </summary>
private DocumentPage ConstructPage(Grid content, int pageNumber)
{
if (content == null)
return null;
//Build the page inc header and footer
Grid pageGrid = new Grid();
//Header row
AddGridRow(pageGrid, GridLength.Auto);
//Content row
AddGridRow(pageGrid, new GridLength(1.0d, GridUnitType.Star));
//Footer row
AddGridRow(pageGrid, GridLength.Auto);
ContentControl pageHeader = new ContentControl();
pageHeader.Content = this.CreateDocumentHeader();
pageGrid.Children.Add(pageHeader);
if (content != null)
{
content.SetValue(Grid.RowProperty, 1);
pageGrid.Children.Add(content);
}
ContentControl pageFooter = new ContentControl();
pageFooter.Content = CreateDocumentFooter(pageNumber + 1);
pageFooter.SetValue(Grid.RowProperty, 2);
pageFooter.FlowDirection = PageDirection;
pageGrid.Children.Add(pageFooter);
double width = this.PageSize.Width - (this.PageMargin.Left + this.PageMargin.Right);
double height = this.PageSize.Height - (this.PageMargin.Top + this.PageMargin.Bottom);
pageGrid.Measure(new Size(width, height));
pageGrid.Arrange(new Rect(this.PageMargin.Left, this.PageMargin.Top, width, height));
return new DocumentPage(pageGrid, PageSize, new Rect(content.DesiredSize), new Rect(content.DesiredSize));
}
/// <summary>
/// Creates a default header for the document; containing the doc title
/// </summary>
private object CreateDocumentHeader()
{
Border headerBorder = new Border();
TextBlock titleText = new TextBlock();
titleText.Style = this.DocumentHeaderTextStyle;
if (WrapText)
titleText.TextWrapping = TextWrapping.Wrap;
else
titleText.TextTrimming = TextTrimming.CharacterEllipsis;
titleText.Text = this.DocumentTitle;
titleText.HorizontalAlignment = HorizontalAlignment.Center;
titleText.TextAlignment = TextAlignment.Center;
headerBorder.Child = titleText;
headerBorder.FlowDirection = PageDirection;
return headerBorder;
}
/// <summary>
/// Creates a default page footer consisting of datetime and page number
/// </summary>
private object CreateDocumentFooter(int pageNumber)
{
Grid footerGrid = new Grid();
footerGrid.Margin = new Thickness(0, 10, 0, 0);
ColumnDefinition colDefinition = new ColumnDefinition();
colDefinition.Width = new GridLength(0.5d, GridUnitType.Star);
TextBlock dateTimeText = new TextBlock();
dateTimeText.Style = this.DocumentFooterTextStyle;
switch (PageDirection)
{
case FlowDirection.LeftToRight:
dateTimeText.Text = DateTime.Now.ToString("dd-MMM-yyy HH:mm");
break;
case FlowDirection.RightToLeft:
dateTimeText.Text = DateTime.Now.ToString("yyyy-MM-dd");
break;
}
dateTimeText.FlowDirection = PageDirection;
footerGrid.Children.Add(dateTimeText);
TextBlock pageNumberText = new TextBlock();
pageNumberText.Style = this.DocumentFooterTextStyle;
pageNumberText.Text = "Page " + pageNumber.ToString() + " of " + this.PageCount.ToString();
pageNumberText.SetValue(Grid.ColumnProperty, 1);
pageNumberText.HorizontalAlignment = HorizontalAlignment.Right;
pageNumberText.FlowDirection = PageDirection;
footerGrid.Children.Add(pageNumberText);
return footerGrid;
}
/// <summary>
/// The following function creates a temp table with a single row so that it can be measured and used to
/// calculate the total number of req'd pages
/// </summary>
/// <returns></returns>
private Grid CreateTempRow(object item)
{
Grid tableRow = new Grid();
tableRow.Style = this.GridContainerStyle;
if (_viewSource != null)
{
foreach (ColumnDefinition colDefinition in _tableColumnDefinitions)
{
ColumnDefinition copy = XamlReader.Parse(XamlWriter.Save(colDefinition)) as ColumnDefinition;
tableRow.ColumnDefinitions.Add(copy);
}
int columnIndex = 0;
if (_columns != null)
foreach (var column in _columns)
if (AddTableCell(tableRow, column, item, columnIndex, 0))
columnIndex++;
}
return tableRow;
}
/// <summary>
/// This function counts the number of rows in the document
/// </summary>
private object CreateTable()
{
if (_viewSource == null)
return null;
Grid table = new Grid();
table.Style = this.GridContainerStyle;
int columnIndex = 0;
if (_columns != null)
{
foreach (var column in _columns)
{
if (AddTableColumn(table, columnIndex, column))
columnIndex++;
}
}
if (this.TableHeaderBorderStyle != null)
{
Border headerBackground = new Border();
headerBackground.Style = this.TableHeaderBorderStyle;
headerBackground.SetValue(Grid.ColumnSpanProperty, columnIndex);
headerBackground.SetValue(Grid.ZIndexProperty, -1);
table.Children.Add(headerBackground);
}
return table;
}
/// <summary>
/// Measures the height of an element
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private double MeasureHeight(FrameworkElement element)
{
if (element == null)
throw new ArgumentNullException("element");
double width = this.PageSize.Width - (this.PageMargin.Left + this.PageMargin.Right);
double height = this.PageSize.Height - (this.PageMargin.Top + this.PageMargin.Bottom);
element.Measure(new Size(width, height));
return element.DesiredSize.Height;
}
/// <summary>
/// Adds a column to a grid
/// </summary>
/// <param name="grid">Grid to add the column to</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="column">Source column defintition which will be used to calculate the width of the column</param>
private bool AddTableColumn(Grid grid, int columnIndex, DependencyObject column)
{
double proportion = 0;
string header_text;
if (column is DataGridColumn)
{
var datagridcolumn = column as DataGridColumn;
if (datagridcolumn.Visibility != Visibility.Visible)
return false;
proportion = datagridcolumn.ActualWidth / (this.PageSize.Width - (this.PageMargin.Left + this.PageMargin.Right));
header_text = datagridcolumn.Header.ToString();
}
else if (column is GridViewColumn)
{
var gridviewcolumn = column as GridViewColumn;
if (gridviewcolumn.ActualWidth < 2)
return false;
proportion = gridviewcolumn.ActualWidth / (this.PageSize.Width - (this.PageMargin.Left + this.PageMargin.Right));
header_text = gridviewcolumn.Header.ToString();
}
else
return false;
ColumnDefinition colDefinition = new ColumnDefinition();
colDefinition.Width = new GridLength(proportion, GridUnitType.Star);
grid.ColumnDefinitions.Add(colDefinition);
TextBlock text = new TextBlock();
text.Style = this.TableHeaderTextStyle;
if (WrapText)
text.TextWrapping = TextWrapping.Wrap;
else
text.TextTrimming = TextTrimming.CharacterEllipsis;
text.Text = header_text;
text.SetValue(Grid.ColumnProperty, columnIndex);
grid.Children.Add(text);
_tableColumnDefinitions.Add(colDefinition);
return true;
}
/// <summary>
/// Adds a cell to a grid
/// </summary>
/// <param name="grid">Grid to add teh cell to</param>
/// <param name="column">Source column definition which contains binding info</param>
/// <param name="item">The binding source</param>
/// <param name="columnIndex">Column index</param>
/// <param name="rowIndex">Row index</param>
private bool AddTableCell(Grid grid, DependencyObject column, object item, int columnIndex, int rowIndex)
{
if (column is DataGridColumn)
{
var datagridcolumn = column as DataGridColumn;
if (datagridcolumn.Visibility != Visibility.Visible)
return false;
if (column is DataGridTemplateColumn)
{
DataGridTemplateColumn templateColumn = column as DataGridTemplateColumn;
ContentControl contentControl = new ContentControl();
contentControl.Focusable = true;
contentControl.ContentTemplate = templateColumn.CellTemplate;
contentControl.Content = item;
contentControl.SetValue(Grid.ColumnProperty, columnIndex);
contentControl.SetValue(Grid.RowProperty, rowIndex);
grid.Children.Add(contentControl);
return true;
}
else if (column is DataGridTextColumn)
{
DataGridTextColumn textColumn = column as DataGridTextColumn;
TextBlock text = new TextBlock { Text = "Text" };
text.Style = this.TableCellTextStyle;
if (WrapText)
text.TextWrapping = TextWrapping.Wrap;
else
text.TextTrimming = TextTrimming.CharacterEllipsis;
text.DataContext = item;
Binding binding = textColumn.Binding as Binding;
//if (!string.IsNullOrEmpty(column.DisplayFormat))
//binding.StringFormat = column.DisplayFormat;
text.SetBinding(TextBlock.TextProperty, binding);
text.SetValue(Grid.ColumnProperty, columnIndex);
text.SetValue(Grid.RowProperty, rowIndex);
grid.Children.Add(text);
return true;
}
}
if (column is GridViewColumn)
{
var gridviewcolumn = column as GridViewColumn;
if (gridviewcolumn.ActualWidth < 2)
return false;
if (gridviewcolumn.CellTemplate != null)
{
ContentControl contentControl = new ContentControl();
contentControl.Focusable = true;
contentControl.ContentTemplate = gridviewcolumn.CellTemplate;
contentControl.Content = item;
contentControl.SetValue(Grid.ColumnProperty, columnIndex);
contentControl.SetValue(Grid.RowProperty, rowIndex);
grid.Children.Add(contentControl);
return true;
}
else //if (column is DataGridTextColumn)
{
//DataGridTextColumn textColumn = column as DataGridTextColumn;
TextBlock text = new TextBlock { Text = "Text" };
text.Style = this.TableCellTextStyle;
if (WrapText)
text.TextWrapping = TextWrapping.Wrap;
else
text.TextTrimming = TextTrimming.CharacterEllipsis;
text.DataContext = item;
Binding binding = gridviewcolumn.DisplayMemberBinding as Binding;
//if (!string.IsNullOrEmpty(column.DisplayFormat))
//binding.StringFormat = column.DisplayFormat;
text.SetBinding(TextBlock.TextProperty, binding);
text.SetValue(Grid.ColumnProperty, columnIndex);
text.SetValue(Grid.RowProperty, rowIndex);
grid.Children.Add(text);
return true;
}
}
return false;
}
/// <summary>
/// Adds a row to a grid
/// </summary>
private void AddGridRow(Grid grid, GridLength rowHeight)
{
if (grid == null)
return;
RowDefinition rowDef = new RowDefinition();
if (rowHeight != null)
rowDef.Height = rowHeight;
grid.RowDefinitions.Add(rowDef);
}
#endregion
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AAConfigUi"
xmlns:ms="http://namespaces.mindscape.co.nz/wpf"
>
<!-- styles for printing -->
<Style x:Key="DocumentHeaderTextStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontFamily" Value="Aria"/>
<Setter Property="FontSize" Value="30"/>
</Style>
<Style x:Key="AlternatingRowBorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="#FFFFFFFF"/>
<Setter Property="Background" Value="#FFFFFFFF" />
</Style>
<Style x:Key="DocumentFooterTextStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontFamily" Value="Times New Roman"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<Style x:Key="TableCellTextStyle" TargetType="TextBlock">
<Setter Property="Padding" Value="5,0,0,0" />
</Style>
<Style x:Key="TableHeaderTextStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="Black" />
<Setter Property="FontFamily" Value="Times New Roman"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style x:Key="TableHeaderBorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Background" Value="Black" />
</Style>
<Style x:Key="GridContainerStyle" TargetType="Grid">
<Setter Property="Background" Value="#FFFCFCFC"/>
</Style>
</ResourceDictionary>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment