C# Printvisual, can not fit to page - c#

I want to use printvisual to print this gridcontroll:
<Grid Name="printingkvitto2" Margin="0" Height="900" Width="842" VerticalAlignment="Top">
//stuff
</Grid>
Got this code:
PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
//if (printDlg.ShowDialog() == true)
{
LocalPrintServer localPrintServer = new LocalPrintServer();
PrintQueue pq = localPrintServer.GetPrintQueue(Properties.Settings.Default.valdskrivarenamn);
printDlg.PrintQueue = pq;
//get selected printer capabilities
System.Printing.PrintCapabilities capabilities = printDlg.PrintQueue.GetPrintCapabilities(printDlg.PrintTicket);
//get scale of the print wrt to screen of WPF visual
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / printingkvitto2.ActualWidth, capabilities.PageImageableArea.ExtentHeight /
printingkvitto2.ActualHeight);
//Transform the Visual to scale
printingkvitto2.LayoutTransform = new ScaleTransform(scale, scale);
//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.
printingkvitto2.Measure(sz);
printingkvitto2.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.PrintVisual(printingkvitto2, "print");
}
But I cant get it to fit the printed page (A4). It's always outside of the frame on the right side.
I want to send a "fit to page" setting to the printer.
What am I doing wrong?

Related

How to print WPF layout to not be dependent on the screen resolution?

