WPF print - scale content to page visible area - wpf

I have to implement printing in a WPF application and I have to scale printable content to page visible area.
This is my code:
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog().Value)
{
FixedDocument fixedDocument = new FixedDocument();
Canvas canvas = new Canvas();
PrintCapabilities pc = dialog.PrintQueue.GetPrintCapabilities();
System.Windows.Shapes.Rectangle rc = new System.Windows.Shapes.Rectangle();
rc.Width = pc.PageImageableArea.ExtentWidth;
rc.Height = pc.PageImageableArea.ExtentHeight;
rc.Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(255, 0, 0));
canvas.Children.Add(rc);
Canvas.SetLeft(rc, pc.PageImageableArea.OriginWidth);
Canvas.SetTop(rc, pc.PageImageableArea.OriginHeight);
FixedPage fixedPage = new FixedPage();
fixedPage.Width = dialog.PrintableAreaWidth;
fixedPage.Height = dialog.PrintableAreaHeight;
fixedPage.Children.Add(canvas);
PageContent pageContent = new PageContent();
((IAddChild)pageContent).AddChild(fixedPage);
fixedDocument.Pages.Add(pageContent);
dialog.PrintDocument(fixedDocument.DocumentPaginator, "Demo");
}
The red rectangle is set to fill the page printable area.
The problem is the PrintCapabilities.PageImageableArea always returns the same values no matter what paper (Letter, Legal, etc) is selected.
If I run the code and don't change anything in the print dialog (default paper size is Letter) I get the print below:
The red rectangle fills the entire Letter page.
If I run the code and select Legal paper size in the print dialog then I get the print below:
The PrintCapabilities.PageImageableArea property still returns a Letter sized area no matter I selected Legal paper in print dialog.
I tested the code above with "Microsoft Print to PDF" printer which does not have unprintable margins (this is why the red rectangle fills the entire page) but the problem persists also with a physical printer.
What am I doing wrong?

Related

Unable to capture WPF livechart in bitmap

I'm trying to write a function that will write the results of a test (which have both text output and chart output, stored in a textbox and a livecharts chart respectively) to a pdf file. There are no issues with the text output, but I haven't been able to get the charts to save unless I extract each chart from its parent stackpanel (each test has a stackpanel that is shown when the test is selected) and then display the chart in a separate window. There is a lot of code around the problematic lines, so I'm going to paste a shortened version below.
The issue seems to be calling "_saveWindow.Show()" instead of ".ShowDialog". I need this to open and close without user input, but the "Show()" doesn't draw the chart in the window at all.
Any ideas why this is happening?
foreach (TestSequenceItem tsi in resultsToSave)
{
tsi.Instances[0].ChartStackPanel.Visibility = Visibility.Visible;
for (int i=0; i<tsi.Instances[0].StackChartList.Count; i++)
{
Chart _saveChart = tsi.Instances[0].StackChartList[i];
tsi.Instances[0].ChartStackPanel.Children.Remove(_saveChart);
ScrollViewer panel = new ScrollViewer { Content = _saveChart };
Window _saveWindow = new Window { Content = panel };
_saveWindow.Show();
var encoder = new PngBitmapEncoder();
RenderTargetBitmap bmp = new RenderTargetBitmap((int)tsiChartDoc.DefaultPageSetup.PageWidth, 600, 96, 96, PixelFormats.Pbgra32);
bmp.Render(_saveChart);
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (MemoryStream stm = new MemoryStream())
{
encoder.Save(stm);
string fileName = "base64:" + Convert.ToBase64String(stm.ToArray());
// Adding a heading to the pdf
tsiChartDoc.LastSection.AddParagraph(tsi.Instances[0].StackTitleList[i].Text, "Heading2");
tsiChartDoc.LastSection.AddImage(fileName);
}
_saveWindow.Close();
panel.Content = null;
tsi.Instances[0].ChartStackPanel.Children.Insert(chartIdx, _saveChart);
}
}

WPF event within a frame stored on a stackpanel

I need to recreate a program similar to whatsapp that can send and receive messages, images videos and audio. I have created a WPF form to show messages that looks like this:
I have a stack panel that contains text bubbles on them. Text messages work fine but if I send an image I want the user to be able to click on the image text bubble and it must become full screen. The image text bubble consists of a label that has a frame in it and then within that frame the image is stored. The label was used since we could resize the label.
However, because the image is done like this dynamically, we cannot seem to register an on-click event on this image bubble. If you have any better ways that we can display the image or how to log this event it would be much appreciated. Here is the method used to add the image.
public void AddMessage_Image(string path, string displayName, int role, string date = "")
{
//Create an image from the path
ImageBrush image = new ImageBrush();
image.ImageSource = new BitmapImage(new Uri(path, UriKind.Absolute));
image.Stretch = Stretch.Uniform;
//Create a frame in which to place the image
Frame fr = new Frame();
fr.Background = image;
fr.MinHeight = 120;
fr.MinWidth = 160;
//Ensure scalabilty of the image
Viewbox vb = new Viewbox();
vb.Child = fr;
vb.Stretch = Stretch.Uniform;
//Place the image in a sizable container
Label lbl = new Label();
lbl.MinHeight = 10;
lbl.MinWidth = 10;
lbl.MaxHeight = 300;
lbl.MaxWidth = 400;
lbl.Content = vb;
if (role == (int) Role.Sender)
lbl.HorizontalAlignment = HorizontalAlignment.Right;
else
lbl.HorizontalAlignment = HorizontalAlignment.Left;
lbl.Background = Brushes.Black;
//Place the image in the chat
chatbox.Children.Add(lbl);
}

