Grid not printing content - c#

The button is placed in a second grid. The first grid contains the content I want to print
However when I print the first grid doesn't show anything and is printing a blank document
Button and grid code:
Button Print = new Button();
Print.Content = "Print";
Print.Click += new RoutedEventHandler(OnPrintClick);
secondGrid.Children.Add(Print);
Grid.SetColumn(Print, 2);
Grid.SetRow(Print, 5);
ColumnDefinition myColumsecondGrid = new ColumnDefinition();
RowDefinition myRowsecondGrid = new RowDefinition();
myRowsecondGrid.Height = new GridLength(300);
myColumsecondGrid.Width = new GridLength(165);
secondGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = myColumsecondGrid.Width });
secondGrid.RowDefinitions.Add(new RowDefinition() { Height = myRow.Height });
Button print code:
private void OnPrintClick(object sender, RoutedEventArgs e)
{
PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
if (printDlg.ShowDialog() == true)
{
//System.Printing
//get selected printer capabilities
System.Printing.PrintCapabilities capabilities = printDlg.PrintQueue.GetPrintCapabilities(printDlg.PrintTicket);
//get the size of the printer page
Size sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
// update the layout of the visual to the printer page size.
myGrid.Measure(sz);
myGrid.Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
//now print the visual to printer to fit on the one page.
//printDlg.PageRangeSelection(printQty);
//now print the visual to printer to fit on the one page.
String printerName = "PDF reDirect v2";
System.Printing.PrintQueue queue = new System.Printing.LocalPrintServer().GetPrintQueue(printerName);
printDlg.PrintQueue = queue;
printDlg.PrintVisual(myGrid, "");
}
}

maybe this question will help you. You can look at the Measure and Arrange methods, probably smth in there.
Print Grid which generated dynamically in wpf

Does this code print for you anything? If it does, there's something wrong with the measurements. Also, don't forget that the grid has to be rendered at the moment you're trying to print it using PrintVisual method.
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{
dialog.PrintVisual(myGrid, "CustomDescription");
}

Related

How can I check if user has cancelled printing from printing queue?

I am trying to print a WPF component through XPS document writer, There are lots of pages (in 100). It takes lot of time to print. That is why I used Thread.Sleep to simulate that. While printing those pages. User can cancel the print from print queue of windows. I can print it easily but if user cancel it. It throws an exception at
collator.EndBatchWrite();
Exception Message
private void Print_Click(object sender, RoutedEventArgs e)
{
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
PrintQueue printQueue = printDialog.PrintQueue;
XpsDocumentWriter writer = PrintQueue.CreateXpsDocumentWriter(printQueue);
SerializerWriterCollator collator = writer.CreateVisualsCollator();
collator.BeginBatchWrite();
collator.Write(VisualLE());
Thread.Sleep(20000);
collator.Write(VisualLE());
collator.EndBatchWrite();
}
}
private static ContainerVisual VisualLE()
{
ContainerVisual newPage = new ContainerVisual();
FixedPage fixedPage;
Border border = new Border();
border.Height = 400;
border.Width = 400;
border.BorderThickness = new Thickness(1);
border.BorderBrush = new SolidColorBrush(Color.FromRgb(1, 1, 1));
fixedPage = new FixedPage();
Size size = new Size(600, 600);
fixedPage.Height = size.Height;
fixedPage.Width = size.Width;
fixedPage.Children.Add(border);
fixedPage.Measure(size);
fixedPage.Arrange(new Rect(new Point(), size));
fixedPage.UpdateLayout();
newPage.Children.Add(fixedPage);
return newPage;
}
Printing Queue

WPF FixedPage with same instance of UserControl

