Printing in WPF - wpf

I've created some windows in wpf that I need to print to one xps document. Each window opens, loads the relevant data and then immediately closes. Currently I use the below code to create the xps:
Using doc = New XpsDocument(TempLoc, FileAccess.Write)
Dim writer = XpsDocument.CreateXpsDocumentWriter(doc)
Dim collator = writer.CreateVisualsCollator()
Dim Window1 As Window1 = New Window1()
Window1.ShowDialog()
Dim Window2 As Window2 = New Window2()
Window2.ShowDialog()
Dim WindowX As WindowX = New WindowX()
WindowX.ShowDialog()
collator.BeginBatchWrite()
collator.Write(Window1)
collator.Write(Window2)
collator.Write(WindowX)
collator.EndBatchWrite()
End Using
Dim doc2 = New XpsDocument(TempLoc, FileAccess.Read)
Dim seq = doc2.GetFixedDocumentSequence()
Dim window = New Window()
window.Content = New DocumentViewer() With {.Document = seq}
window.ShowDialog()
doc2.Close()
However the trouble with this approach is that the area printed varies between machines - I assume this is due to the local screen size being used etc.
Is it possible to make the program print the full window independent of the computer its on by modifying this code? Alternativly is there a better way to approach this problem?
Thanks for any help

I print with FixedDocuments by either appending or adding placed UIElements. I posted the full source code to my helper class here. You may find that breaking it down by UIElements gives you much better control over your exact printed output, though yes, it requires you to separate printing code instead of just emitting your window.
I've used this helper class to create some very nicely formated multi-page reports that always come out the same on every computer.

Related

Manipulate visibility by using x:Name=" as string from code

