I am making a WPF application that will be used to print labels. I want to design a label template as a WPF window. In a different class I will instantiate this 'window template', fill the properties at runtime and print the label. I cannot show the label on the screen before printing, so I am unable to call .ShowDialog() on this window instance. This comes into play later.
I have been doing research on this for the past week and I have found two ways that nearly do what I want separately, and if I could combine them it would work, but I am missing a piece.
I could get this to work but following the steps here Printing in WPF Part 2
This accomplishes the basics of printing a document. However, I would not be able to use my template, and I would have to position everything in the code behind. This is a viable option but I would like to explore the template idea more.
PrintDialog pd = new PrintDialog();
FixedDocument document = new FixedDocument();
document.DocumentPaginator.PageSize = new System.Windows.Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight);
FixedPage page1 = new FixedPage();
page1.Width = document.DocumentPaginator.PageSize.Width;
page1.Height = document.DocumentPaginator.PageSize.Height;
// add some text to the page
Label _lblBarcode = new Label();
_lblBarcode.Content = BarcodeConverter128.StringToBarcode(palletID);
_lblBarcode.FontFamily = new System.Windows.Media.FontFamily("Code 128");
_lblBarcode.FontSize = 40;
_lblBarcode.Margin = new Thickness(96);
page1.Children.Add(_lblBarcode);
// add the page to the document
PageContent page1Content = new PageContent();
((IAddChild)page1Content).AddChild(page1);
document.Pages.Add(page1Content);
pd.PrintQueue = new System.Printing.PrintQueue(new System.Printing.PrintServer(), "CutePDF Writer");
pd.PrintDocument(document.DocumentPaginator, "PalletID");
I just show how I am printing a barcode here. In the actual program I would add everything on the label and position it as well.
In this class I instantiate my label, fill its properties, and try to add it to the FixedPage's Children - but it returns this error.
Specified element is already the logical child of another element. Disconnect it first.
To get around this, I could remove each UI element from the template then add it to my fixed document. This does not seem like the cleanest solution, but it is possible.
I then wanted to iterate through each UIElement in this list instead of manually removing each element from my template and adding it to my fixed document. I would do this in the case that the label ever needs to change, this would make that easier.
I was able to find this link that shows how to iterate through each element
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
However, this only seems to work in the code behind of the template itself! So that doesn't help me in my class where I want to do the printing. I, of course, could make a global scoped variable and pass my list of UIelements that way, but that is getting less and less clean. On top of that, I can only call this method, in the Windows_Loaded event of the template class. Windows_Loaded only gets called when the WindowInstance.ShowDialog();
gets called, which of course shows the template on the screen which is also what I can't have.
Is there anyway to accomplish what I am trying, or am I better of scrapping the whole template idea and positioning everything via the code behind.
UPDATED While some of the answers here pointed me in the right direction, it took a little more digging to do what I wanted to do. Solution to follow:
I pieced my solution together with the help of Lena's answer which I accepted below and another post
To get an example of this working for yourself take Lena's View.xaml, View.xaml.cs, ViewModel.cs, and then my own method to print the View.xaml
private void StackOverFlow()
{
string palletID = "00801004018000020631";
PrintDialog pd = new PrintDialog();
FixedDocument fixedDoc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage fixedPage = new FixedPage();
fixedDoc.DocumentPaginator.PageSize = new System.Windows.Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight);
fixedPage.Width = fixedDoc.DocumentPaginator.PageSize.Width;
fixedPage.Height = fixedDoc.DocumentPaginator.PageSize.Height;
fixedPage.Width = 4.0 * 96;
fixedPage.Height = 3.0 * 96;
var pageSize = new System.Windows.Size(4.0 * 96.0, 3.0 * 96.0);
View v = new View();
ViewModel vm = new ViewModel(); //This would be the label object with
//set all ViewModel.cs Props here
vm.Text1 = "MyText1";
vm.Text2 = "MyText2";
v.DataContext = vm;
v.Height = pageSize.Height;
v.Width = pageSize.Width;
v.UpdateLayout();
fixedPage.Children.Add(v);
((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
fixedDoc.Pages.Add(pageContent);
//Use the XpsDocumentWriter to "Write" to a specific Printers Queue to Print the document
XpsDocumentWriter dw1 = PrintQueue.CreateXpsDocumentWriter(new System.Printing.PrintQueue(new System.Printing.PrintServer(), "CutePDF Writer"));
dw1.Write(fixedDoc);
}