I'm having problems formatting a richTextBox for printing.
I've created a user control containing a richtextbox and have pre-defined paragraphs that I use to populate data in the code behind from a POCO object that I load in another page and pass into the user control via a property.
I'm then using a similar methodology to this article: http://www.silverlightshow.net/items/Advanced-printing-in-Silverlight-4.aspx to paginate the data and pass it to the printpagecontrol
public void Print()
{
_printLineIndex = 0;
_printBlockIndex = 0;
_printPageCount = 0;
_printDocumentBodyLines = new List<string>();
LayoutRoot.DataContext = PrintRequestData;
//requestDetailGrid.DataContext = PrintRequestData;
string printInfoText = "Chaos Request Form No: " + RequestID;
setTextboxData();
PrintDocument printDocument = new PrintDocument();
printDocument.BeginPrint += new EventHandler<BeginPrintEventArgs>(printDocument_BeginPrint);
printDocument.EndPrint += new EventHandler<EndPrintEventArgs>(printDocument_EndPrint);
printDocument.PrintPage += new EventHandler<PrintPageEventArgs>(printDocument_PrintPage);
printDocument.Print(printInfoText);
}
void printDocument_EndPrint(object sender, EndPrintEventArgs e)
{
busyIndicator.IsBusy = false;
}
void printDocument_BeginPrint(object sender, BeginPrintEventArgs e)
{
busyIndicator.IsBusy = true;
}
void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
PrintPageControl page = new PrintPageControl();
//increment the page count
_printPageCount++;
Paragraph p;
Run r;
int lineCount = 0;
int blockCount = 0;
string headerText = "Chaos CIP Request";
string footerText = "Page " + _printPageCount;
page.SetHeaderAndFooterText(headerText, footerText);
//e.PageVisual = LayoutRoot;
int numberOfLinesAdded = 0;
int numberOfBlocksAdded = 0;
foreach (Block b in printRTB.Blocks)
{
if (b is Paragraph)
{
if (blockCount>=_printBlockIndex )
{
numberOfLinesAdded = 0;
p = new Paragraph();
p = b as Paragraph;
foreach (Run run in p.Inlines)
{
if (lineCount >= _printLineIndex)
{
r = new Run();
r.Text = run.Text;
r.TextDecorations = run.TextDecorations;
r.FontWeight = run.FontWeight;
page.AddLine(r);
page.Measure(new Size(e.PrintableArea.Width, double.PositiveInfinity));
if (page.DesiredSize.Height > e.PrintableArea.Height
&& numberOfLinesAdded > 0)
{
page.RemoveLastLine();
e.HasMorePages = true;
_printLineIndex = numberOfLinesAdded;
_printBlockIndex += numberOfBlocksAdded;
break;
}
numberOfLinesAdded++;
}
lineCount++;
}
if (e.HasMorePages)
break;
numberOfBlocksAdded++;
}
blockCount++;
}
}
e.PageVisual = page;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace ChaosApplication.Controls
{
public partial class PrintPageControl : UserControl
{
public PrintPageControl()
{
InitializeComponent();
}
public void SetHeaderAndFooterText(string header, string footer)
{
headerTextBlock.Text = header;
footerTextBlock.Text = footer;
}
public void AddLine(Run r)
{
Paragraph p = new Paragraph();
p.Inlines.Add(r);
bodyRichTextBox.Blocks.Add(p);
}
public void AddLine(Span s)
{
Paragraph p = new Paragraph();
p.Inlines.Add(s);
bodyRichTextBox.Blocks.Add(p);
}
public void RemoveLastLine()
{
BlockCollection bc = bodyRichTextBox.Blocks;
//get the last block
Block b = bc.ElementAt(bodyRichTextBox.Blocks.Count - 1);
Paragraph p = b as Paragraph;
p.Inlines.RemoveAt(p.Inlines.Count - 1);
}
}
}
This all works however the pages are determined based on paragraphs, rather than lines of text within paragraphs and the formatting is poor.
What I would like to achieve is to have the field titles in one column and the values in another column and nicely paginated based on lines of text rather than paragraphs. I initially tried using a grid but couldn't work out how to paginate it.
s_aus_1
0 Points
4 Posts
RichTextBox Paginating and Printing
Oct 19, 2010 11:18 PM | LINK
Hi,
I'm having problems formatting a richTextBox for printing.
I've created a user control containing a richtextbox and have pre-defined paragraphs that I use to populate data in the code behind from a POCO object that I load in another page and pass into the user control via a property.
Here is the user control:
<UserControl x:Class="ChaosApplication.Controls.RequestPrintControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" xmlns:ChaosServices="clr-namespace:ChaosApplication.Web.Services" xmlns:ChaosDataModels="clr-namespace:ChaosApplication.Web.DataModels" xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" mc:Ignorable="d"> <toolkit:BusyIndicator x:Name="busyIndicator"> <Grid x:Name="LayoutRoot" Background="White"> <RichTextBox x:Name="printRTB" Grid.Row="2" Grid.Column="1" Margin="0"> <Paragraph x:Name="idParagraph"> <Run Text="Request ID " FontWeight="Bold"/><Run x:Name="idRun" /> </Paragraph> <Paragraph> <Run Text="Request Date: " FontWeight="Bold"/><Run x:Name="dateRun" /> </Paragraph> <Paragraph> <Run Text="Title: " FontWeight="Bold"/><Run x:Name="titleRun" /> </Paragraph> <Paragraph> <Run Text="Author: " FontWeight="Bold"/> <Run x:Name="authorRun" /> </Paragraph> <Paragraph> <Run Text="CIP Group: " FontWeight="Bold"/><Run x:Name="groupRun"/> </Paragraph> <Paragraph> <Run Text="Functional Area: " FontWeight="Bold"/><Run x:Name="areaRun" /> </Paragraph> <Paragraph> <Run Text="Business Unit: " FontWeight="Bold"/><Run x:Name="buRun" /> </Paragraph> <Paragraph> <Run Text="Priority: " FontWeight="Bold"/><Run x:Name="priorityRun" /> </Paragraph> <Paragraph> <Run Text="Status: " FontWeight="Bold"/><Run x:Name="statusRun"/> </Paragraph> <Paragraph> <Run Text="Requirements: " FontWeight="Bold"/><Run x:Name="requirementsRun" /> </Paragraph> <Paragraph> <Run Text="Summary Description: " FontWeight="Bold"/><Run x:Name="summaryRun"/> </Paragraph> <Paragraph> <Run Text="Benefits: " FontWeight="Bold"/><Run x:Name="benefitsRun" /> </Paragraph> <Paragraph> <Run Text="Objectives Description: " FontWeight="Bold"/><Run x:Name="objectivesRun" /> </Paragraph> </RichTextBox> </Grid> </toolkit:BusyIndicator> </UserControl>in my code behind I call a function to populate the date from my class as follows:
private void setTextboxData() { idRun.Text = PrintRequestData.RequestID.ToString(); dateRun.Text = PrintRequestData.RequestDate.ToString(); titleRun.Text = PrintRequestData.Title; authorRun.Text = PrintRequestData.Author; groupRun.Text = PrintRequestData.CIPGroupDesc; areaRun.Text = PrintRequestData.AreaName; buRun.Text = PrintRequestData.BusinessUnitDesc; priorityRun.Text = PrintRequestData.PriorityDesc; statusRun.Text = PrintRequestData.StatusDesc; requirementsRun.Text = PrintRequestData.RequirementDesc; summaryRun.Text = PrintRequestData.SummaryDesc; benefitsRun.Text = PrintRequestData.BenefitsDesc; objectivesRun.Text = PrintRequestData.ObjectivesDesc; }I'm then using a similar methodology to this article: http://www.silverlightshow.net/items/Advanced-printing-in-Silverlight-4.aspx to paginate the data and pass it to the printpagecontrol
public void Print() { _printLineIndex = 0; _printBlockIndex = 0; _printPageCount = 0; _printDocumentBodyLines = new List<string>(); LayoutRoot.DataContext = PrintRequestData; //requestDetailGrid.DataContext = PrintRequestData; string printInfoText = "Chaos Request Form No: " + RequestID; setTextboxData(); PrintDocument printDocument = new PrintDocument(); printDocument.BeginPrint += new EventHandler<BeginPrintEventArgs>(printDocument_BeginPrint); printDocument.EndPrint += new EventHandler<EndPrintEventArgs>(printDocument_EndPrint); printDocument.PrintPage += new EventHandler<PrintPageEventArgs>(printDocument_PrintPage); printDocument.Print(printInfoText); } void printDocument_EndPrint(object sender, EndPrintEventArgs e) { busyIndicator.IsBusy = false; } void printDocument_BeginPrint(object sender, BeginPrintEventArgs e) { busyIndicator.IsBusy = true; } void printDocument_PrintPage(object sender, PrintPageEventArgs e) { PrintPageControl page = new PrintPageControl(); //increment the page count _printPageCount++; Paragraph p; Run r; int lineCount = 0; int blockCount = 0; string headerText = "Chaos CIP Request"; string footerText = "Page " + _printPageCount; page.SetHeaderAndFooterText(headerText, footerText); //e.PageVisual = LayoutRoot; int numberOfLinesAdded = 0; int numberOfBlocksAdded = 0; foreach (Block b in printRTB.Blocks) { if (b is Paragraph) { if (blockCount>=_printBlockIndex ) { numberOfLinesAdded = 0; p = new Paragraph(); p = b as Paragraph; foreach (Run run in p.Inlines) { if (lineCount >= _printLineIndex) { r = new Run(); r.Text = run.Text; r.TextDecorations = run.TextDecorations; r.FontWeight = run.FontWeight; page.AddLine(r); page.Measure(new Size(e.PrintableArea.Width, double.PositiveInfinity)); if (page.DesiredSize.Height > e.PrintableArea.Height && numberOfLinesAdded > 0) { page.RemoveLastLine(); e.HasMorePages = true; _printLineIndex = numberOfLinesAdded; _printBlockIndex += numberOfBlocksAdded; break; } numberOfLinesAdded++; } lineCount++; } if (e.HasMorePages) break; numberOfBlocksAdded++; } blockCount++; } } e.PageVisual = page; }Print() is called from a button click.
the printpagecontrol looks like this: (XAML)
<UserControl x:Class="ChaosApplication.Controls.PrintPageControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid x:Name="LayoutRoot" Background="White"> <Grid x:Name="documentRoot"> <Grid.ColumnDefinitions> <ColumnDefinition x:Name="leftMargin" Width="20"/> <ColumnDefinition/> <ColumnDefinition x:Name="rightMargin" Width="20"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition x:Name="topMargin" Height="20"/> <RowDefinition x:Name="headerRow" Height="25"/> <RowDefinition /> <RowDefinition x:Name="footerRow" Height="25"/> <RowDefinition x:Name="bottomMargin" Height="20"/> </Grid.RowDefinitions> <TextBlock x:Name="headerTextBlock" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" FontFamily="Arial" FontSize="20" /> <RichTextBox x:Name="bodyRichTextBox" Grid.Row="2" Grid.Column="1" TextWrapping="Wrap" /> <TextBlock x:Name="footerTextBlock" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Center"/> </Grid> </Grid> </UserControl>PrintpageControl (Code Behind):
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace ChaosApplication.Controls { public partial class PrintPageControl : UserControl { public PrintPageControl() { InitializeComponent(); } public void SetHeaderAndFooterText(string header, string footer) { headerTextBlock.Text = header; footerTextBlock.Text = footer; } public void AddLine(Run r) { Paragraph p = new Paragraph(); p.Inlines.Add(r); bodyRichTextBox.Blocks.Add(p); } public void AddLine(Span s) { Paragraph p = new Paragraph(); p.Inlines.Add(s); bodyRichTextBox.Blocks.Add(p); } public void RemoveLastLine() { BlockCollection bc = bodyRichTextBox.Blocks; //get the last block Block b = bc.ElementAt(bodyRichTextBox.Blocks.Count - 1); Paragraph p = b as Paragraph; p.Inlines.RemoveAt(p.Inlines.Count - 1); } } }This all works however the pages are determined based on paragraphs, rather than lines of text within paragraphs and the formatting is poor.
What I would like to achieve is to have the field titles in one column and the values in another column and nicely paginated based on lines of text rather than paragraphs. I initially tried using a grid but couldn't work out how to paginate it.
Any suggestions would be greatlly appreciated.
thanks
kmcsql
Member
2 Points
1 Post
Re: RichTextBox Paginating and Printing
Apr 21, 2011 06:30 PM | LINK
You are on the right path
Set up your DocumetBodylines as List<string> ... fr each paragraph and each run .. add the run.Text to the string list
Create a DocumentFontWeight List<FontWeight> .. and add the fontweight from run at the same time as you add the string.
You can do this for each font and the text decoration as well.
Also, change back to a TextBlock in the PrintPage Usercontrol for the body
Samples:
DocumentBodyLines =
new List<string
>();
DocumentFontWeight =
new List<FontWeight
>();
int
blockCount = 0;
int
lineCount = 0;
foreach (Block b in
rtb.Blocks)
{
if (b is Paragraph
)
{
Paragraph p = new Paragraph
();
p = b
as Paragraph
;
foreach (Run run in
p.Inlines)
{
DocumentBodyLines.Add(run.Text);
DocumentFontWeight.Add(run.FontWeight);
lineCount++;
}
blockCount++;
}
}
The Addline in the PrintPage Usercontrol:
public void AddLine(string line, FontWeight
weight )
{
bodyTextBlock.Inlines.Add(line);
int
oIndex = bodyTextBlock.Inlines.Count - 1;
bodyTextBlock.Inlines[oIndex].FontWeight = weight;
bodyTextBlock.Inlines.Add(
new LineBreak
());
}
And the print page handler
private void printDocument_PrintPage(object sender, PrintPageEventArgs
e)
{
PEN_SL_C_MANAGER.Controls.
PrintPage page = new PEN_SL_C_MANAGER.Controls.PrintPage
();
page.SetHeaderAndFooterText(DocumentTitle,
"Date"
);
int
numberOfLinesAdded = 0;
while
(LineIndex < DocumentBodyLines.Count)
{
page.AddLine(DocumentBodyLines[LineIndex], DocumentFontWeight[LineIndex]);
page.Measure(
new Size(e.PrintableArea.Width, double
.PositiveInfinity));
if
(page.DesiredSize.Height > e.PrintableArea.Height && numberOfLinesAdded > 1)
{
page.RemoveLastLine();
e.HasMorePages =
true
;
break
;
}
LineIndex++;
numberOfLinesAdded++;
}
e.PageVisual = page;
}