Is it possible to manipulate visibility and other options of element by using x:Name=" as string in VB.Net + WPF?
Things i tried?
Dim w As MainWindow = Application.Current.Windows(0)
'Dim nameOfControl As String = ("buttonClose")
w.[nameOfControl].Visibility = Visibility.Visible
And outpus is as i expected it to be, nameOfControl is not memeber of main window
My final result should be to manipulate(visualy) all gui elements based on database information.
(Turns out OP needs to look up arbitrary controls by string name in a loop -- which is ideally done via MVVM and a redesign, but for now the answer is walking the visual tree).
You've already pretty much got it.
Dim w As MainWindow = Application.Current.Windows(0)
w.buttonClose.Visibility = Visibility.Collapsed
When you put the x:Name attribute on a control, WPF gives the parent class (MainWindow in this case) a Friend property for the control that has that name. Friend (C# calls it internal) means any code in the same assembly has access to it. w is a reference to your MainWindow class, so there it is.
Therefore, assuming this code is in the same assembly as MainWindow, that should work.
Edit2: Fixed a bug that caused only the first child of matching type to be returned.
Edit: After looking into this more, it seems there are some substantial differences with extension methods in C# and VB.Net. I have made some changes to hopefully account for those changes.
The following method will get all children of the given visual (Window is a Visual):
Imports System.Collections.Generic;
Imports System.Windows;
Imports System.Windows.Media;
Module VisualExtensions
<System.Runtime.CompilerServices.Extension>
Public Function GetVisualChildren(Of T As Visual)(parent As DependencyObject) As IEnumerable(Of T)
Dim child As T = Nothing
Dim numVisuals As Integer = VisualTreeHelper.GetChildrenCount(parent)
For i As Integer = 0 To numVisuals - 1
Dim v As Visual = DirectCast(VisualTreeHelper.GetChild(parent, i), Visual)
child = TryCast(v, T)
If v IsNot Nothing Then
For Each item As var In GetVisualChildren(Of T)(v)
yield Return item
Next
End If
If child IsNot Nothing Then
yield Return child
End If
Next
End Function
End Module
This extension method must be in a Module, like shown above.
You can get a child with a specific name like so:
Dim window = Application.Current.Windows(0)
Dim visuals = window.GetVisualChildren(Of FrameworkElement)()
Dim nameOfControl = "NameOfControl"
Dim child = visuals.OfType(Of FrameworkElement)().FirstOrDefault(Function(x) x.Name = nameOfControl)
child.Visiablity = Visibility.Collapsed
FrameworkElement can be replaced if you know what the type of the child control is. Note that if there are multiple children with the same name, only one of those children will be returned. Use Where() instead of FirstOrDefault() if you want to get them all.
Disclaimer: I am a C# programmer, all of this VB.Net code was converted from C# to VB using telerik's converter found here. There may be syntax or other errors in this code.

I need help converting System.Window.Size to System.Window.Point

I just started working on an existing VB project where the end user wants the ability to print a WPF window as a full page to a printer. I found this code sample in C# and it worked just fine in C#
Printing WPF Window to Printer and Fit on a Page
However when I tried converting it to VB I am getting two errors
System.Drawing.Size cannot be converted to System.Windows.Size
System.Drawing.Point cannot be converted to System.Windows.Point
I sort of know what the difference in Drawing.Size and Windows.Size is based on this (and a couple of other) SO threads What is the difference between System.Drawing.Point and System.Windows.Point? but I cannot figure out how to modify my translation in a way that I can make the conversion happen in VB the way it works on C#. The errors appear on the Measure(sz) and the Arrange(new Rect(.....) lines.
What do I need to do to make this work?
Private Sub PrintWindow()
Dim printDlg As PrintDialog = New PrintDialog()
If printDlg.ShowDialog() = True Then
Dim capabilities As System.Printing.PrintCapabilities =
printDlg.PrintQueue.GetPrintCapabilities(printDlg.PrintTicket)
Dim scale As Double = Math.Min(capabilities.PageImageableArea.ExtentWidth / ActualWidth,
capabilities.PageImageableArea.ExtentHeight / ActualHeight)
LayoutTransform = New ScaleTransform(scale, scale)
Dim sz As New Size(CInt(capabilities.PageImageableArea.ExtentWidth),
CInt(capabilities.PageImageableArea.ExtentHeight))
Measure(sz)
Arrange(New Rect(New Point(CInt(capabilities.PageImageableArea.OriginWidth),
CInt(capabilities.PageImageableArea.OriginHeight)), sz))
printDlg.PrintVisual(Me, "First Fit to Page WPF Print")
End If
End Sub
Why not do something like this ?
Dim sz As New System.Windows.Size(CInt(capabilities.PageImageableArea.ExtentWidth),
CInt(capabilities.PageImageableArea.ExtentHeight))
Arrange(New Rect(New System.Windows.Point(CInt(capabilities.PageImageableArea.OriginWidth),
CInt(capabilities.PageImageableArea.OriginHeight)), sz))

DirectCast any window, not only main window?

I've been trying to edit variables in other windows like in VB.NET using DirectCast. This seems to be working very well with the main window, as I use
Private Main As MainWindow = DirectCast(Application.Current.MainWindow, MainWindow)
But, I am unable to find a way to use this with a window other than the main one. For now, I am stuck using this
Dim WindowOne As New Window1
WindowOne.Show()
This works, but I would rather not have to create a new instance of the window each time I want it to open. I have tried using
Private WindowOne As Window1 = DirectCast(Application.Current.Windows.OfType(Of Window1).First(), Window1)
but it always gives me an error saying that "The sequence contains no elements".
Is there any other way to do this? What am I doing wrong?
The correct syntax is below.
Private WindowOne As Window1 = Application.Current.Windows.OfType(Of Window1)().FirstOrDefault()
If Not WindowOne Is Nothing Then
'object is available here
End If

WPF loading image from internet

Hey all i am trying to find a way to load an image via the WebClient using a WFP window.... The code below loads just fine when i am using a normal Windows Form:
Dim wc As New Net.WebClient
picNextTopic1.Image = Image.FromStream(wc.OpenRead(theAPI.ImgURL(2).Replace("{width}", "50").Replace("{height}", "50")))
However, this doesnt seem to work for WPF???
The WPF seems to use a .source for images? How can i convert that code above in order to use it with a WPF window?
WPF is not Windows Forms. You need to build WPF applications completely different. Properties, INotifyPropertyChanged etc.
But to your problem:
Dim wc As New WebClient()
Dim bytes = wc.DownloadData("http://....")
Dim ms = New MemoryStream(bytes)
Dim img = New BitmapImage()
ms.Seek(0, SeekOrigin.Begin)
img.BeginInit()
img.StreamSource = ms
img.EndInit()
picNextTopic1.Source = img

I can't use Window.Show despite of creating new Window instance

here's the problem:
In my WPF application I used to load/parse my .xaml files using XamlReader.Load Method to open a window in my application.
Codefragment of my function which return the window:
Dim win As New Window()
Dim myObject As Object
Dim xml As XmlReader = XmlReader.Create("mysample.xaml")
myObject = System.Windows.Markup.XamlReader.Load(xml)
win = CType(myObject, Window)
Return win
I use this to display all my different windows the user wants to see.
I open the window with win.Show and close it, when user switch to another window with win.Close. It works well!
Now to increase the performance I plan to do all the XAMLReader.Load at Application Start and store the information into a Dictionary:
Private Shared windict As Dictionary(Of String, Object)
Public Shared Sub ConvertXAMLToWindow(ByVal formName As String)
windict = New Dictionary(Of String, Object)
Dim myObject As Object
Dim xml As XmlReader = XmlReader.Create(formName)
myObject = System.Windows.Markup.XamlReader.Load(xml)
windict.Add(formName, myObject)
End Sub
Then I want to use that information when calling windows:
If windict.ContainsKey(formName) Then
Dim win As New Window()
Dim myObject As Object
myObject = windict(formName)
win = CType(myObject, Window)
Return win
End If
Now
This works well, but when I use win.Close to close my window I get an error when trying to open it again with win.Show, although I create an new instance of Window?
System.InvalidOperationException
Cannot set Visibility or call Show, ShowDialog... after a Window has
closed.
But it works when I don't use the Dictionary Method but the XAMLReader.Load directly - any ideas whats going on ? Somehow the window I get by returning XamlReader.Load seems different than the stored information from the dict?? Am I missing somehting? Thanks in advance!
You could use Hide() instead of Close()
Hide hides the Form, so instead of disposing of the form (and its controls) you make it invisible. Show will make it visible again.
Be careful though, the form in the dictionary will still hold the state from the previous time it was used.

Resources