First off, I'm not sure if I phrased the title correctly. There are UserControls which are added via a ViewModel and I find them by searching the VisualTree and add them to a ObservableCollection<Grid>. What I would like to do is Print each instance of the UserControl that I retrieve from the VisualTree into a FixedDocument but with each UserControl being on a single page until it fills that page and moves on to the next page.
Here is the code for my current Print Button Click Event:
private async void btnPrint_Click(object sender, RoutedEventArgs e)
{
try
{
if (tabMain.Items.Count > 0)
{
tab = new TabLayout();
tab.Payslip = new ObservableCollection<Grid>();
foreach (var grid in FindVisualChildren<Grid>(this))
{
if (grid.Name == "pdfFile")
{
tab.Payslip.Add(grid);
}
}
FrameworkElement toPrint = new FrameworkElement();
PrintDialog printDialog = new PrintDialog();
PrintCapabilities capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
Size visibleSize = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
FixedDocument fixedDoc = new FixedDocument();
StackPanel panel = new StackPanel(); //was trying to stack them in a stackpanel first but it threw an exception about same instance of usercontrol blah blah...
foreach (var doc in tab.Payslip.ToList())
{
double yOffset = 0;
doc.Measure((new Size(double.PositiveInfinity, double.PositiveInfinity)));
doc.Arrange(new Rect(new Point(0, 0), doc.DesiredSize));
Size size = doc.DesiredSize;
while (yOffset < size.Height)
{
VisualBrush vb = new VisualBrush(doc);
vb.Stretch = Stretch.None;
vb.AlignmentX = AlignmentX.Left;
vb.AlignmentY = AlignmentY.Top;
vb.ViewboxUnits = BrushMappingMode.Absolute;
vb.TileMode = TileMode.None;
vb.Viewbox = new Rect(0, yOffset, visibleSize.Width, visibleSize.Height);
FixedPage page = new FixedPage();
PageContent pageContent = new PageContent();
((IAddChild)pageContent).AddChild(page);
fixedDoc.Pages.Add(pageContent);
page.Width = fixedDoc.DocumentPaginator.PageSize.Width;
page.Height = fixedDoc.DocumentPaginator.PageSize.Height;
Canvas canvas = new Canvas();
FixedPage.SetLeft(canvas, capabilities.PageImageableArea.OriginWidth);
FixedPage.SetTop(canvas, capabilities.PageImageableArea.OriginHeight);
canvas.Width = visibleSize.Width;
canvas.Height = visibleSize.Height;
canvas.Background = vb;
page.Children.Add(canvas);
yOffset += visibleSize.Height;
}
}
//printDialog.PrintDocument(fixedDoc.DocumentPaginator, "");
ShowPrintPreview(fixedDoc);
}
}
catch(Exception ex)
{
var exceptionDialog = new MessageDialog
{
Message = { Text = ex.ToString() }
};
await DialogHost.Show(exceptionDialog, "RootDialog");
}
}
This is how it looks when I try printing three Tabs (UserControls are hosted in Tabs):
As you can see here it prints three separate pages
I want all three on one page and if I print 10 tabs, then it should fill the first page and go on to the next page.
The last time I asked a similar question I was questioned on whether I wrote the code. Bits and pieces of this code came from similar FixedDocument questions on StackOverflow but it has been edited to the point where it actually works for me. So yes I know that the FixedPage reference inside the foreach is whats causing the creation of the three separate pages and I do understand the code.
Summary:
What I want to know is how to get the UserControls from each Tab, onto a single page until its full, without getting the "Specified element is already the logical child of another element. Disconnect it first." error.
I ended up using ItemsControl to hold my ObservableCollection of Controls as a list and print from the ItemsControl using this XpsDocumentWriter method.
public void PrintItemsTo(ItemsControl ic, String jobName)
{
PrintDialog dlg = new PrintDialog();
dlg.UserPageRangeEnabled = true;
if (dlg.ShowDialog().GetValueOrDefault())
{
PageRange range = dlg.PageRange;
// range check - user selection starts from 1
if (range.PageTo > ic.Items.Count)
range.PageTo = ic.Items.Count;
dlg.PrintQueue.CurrentJobSettings.Description = jobName;
XpsDocumentWriter xdw = PrintQueue.CreateXpsDocumentWriter(dlg.PrintQueue);
if (dlg.UserPageRangeEnabled == false || range.PageTo < range.PageFrom)
WriteAllItems(ic, xdw);
else
WriteSelectedItems(ic, xdw, range);
}
}
private void WriteAllItems(ItemsControl ic, XpsDocumentWriter xdw)
{
PageRange range = new PageRange(1, ic.Items.Count);
WriteSelectedItems(ic, xdw, range);
}
private void WriteSelectedItems(ItemsControl ic, XpsDocumentWriter xdw, PageRange range)
{
IItemContainerGenerator generator = ic.ItemContainerGenerator;
// write visuals using a batch operation
VisualsToXpsDocument collator = (VisualsToXpsDocument)xdw.CreateVisualsCollator();
collator.BeginBatchWrite();
if (WritePageRange(collator, generator, range))
collator.EndBatchWrite();
}
private bool WritePageRange(VisualsToXpsDocument collator, IItemContainerGenerator generator, PageRange range)
{
// Get the generator position of the first data item
// PageRange reflects user's selection starts from 1
GeneratorPosition startPos = generator.GeneratorPositionFromIndex(range.PageFrom - 1);
// If PageFrom > PageTo, print in reverse order
// UPDATE: never occurs; PrintDialog always 'normalises' the PageRange
GeneratorDirection direction = (range.PageFrom <= range.PageTo) ? GeneratorDirection.Forward : GeneratorDirection.Backward;
using (generator.StartAt(startPos, direction, true))
{
for (int numPages = Math.Abs(range.PageTo - range.PageFrom) + 1; numPages > 0; --numPages)
{
bool newlyRealized;
// Get or create the child
UIElement next = generator.GenerateNext(out newlyRealized) as UIElement;
if (newlyRealized)
{
generator.PrepareItemContainer(next);
}
// The collator doesn't like ContentPresenters and produces blank pages
// for pages 2-n. Finding the child of the ContentPresenter and writing
// that solves seems to solve the problem
if (next is ContentPresenter)
{
ContentPresenter presenter = (ContentPresenter)next;
presenter.UpdateLayout();
presenter.ApplyTemplate(); // not sure if this is necessary
DependencyObject child = VisualTreeHelper.GetChild(presenter, 0);
if (child is UIElement)
next = (UIElement)child;
}
try
{
collator.Write(next);
}
catch
{
// if user prints to Microsoft XPS Document Writer
// and cancels the SaveAs dialog, we get an exception.
return false;
}
}
}
return true;
}
PrintItemsTo(), displays a PrintDialog to print to any printer while WriteAllItems displays a Dialog to save as PDF.