I've been trying to use the following method to print a page on A4 paper. If my application runs on a laptop where the resolution is 1366x768 the printout scales down and I have no idea why? If I run it on a PC where the monitor resolution is higher, it prints as it should. I have tried to change PageMediaSize, ScaleTransform, LayoutTransform, Windows.Size with no success. What am I doing wrong?
The first 4 lines are there because I am sometimes using a 'print preview' where I'm scaling the page using a Viewbox, so the user can resize the preview. To not to affect the printing I am setting the Height and Width right before printing but outside of the PC's visible desktop area so it is not visible for the user.
var desktopWorkingArea = System.Windows.SystemParameters.WorkArea;
this.Top = desktopWorkingArea.Bottom + 100;
this.Height = 1250;
this.Width = 815;
System.Windows.Controls.PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
printDlg.PrintQueue = new System.Printing.PrintQueue(new System.Printing.PrintServer(), printerName);
System.Printing.PrintCapabilities capabilities = printDlg.PrintQueue.GetPrintCapabilities(printDlg.PrintTicket);
//get scale of the print wrt to screen of WPF visual
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / this.ActualWidth, capabilities.PageImageableArea.ExtentHeight / this.ActualHeight);
//Transform the Visual to scale
this.LayoutTransform = new ScaleTransform(scale, scale);
//get the size of the printer page
System.Windows.Size sz = new System.Windows.Size(this.ActualWidth, this.ActualHeight); //(8.5 * 96.0, 11.0 * 96.0);
//update the layout of the visual to the printer page size.
this.Measure(sz);
this.Arrange(new Rect(new System.Windows.Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
//now print the visual to printer to fit on the one page.
printDlg.PrintVisual(this, "Certificate - " + serialNumberTextBlock.Text);

WPF Print canvas to fit page

I am trying to print a canvas to both printer and file with the help of PrintDialog. I want the canvas to fit the page. I was able to achieve it using the following code
private void Print(Visual v)
{
System.Windows.FrameworkElement e = v as System.Windows.FrameworkElement ;
if (e == null)
return;
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() == true)
{
//store original scale
Transform originalScale = e.LayoutTransform;
//get selected printer capabilities
System.Printing.PrintCapabilities capabilities = pd.PrintQueue.GetPrintCapabilities(pd.PrintTicket);
//get scale of the print wrt to screen of WPF visual
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / e.ActualWidth, capabilities.PageImageableArea.ExtentHeight /
e.ActualHeight);
//Transform the Visual to scale
e.LayoutTransform = new ScaleTransform(scale, scale);
//get the size of the printer page
System.Windows.Size sz = new System.Windows.Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
//update the layout of the visual to the printer page size.
e.Measure(sz);
e.Arrange(new System.Windows.Rect(new System.Windows.Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
//now print the visual to printer to fit on the one page.
pd.PrintVisual(v, "My Print");
//apply the original transform.
e.LayoutTransform = originalScale;
}
}
The above code seems to be working as expected but when I am writing it to a PDF file using a PDF writer, the canvas will be resized when the save dialog box shows up and it will be set back to normal. So there is a resizing happening in the UI as well.
This canvas is already a cloned one and this cannot be printed without showing it in the UI because there are some background operations happening to fill the elements in the canvas which can be started only after it's loaded. Hence the cloned canvas is shown as a print preview.
Does anyone know of a good solution, or perhaps improve on the existing one to solve the UI resizing issue?

Print FixedDocument onto label without PrintDialog (setting page margin and size myself)

tl;dr: Why do I need to set an extra "magic" offset on the left side when printing with WPF, XpsDocumentWriter and FixedDocument compared to printing with WinForms and PrintDocument?
I am building a WPF app that creates a FixedDocument and prints it to a label printer via XpsDocumentWriter. It's split in two parts, as I want the printer part to be able to receive and print a FixedDocument over the network from another app.
It's for use with a point of sale system, so I do not want to show a PrintDialog for every print. Size of labels and the printer name is stored in a template file. The things to be printed is in the template file combined with some data. (In below example printer name is taken from a combobox for now and just some demo things to be printed).
I retrieve the printer queue with the given printer name, and I can print to the printer, but the margins are quite off - the print is too far to the left, outside of the label and by more than just the liner margin (distance from label edge to the roll edge). By trial'n'error I found the magic offset to be around 20 plus the liner margin, but I guess that's different on another printer.
The old version of this app (which is made with WinForms and PrintDocument) prints allright (no matter how the printer properties' paper is setup). The wish from business is that all setup is in the template file, completely disregarding printer properties setup. That is how it is functioning now in the WinForms app.
What am I forgetting to set, what's the gotchas?
Should I do it some other way?
Do I need to set the PageMediaSize and PageMediaType, and why?
I'd like to do checks on PrinterCapabilities, but not if they depend on the current printer properties like the page size set in the properties. That one I'd like to set in code.
Update 11/3
I now tried printing to both a Metapace and a Zebra labelprinter, and both is happy with the 20 unit offset on the left side. But... why is it needed? When printing to pdf/xps, I get the offset to the left plus the margin on both sides. This I do not get with the old program in Winforms?
And do I need to do Measure, Arrange and UpdateLayout?
Part one - the FixedDocument creator
var printerName = CmbPrinters.SelectedValue.ToString();
var size =
(FlexTemplate.TemplateParser.TemplateFunctions.Size)
template.Functions.First(f => f is FlexTemplate.TemplateParser.TemplateFunctions.Size);
var labelSize = new Size(size.Width.Value, size.Height.Value);
// Dimensions are 1/96th of an inch, socalled Device Independent Pixels (matches 96 DPI, which is normal?)
var linerMargin = 4.7244094488189; // 1.25 mm
var magicOffset = 20; // 5.29 mm
// The document
var doc = new FixedDocument();
doc.PrintTicket = new PrintTicket();
var printTicket = (PrintTicket) doc.PrintTicket;
printTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.Unknown, size.Width.Value, size.Height.Value);
// New page
var page = new FixedPage
{
Width = labelSize.Width + magicOffset + 2 * linerMargin,
Height = labelSize.Height,
Margin = new Thickness(magicOffset + linerMargin, 0, linerMargin, 0)
};
// Page content
var border = new Border
{
Width = labelSize.Width,
Height = labelSize.Height,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(4)
};
FixedPage.SetLeft(border, 0);
FixedPage.SetTop(border, 0);
page.Children.Add(border);
// Next item on page
var tb = new TextBlock
{
Text = "Hello World!",
FontFamily = new FontFamily("Algerian Regular"),
FontSize = 30,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top
};
var content = new Border
{
Child = tb,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1)
};
FixedPage.SetLeft(content, 10);
FixedPage.SetTop(content, 10);
page.Children.Add(content);
// Next item on page
var tb2 = new TextBlock
{
Text = "SomethingElse",
FontFamily = new FontFamily("Segoe UI"),
FontSize = 20,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top
};
var content2 = new Border
{
Child = tb2,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1)
};
FixedPage.SetLeft(content2, 20);
FixedPage.SetTop(content2, 55);
page.Children.Add(content2);
//// Add the page to the document
//var pc = new PageContent(); // Old way of doing it (.NET 3.5 and earlier)
//((IAddChild)pc).AddChild(page); //
//doc.Pages.Add(pc); //
var pc = new PageContent();
pc.Child = page;
doc.Pages.Add(pc);
// Send it to the printer
FlexPrinter.Print(printerName, labelSize, doc);
Part two - the FixedDocument printer
// Get XpsDocumentWriter
PrintQueue queue = new PrintServer().GetPrintQueue(printerName);
var dw = PrintQueue.CreateXpsDocumentWriter(queue);
dw.Write(document);