WPF - How to work out how much of a canvas background image is cropped?

I have a canvas with a background image:
var bi = new BitmapImage(new Uri(imgLocFull));
var ib = new ImageBrush(bi) {Stretch = Stretch.UniformToFill};
MyCanvas.Background = ib;
I am overlaying various shapes on the image, and want the position of the shapes relative to the background image to be fixed.
If my application window is resized, the amount of the image that is cropped, horizontally and vertically, changes, and when my shapes are redrawn, they do not appear in the same position on the background image.
How can I determine how much of the image has been cropped (to apply an adjustment factor to the overlaid objects' positions?) Or is there a better way of fixing the location of a shape relative to the background image?
Here is my present drawing code:
var l = new Ellipse();
var scb = new SolidColorBrush();
scb.Color = Color.FromRgb(rCol, gCol, bCol);
l.Fill = scb;
l.StrokeThickness = 0;
l.Width = 3;
l.Height = 3;
Canvas.SetBottom(l, point.Y); // * clipping factor here?
Canvas.SetLeft(l, point.X); // * clipping factor here?
MyCanvas.Children.Add(l);
EDIT: Further Clarification
Here's a concrete example of what I am trying to achieve. My image is an aerial photograph, and I want to mark a particular geographical feature (with, say, an ellipse.)
When the window is resized, the ellipse doesn't stay on the feature, it stays relative to the left and top of the canvas.
I can get it closer to the right place by moving it using a factor (newx = newheight/oldheight * oldx) but this doesn't quite work because of the UniformToFill stretch mode, which sees some of the image clipped off the canvas.
The Top and Left of the Canvas are 'anchored', while the Bottom and Right move when resizing... try setting the Canvas.Top Attached Property instead, along with the Canvas.Left Attached Property as you are:
var l = new Ellipse();
var scb = new SolidColorBrush();
scb.Color = Color.FromRgb(rCol, gCol, bCol);
l.Fill = scb;
l.StrokeThickness = 0;
l.Width = 3;
l.Height = 3;
Canvas.SetTop(l, point.Y); // * clipping factor here?
Canvas.SetLeft(l, point.X); // * clipping factor here?
MyCanvas.Children.Add(l);
UPDATE >>>
You asked Or is there a better way of fixing the location of a shape relative to the background image?
I answered this question, so I don't understand why you would need to do anything else... your objects will not move when the screen in resized *if you only set the Grid.Top and Grid.Left properties.

Content not rotated when printing in wpf

I am printing an XPS document using the System.Windows.Controls.PrintDialog. When I choose landscape orientation in the print dialog the resulting page is rotated to landscape but the actual content stays in portrait mode and is clipped.
This is the way I print. I also tried to use the AddJob method on PrintDialog.PrintQueue and the overloads on PrintQueue.CreateXpsDocumentWriter(...).Write(...) all with the same or worse result. And I tried to set DocumentPaginator.PageSize, the printDialog.PrintTicket.PageMediaSize and the width and height of th first FixedPage to the correct lanscape size with no result. PrintDialog.PrintTicket.PageOrientation is on landscape and PrintDialog.PrintableAreaWidth and PrintDialog.PrintableAreaHeight is as it should be when lanscape is selected after the PrintDialog was shown.
var printDialog = new PrintDialog
{
MaxPage = (uint)pageCount,
MinPage = 1,
PageRange = new PageRange(1, pageCount),
UserPageRangeEnabled = true
};
if (printDialog.ShowDialog() != true) return;
using (var doc = new XpsDocument(filename, FileAccess.Read))
{
var paginator = doc.GetFixedDocumentSequence().DocumentPaginator;
printDialog.PrintDocument(fds.paginator , "myPrintJob");
}

VisualBrush does "lazy evaluation"?

I create FixedDocument in more iterations (one page per iteration) like this:
PrintDialog pr = new PrintDialog();
FixedDocument doc = new FixedDocument();
foreach(var i in a)
{
// some changes of MaingGrid here
...
//
VisualBrush vb = new VisualBrush(this.MainGrid);
FixedPage page = new FixedPage();
page.Width = doc.DocumentPaginator.PageSize.Width;
page.Height = doc.DocumentPaginator.PageSize.Height;
Rectangle rec = new Rectangle();
rec.Width = this.MainGrid.ActualWidth;
rec.Height = this.MainGrid.ActualHeight;
rec.Fill = vb;
page.Children.Add(rec);
PageContent content = new PageContent();
((IAddChild)content).AddChild(page);
doc.Pages.Add(content);
}
pr.PrintDocument(doc.DocumentPaginator, "test");
In each iteration I change the MainGrid a little. So each page should contain the actual state of MainGrid. But the printed document contains pages with same content of last iteration (in other words - the last state is on all pages in document). Is there any "lazy evaluation" of VisualBrush or something?
Call .Freeze() on the VisualBrush in each iteration. Otherwise, it will always be a live view of whatever visual you pointed it at.
EDIT: Freeze doesn't work but you can render the brush into a static bitmap. See http://blog.avanadeadvisor.com/blogs/markti/archive/2008/04/14/10888.aspx

Resources