Docking 2 rich text boxes

I am trying to make a text editor and I have 2 rich text boxes. I am uses the 1st rich text box as a number like and setting its Enabled property to false. Then the second text box is going next to it
I have currently set the dock of the first text box to left and the second one to fill. But the 2nd one keeps taking up the whole tabpage? And going slightly more left towards the number line and its hidden under there a little bit. Here is my create new document void I have... T2 is the number line and T is the default text box they will type into.
TabPage t = new TabPage("new " + getNumber());
tabControl1.TabPages.Add(t);
tabControl1.SelectedTab = t;
RichTextBox T2 = new RichTextBox();
t.Controls.Add(T2);
T2.Dock = DockStyle.Left;
T2.Enabled = false;
RichTextBox T = new RichTextBox();
t.Controls.Add(T);
T.Dock = DockStyle.Fill;
T.Font = new Font("Microsoft San Serif", 11);
Random R = new Random();
int RandomNumberHere = R.Next(1000, 100000);
T.Text = "Welcome, type your text...";
T.Select();
The problem is that you define your fill-docked text box at the end of the control list... winforms likes the fill-docked elements first in the list.
Easily solved:
RichTextBox T = new RichTextBox();
t.Controls.Add(T);
T.Dock = DockStyle.Fill;
T.Font = new Font("Microsoft San Serif", 11);
// Add this line
T.BringToFront();
Or you could also do T2.SendToBack(); after T is added to the controls collection.
Or you could simply create (and add to t.Controls), the fill-docked textbox first, and the left-docked textbox second.
Either way works
By the way, try to name your variables correctly. t, T, T2 are just not good names
Here you go:
t.Controls.Add(T2);
t.Controls.Add(T);
t.Controls.SetChildIndex(T, 1);
t.Controls.SetChildIndex(T2, 0);
t.PerformLayout(); // needed after SetChildIndex!
T2.Dock = DockStyle.Left;
T.Dock = DockStyle.Left;
If you want your boxes to grow with the TabPage here is one way to do it:
private void t_Resize(object sender, EventArgs e)
{
// assuming you want the two RTBs to fill the TabPage
// if you want something else, best add an anchored container panel
// and use its resize event instead
T2.Width = t.Width / 4;
T.Width = t.Width / 4;
}
And yes, t, T and T2 are really bad names!
There's a few things going on:
Fill does just that; it Fills the entirety of the PARENT control. Whether anything else is there or not.
Your other text box will be hidden until it is set to Enabled.
What you Might? be looking for is a way to have the text box size itself based on the size of the tab page being created:
{
TabPage t = new TabPage("new " + 1);
tabControl1.TabPages.Add(t);
tabControl1.SelectedTab = t;
RichTextBox T2 = new RichTextBox();
t.Controls.Add(T2);
T2.Dock = DockStyle.Left;
T2.Enabled = true ;
RichTextBox T = new RichTextBox();
t.Controls.Add(T);
T2.Dock = DockStyle.Right;
var AdjustedSize = T2.Size;
AdjustedSize.Width = t.Size.Width * 2 / 3;
T2.Size = AdjustedSize;
T.Font = new Font("Microsoft San Serif", 11);
Random R = new Random();
int RandomNumberHere = R.Next(1000, 100000);
T.Text = "Welcome, type your text...";
T.Select();
}