Microsoft Report page orientation (VS 2013, C#)

I am printing labels with MS Report (c#/VS2013). The labels are 8 cm wide and 4 cm high. The printer sees and feeds them as portrait (no rotation) but the report viewer prints them landscape because the width is greater than the hight. Printer specific page orientation change before printing are ignored (!), so the labels are always printed in a 90° rotation against the label orientation. printer is an industial thermo transfer printer.
I don't understand why the page orientation is bound to the relation between hight and width and cannot be set indepenently! I already tried to change the orientation right before printing - this caused the labes be printed in several pieces but did not rotate them.
The only thing that helped was changing the paper size to 8 width and 8.1 height. Then the labels print correctly but this leads to a lot of empty pages (labels) and is no good solution.
The only way I see currently is redesigning all the labels rotated by 90° which is quite some effort, so I would be grateful if someone had a solution for this stange behaviour!
I solved this issue by rendering to an image and printing that through a PrintDocument object. This way I am able to send the landscape-formed label (8cm width, 4cm height) to the printer in portrait.
I am sure this can be done more elegant but due to time pressure I now go with this solution:
String tempDir = Path.GetTempFileName();
File.Delete(tempDir);
Directory.CreateDirectory(tempDir);
tempDir = Utils.addBackslash(tempDir);
ExportToPNG(tempDir);
String imageFile = Utils.FindFirstFile(tempDir, "*.png");
if (imageFile != "")
{
PrintDocument pd = new PrintDocument();
try
{
pd.DefaultPageSettings.PrinterSettings.PrinterName = DB.Instance.getSetting("label.printername");
}
catch
{
// This is just a preset, it may fail without consequences
}
pd.DefaultPageSettings.Landscape = false; // Now I can do it!
pd.PrintPage += (sender, args) =>
{
Image i = Image.FromFile(imageFile);
Point p = new Point(100, 100);
args.Graphics.DrawImage(i, 0, 0, i.Width, i.Height);
};
PrintDialog pdi = new PrintDialog();
pdi.Document = pd;
if (pdi.ShowDialog() == DialogResult.OK)
pd.Print();
pd.Dispose();
}

WPF Print Problem

When i select Microsoft XPS Document Writer as Printer, my output is perfect but when i select my HP 1020 printer machine, the printer outputs blank copy...Following is the code....
private void printButton_Click(object sender, RoutedEventArgs e)
{
PrintInvoice pi = new PrintInvoice();
pi.DataContext = this.DataContext;
PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
if (printDlg.ShowDialog() == true)
{
pi.Margin = new Thickness(30);
//now print the visual to printer to fit on the one page.
printDlg.PrintVisual(pi, "First Fit to Page WPF Print");
}
}
This could be caused by a number of different things. There are a few steps that you can add which, when performed correctly, may cause the flying men to return with goods and knowledge.
First, you should scale to the printed page (code from a2zdotnet):
System.Printing.PrintCapabilities capabilities =
printDlg.PrintQueue.GetPrintCapabilities(printDlg.PrintTicket);
//get scale of the print wrt to screen of WPF visual
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / this.ActualWidth, capabilities.PageImageableArea.ExtentHeight /
this.ActualHeight);
//Transform the Visual to scale
this.LayoutTransform = new ScaleTransform(scale, scale);
//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.
this.Measure(sz);
this.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.PrintVisual(this, "Code ganked from http://www.a2zdotnet.com/View.aspx?id=66");
The cargo-cult code is in the Measure and Arrange steps. Often, if you call Measure and pass in a new Size(Double.MaxValue, Double.MaxValue) that's all you need to do.
The second ritual involves bumping the Dispatcher.
visual.DataContext = foo;
Dispatcher.Invoke((Action)()=>{;}); // bamp
// print here
Try these and see if it helps.
this XAML does the trick in some situations
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
<... Name="myPrintElement" />
</ScrollViewer >

Categories