I'm trying to print a WPF FlowDocument. The layout needs to be in the form of 4 documents per page, laid out as follows:
Doc1 | Doc2
-------------
Doc3 | Doc4
(Sorry, I couldn't come up with a better way of illustrating the layout).
The page needs to fill, so if Doc1 & 2 are blank or just one or two characters, it still needs to print them the same size as Doc3 & 4.
The code I'm using is as follows (sorry it's long, I've tried to abridge where feasible):
PrintDialog printDialog = new PrintDialog();
if ((bool)printDialog.ShowDialog().GetValueOrDefault())
{
FlowDocument flowDocument = new FlowDocument();
flowDocument.PageHeight = printDialog.PrintableAreaHeight;
flowDocument.PageWidth = printDialog.PrintableAreaWidth;
flowDocument.PagePadding = new Thickness(25);
flowDocument.ColumnGap = 0;
flowDocument.ColumnWidth = (flowDocument.PageWidth -
flowDocument.ColumnGap -
flowDocument.PagePadding.Left -
flowDocument.PagePadding.Right);
Table myTable = new Table();
myTable.BorderThickness = new Thickness(3);
AddCols(myTable); // Add 2 cols
TableRowGroup rg = new TableRowGroup();
TableRow row = new TableRow();
AddRows(myTable); // Adds 2 rows
TableCell cell = new TableCell(new Paragraph(new Run("Doc1")));
cell.BorderThickness = new Thickness(1);
cell.BorderBrush = Brushes.Black;
// Repeat 4 times
row.Cells.Add(cell);
myTable.RowGroups.Add(rg);
doc.Blocks.Add(myTable);
....
The problem that I have is that although this does print, it doesn't try to fit it to the page as described above. Is what I am attempting possible and if so, how?
EDIT:
From looking here I believe what I actually need is a way to calculate the height of the paragraph, so that I can set the Padding property. Unfortunately the solution proposed in this link doesn't work!
Try placing the entire block in a grid so as to give it the uniform layout and then place the grid in the block and block inside your single table cell. See if this works for you -
Grid grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions.Add(new RowDefinition());
grid.ColumnDefinitions.Add(new ColumnDefinition());
grid.ColumnDefinitions.Add(new ColumnDefinition());
Label text1 = new Label();
text1.Content = "Doc1";
grid.Children.Add(text1);
Grid.SetColumn(text1, 0);
Grid.SetRow(text1, 0);
Label text2 = new Label();
text1.Content = "Doc2";
grid.Children.Add(text2);
Grid.SetColumn(text2, 1);
Grid.SetRow(text2, 0);
Label text3 = new Label();
text1.Content = "Doc3";
grid.Children.Add(text3);
Grid.SetColumn(text3, 0);
Grid.SetRow(text3, 1);
Label text4 = new Label();
text1.Content = "Doc4";
grid.Children.Add(text4);
Grid.SetColumn(text4, 1);
Grid.SetRow(text4, 1);
BlockUIContainer block = new BlockUIContainer(grid);
Table table = new Table();
TableRowGroup rg = new TableRowGroup();
TableCell cell = new TableCell();
cell.Blocks.Add(block);
TableRow row = new TableRow();
row.Cells.Add(cell);
rg.Rows.Add(row);
table.RowGroups.Add(rg);
doc.Blocks.Add(table);
You could create your own custom DocumentPaginator. See here:
http://www.codeproject.com/Articles/164033/WPF-Visual-Print-Component
http://www.codeproject.com/Articles/31834/FlowDocument-pagination-with-repeating-page-header
http://www.switchonthecode.com/tutorials/wpf-printing-part-2-pagination
http://www.codeproject.com/Articles/138233/Custom-Data-Grid-Document-Paginator
Is this what you're looking for?
Convert xaml flow document to xps
question, you added the cell to the row.cell and the rowgroup to the table, but did you add the row to the rowgroup?
Related
I'm working on a WPF App, and I would like to create the entire window from c# code, instead of from XML.
I tried the following code, but nothing happens, the grid is not displayed. Did I miss something? Is it possible to do it this way or is there any other solution?
public MainWindow()
{
Grid grd = new Grid();
grd.Margin = new System.Windows.Thickness(10, 10, 10, 0);
grd.Background = new SolidColorBrush(Colors.White);
grd.Height = 104;
grd.VerticalAlignment = System.Windows.VerticalAlignment.Top;
grd.ColumnDefinitions.Add(new ColumnDefinition());
grd.ColumnDefinitions.Add(new ColumnDefinition());
grd.ColumnDefinitions.Add(new ColumnDefinition());
RowDefinition row = new RowDefinition();
row.Height = new System.Windows.GridLength(45);
grd.RowDefinitions.Add(row);
row = new RowDefinition();
row.Height = new System.Windows.GridLength(45);
grd.RowDefinitions.Add(row);
row = new RowDefinition();
row.Height = new System.Windows.GridLength(45);
grd.RowDefinitions.Add(row);
InitializeComponent();
}
Grid grd was created, but not added to Window.
InitializeComponent();
this.Content = grd;
it will replace all content which was declared in XAML (if any).
However, Grid is a Panel and doesn' have visual representation itself, so window with Grid without child element will still look empty. Try grd.ShowGridLines = true; to see rows and columns
Grid documentation shows a large example actually, with equivalent c# code and xaml markup
I am trying to add a row in middle of table layout panel. However I am struggling to find a way to do this. I tried following from one of the article on internet but this doesn't seems to be working. Can you help.
i have 3 columns and 5 rows in table layout panel. each row containing lable, textbox and blank lable.
i am trying to add a row after 2nd row in already created table layout panel.
var AddOnControl = ConfirmationTable.Controls.Find("Discount", true).First();
int childIndex = 1+ ConfirmationTable.Controls.GetChildIndex(AddOnControl);
Label lbl = new Label();
lbl.Name = key;
lbl.Text = key;
lbl.Font = new Font("Calibri", 10F, System.Drawing.FontStyle.Regular);
lbl.Size = new Size(lbl.Size.Width + 70, lbl.Size.Height);
ConfirmationTable.Controls.Add(lbl);
TextBox txt = new TextBox();
txt.Multiline = true;
txt.TextChanged += txt_TextChanged;
txt.Name = "txtPrice" + key;
txt.Text = value;
txt.BorderStyle = BorderStyle.None;
ConfirmationTable.Controls.Add(txt);
lbl = new Label();
lbl.Name = "blank";
ConfirmationTable.Controls.Add(lbl);
ConfirmationTable.Controls.SetChildIndex(lbl, childIndex);
ConfirmationTable.Controls.SetChildIndex(txt, childIndex + 1);
ConfirmationTable.Controls.SetChildIndex(txt, childIndex + 2);
but above code always adds a row at the bottom of the table. Any suggestion?
There is an overload of the Controls.Add method that accepts two integers as indexes for column/row position.
Like this:
tableLayoutPanel1.Controls.Add(lb3, 0, 1);
I believe this is enough to solve your problem in a simple way.
Is there a way to add a border around the table and hide the cell borders in MigraDoc?
The default width of the borders is 0 and borders are not visible. To enable borders, set a value greater than 0.
If table is your Table object, you could write table.Borders.Width = 0.5;
You can set borders for the table and for each cell. Cells inherit border properties from the table, the column, the row unless they are overwritten at a lower stage.
Also check the SetEdge method of the Table class.
Sample code discussed here:
http://www.pdfsharp.net/wiki/Invoice-sample.ashx
My test code:
private static void TabelWithBorderTest()
{
var document = new Document();
// Add a section to the document.
var section = document.AddSection();
Table table = section.AddTable();
table.Borders.Width = 0.25;
table.Rows.LeftIndent = 0;
// Before you can add a row, you must define the columns
Column column = table.AddColumn("7cm");
column.Format.Alignment = ParagraphAlignment.Left;
Row row = table.AddRow();
row.Cells[0].AddParagraph("Text in table");
// Create a renderer for the MigraDoc document.
var pdfRenderer = new PdfDocumentRenderer(false) { Document = document };
// Associate the MigraDoc document with a renderer.
// Layout and render document to PDF.
pdfRenderer.RenderDocument();
// Save the document...
const string filename = "TableTest.pdf";
pdfRenderer.PdfDocument.Save(filename);
// ...and start a viewer.
Process.Start(filename);
}
I managed to get this down by setting each row borders visibility as false;
var document = new Document();
var page = document.AddSection();
Table table = page.AddTable();
table.Borders.Visible = true;
Column col = table.AddColumn("3cm");
col = table.AddColumn("10cm");
col = table.AddColumn("3cm");
col.Format.Alignment = ParagraphAlignment.Left;
Row row = table.AddRow();
Paragraph p = row.Cells[0].AddParagraph();
p.AddFormattedText("Top header row");
row.Cells[0].MergeRight = 2;
// then set it in visible as false like this, you can do top, left and right as well
row.Cells[0].Borders.Bottom.Visible = false;
Doesn't look nice but if anyone has a better solution do post it up
My question i asked for "Randomly" adding controls in spefic
Table Cells. e.g.
if i call funtion CreateTable() , it should create tables
with vary number of rows and columns, and then for example if i
want a text box in Cell(0,1), a dropdown in cell(2,1) then again a text
area control in Cell(5,1) etc etc.( u getting my point, i am not
putting one type of control), how can i code that.The control state should be saved in the table.And whenever i open particylar webform it show shos all the control which i have created before
private void CreateTable(short noOfRows)
{
PlaceHolder p1 = new PlaceHolder();
Table table1 = new Table();
table1.BorderWidth = 1;
table1.BorderStyle = BorderStyle.Solid;
TableRow[] rows = new TableRow[noOfRows];
for (int i = 0; i < rows.Length; i++)
{
rows[i] = new TableRow();
}
table1.Rows.AddRange(rows);
TextBox t1 = new TextBox();
t1.Text = "Hello";
t1.EnableViewState = true;
TextBox t2 = new TextBox();
t2.Text = "World";
t2.EnableViewState = true;
Button btnOk = new Button();
btnOk.EnableViewState = true;
btnOk.Text = "OK";
Button btnCancel = new Button();
btnCancel.EnableViewState = true;
btnCancel.Text = "Cancel";
TableCell cell1=new TableCell();
TableCell cell2 = new TableCell();
TableCell cell3 = new TableCell();
TableCell cell4 = new TableCell();
cell1.Controls.Add(t1);
cell2.Controls.Add(t2);
cell3.Controls.Add(btnOk);
cell4.Controls.Add(btnCancel);
table1.Rows[0].Cells.AddAt(0, cell1);
table1.Rows[0].Cells.AddAt(1, cell2);
table1.Rows[1].Cells.AddAt(0, cell3);
table1.Rows[1].Cells.AddAt(1, cell4);
p1.Controls.Add(table1);
form1.Controls.Add(p1);
}
I have an issue regarding a TableCell splitting strategy on WPF FlowDocument Table.
Here is a simple code allowing to reproduce the issue :
MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var table = new Table() { BorderThickness = new Thickness(1), BorderBrush = Brushes.Black, CellSpacing = 0 };
var rowGroup = new TableRowGroup();
var tableRow = new TableRow();
var cell1 = new TableCell() { Background = Brushes.Red, BorderThickness = new Thickness(0, 0, 1, 0), BorderBrush = Brushes.Black };
var cell2 = new TableCell() { Background = Brushes.Red };
cell1.Blocks.Add(new Paragraph(new Run("Cell 1 ******************************************************************************")));
cell2.Blocks.Add(new Paragraph(new Run("Cell 2")));
tableRow.Cells.Add(cell1);
tableRow.Cells.Add(cell2);
rowGroup.Rows.Add(tableRow);
table.RowGroups.Add(rowGroup);
var flowDocument = new FlowDocument();
flowDocument.Blocks.Add(table);
Content = flowDocument;
}
}
And here is the result :
As you can see on the second page, the right cell Background color is lost.
Has anyone already came across this issue? Any solution/workaround will be welcome!
Edit 1 : All properties are lost so setting the Background color on the Row/Column won't solve my problem (I have mainly issues regarding TableCell Border Thicknesses)
Here is a screen showing the issue with borders :
Edit 2 : Looking at the Visual Tree is fairly instructive. The pagination process seems to only generates one ParagraphVisual for the Row on the second page, thus explaining the loss of all visual effects. There is no Visual, and thus no background/borders/etc...
A solution may be to tweak the DocumentPaginator associated to the FlowDocument
I've changed your code to demonstrate even more what Eyal H has stated:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var table = new Table() { BorderThickness = new Thickness(1), BorderBrush = Brushes.Black, CellSpacing = 4 };
var rowGroup = new TableRowGroup();
var tableRow = new TableRow();
var cell1 = new TableCell() { RowSpan = 1, Background = Brushes.Red, BorderThickness = new Thickness(3, 3, 3, 3), BorderBrush = Brushes.Green };
var cell2 = new TableCell() { RowSpan = 1, Background = Brushes.Red, BorderThickness = new Thickness(2, 2, 2, 2), BorderBrush = Brushes.Blue };
var correctContent = "**************************************************************************************************************************************************************************************************************************************";
cell1.Blocks.Add(new Paragraph(new Run("Cell 1" + correctContent)));
cell2.Blocks.Add(new Paragraph(new Run("Cell 2" + correctContent.Replace("*"," ")+".")));
tableRow.Cells.Add(cell1);
tableRow.Cells.Add(cell2);
rowGroup.Rows.Add(tableRow);
table.RowGroups.Add(rowGroup);
var flowDocument = new FlowDocument();
flowDocument.Blocks.Add(table);
Content = flowDocument;
}
}
The Cell 2 has many spaces ending with a dot. Cell 1 fits on page 1 and an empty cell with no borders (and no colour) is placed on the second page. I was not able to find a property of any Table or DocumentPaginator object in the hierarchy that would deal with the page breaks within cells.
"Since the Cell2 is shorter than the page it does not split and therefore an "empty" cell is located at the second page (with no properties)"
Maybe it's time for a Connect article?
Sadly, I was not able to find a solution. This seems to be a bug inherent to the WPF FlowDocument and it is not easy to find an entry point in the pagination process.
My main goal was to have Tables splitting correctly among pages in my document so I finally decided to allow Table to split, but not Cells.
This was quite easy to do, I just had to wrap my cell contents in a BlockUIContainer like this :
cell1.Blocks.Add(new BlockUIContainer() { Child = new TextBlock () { Text = "Cell 1 ******************************************************************************", TextWrapping = TextWrapping.Wrap}});
This allows me to avoid having missing borders in split tables, but cells cannot split anymore.
This is not satisfactory but is the best I was able to achieve.
I think adding TableColumn will solve your problem. below is the sample code.
var table = new Table() { BorderThickness = new Thickness(1), BorderBrush = Brushes.Black, CellSpacing = 0 };
var rowGroup = new TableRowGroup();
var tableRow = new TableRow();
var tableColumn1 = new TableColumn { Background = Brushes.Red };
var tableColumn2 = new TableColumn { Background = Brushes.Red };
var cell1 = new TableCell() { Background = Brushes.Red, BorderThickness = new Thickness(0, 0, 1, 0), BorderBrush = Brushes.Black };
var cell2 = new TableCell() { Background = Brushes.Red };
cell1.Blocks.Add(new Paragraph(new Run("Cell 1 ******************************************************************************")));
cell2.Blocks.Add(new Paragraph(new Run("Cell 2")));
tableRow.Cells.Add(cell1);
tableRow.Cells.Add(cell2);
rowGroup.Rows.Add(tableRow);
table.Columns.Add(tableColumn1);
table.Columns.Add(tableColumn2);
table.RowGroups.Add(rowGroup);
var flowDocument = new FlowDocument();
flowDocument.Blocks.Add(table);
Content = flowDocument;