Printing more than one page for control

This code will only print one page but the height of the control exceeds the page height and therefore will need to be printed on a second page. I have been doing some investigation as to what will enable the control to proceed onto another page. I got as far as DocumentPaginator and fiddled with the size making it bigger and smaller than the sz variable but no difference. Any Ideas as to what controls a creation of a new page? does the size relate to pagination?
private void Print()
{
var pd = new PrintDialog();
var document = new FixedDocument();
var fixedPage = new FixedPage();
var pageContent = new PageContent();
System.Printing.PrintCapabilities capabilities = pd.PrintQueue.GetPrintCapabilities(pd.PrintTicket);
System.Windows.Size sz = new System.Windows.Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
MarSheetReport mar = new MarSheetReport();
document.DocumentPaginator.PageSize = sz;
Transform originalScale = fixedPage.LayoutTransform;
//get selected printer capabilities
fixedPage.LayoutTransform = new ScaleTransform(0.2823293807641634 + 0.2498215560314061, 0.2823293807641634 + 0.2498215560314061);
fixedPage.Width = sz.Width;
fixedPage.Height = sz.Height;
// Add visual, measure/arrange page.
fixedPage.Children.Add(mar.o);
fixedPage.Measure(sz);
fixedPage.Arrange(new System.Windows.Rect(new System.Windows.Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
fixedPage.UpdateLayout();
//fixedPage.LayoutTransform = originalScale;
((IAddChild)pageContent).AddChild(fixedPage);
document.Pages.Add(pageContent);
pd.PrintDocument(document.DocumentPaginator, "My Document");
}
Here is one way of doing it i.e creating the bitmap from the visual and breaking it on multiple pages.
http://www.codeproject.com/Articles/339416/Printing-large-WPF-UserControls

How to create windows forms dialog with dynamic number of buttons?

I need to create a sort of a dialog box or something like a popup screen. I have this array of items and then I need to create a buttons for each of them on the dialogbox so that i could navigate with a button click.
Whats the best way to do it in C#? can someone guide me on this please
If you are using winforms, then place FlowLayoutPanel on your form. Then add all controls to it at runtime.
foreach(var item in items)
{
Button button = new Button();
// setup button properties
// subscribe to events
flowLayoutPanel.Controls.Add(button);
}
FlowLayoutPanel will arrange your controls automatically.
Consider that you Dialog or similar parent element is called sp and ar is the array of elements that you want to use to create the buttons:
for(YourObject obj : ar)
{
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = obj.YourProperty;
newBtn.Name = "Button" + obj.YourProperty;
sp.Children.Add(newBtn);
}
I was playing with some concepts and one part of it seem to be exactly example of creating dynamic dialog. You can add dynamic button creation to it, combine it and properly format it using table or flow layout panel
DialogResult result;
using (var popup = new Form())
{
popup.Size = new Size(1000, 500);
popup.Location = new Point(Convert.ToInt32(this.Parent.Width / 2) - 500, Convert.ToInt32(this.Parent.Height / 2) - 250);
popup.FormBorderStyle = FormBorderStyle.FixedDialog;
popup.MinimizeBox = false;
popup.MaximizeBox = false;
popup.Text = "My title";
var lbl = new Label() { Dock = DockStyle.Top, Padding = new Padding(3), Height = 30 };
lbl.Font = new Font("Microsoft Sans Serif", 11f);
lbl.Text = "Do you want to Continue?";
// HERE you will add your dynamic button creation instead of my hardcoded
var btnYes = new Button { Text = "Yes", Location = new Point(700, 400) };
btnYes.Click += (s, ea) => { ((Form)((Control)s).Parent).DialogResult = DialogResult.Yes; ((Form)((Control)s).Parent).Close(); };
var btnNo = new Button { Text = "No", Location = new Point(900, 400) };
btnNo.Click += (s, ea) => { ((Form)((Control)s).Parent).DialogResult = DialogResult.No; ((Form)((Control)s).Parent).Close(); };
popup.Controls.AddRange(new Control[] { lbl, btnYes, btnNo });
result = popup.ShowDialog(this);
}
if (result == DialogResult.Yes)
{
// do this
}
else
{
// do that
}
This was example of how to create dynamic dialog with dialog result output.

Categories