Using the WPF Window as a Visual Template Before Printing

I am creating a WPF application that will be used to print tags. I want to create a shortcut template as a WPF window. In another class, I will create an instance of this “window template”, fill in the properties at runtime, and print the label. I cannot show the shortcut on the screen before printing, so I cannot call .ShowDialog () on this instance of the window. It comes into play later.

I have been doing this research last week, and I found two ways that almost do what I want separately, and if I could combine them, it would work, but I’m missing a piece.

I could get this to work, but after the steps here, Printing in WPF Part 2

This provides the basics of printing a document. However, I could not use my template, and I would have to position everything in the code. This is a viable option, but I would like to learn more about the idea of ​​the template.

        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 will just show how I type the barcode here. In a real program, I would add everything to the label and place it.

In this class, I create an instance of my shortcut, populate its properties and try to add it to the FixedPage children, but it returns this error.

Specified element is already the logical child of another element. Disconnect it first.

To get around this, I can remove each user interface element from the template and then add it to my fixed document. This does not seem like the cleanest solution, but it is possible.

UIElement , . , - , .

,

        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;
                }
            }
        }
    }

, , ! , . , , UIelements , . , Windows_Loaded . Windows_Loaded WindowInstance.ShowDialog();, , , , .

, , .

. , , , . :

, ,

, Lena View.xaml, View.xaml.cs, ViewModel.cs, 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);

        }
+4
3

, , , , . - !

View.xaml

<DockPanel x:Class="WpfApplication1.View"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             Height="300" Width="400">
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="10" Text="Static Text"/>
            <Button Margin="10">Any control can be here</Button>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="10" Width="100"  Text="{Binding Text1}"/>
            <TextBox Width="100" Text="{Binding Text2}"/>
        </StackPanel>
    </StackPanel>
</DockPanel>

View.xaml.cs

using System.Windows.Controls;

namespace WpfApplication1
{
    public partial class View : DockPanel
    {
        public View()
        {
            InitializeComponent();
        }
    }
}

ViewModel.cs

namespace WpfApplication1
{
    public  class ViewModel
    {
        public string Text1 { get; set; }
        public string Text2 { get; set; }
    }
}

, ViewModel

var path = "newdoc.xps";
FixedDocument fixedDoc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage fixedPage = new FixedPage();
fixedPage.Width = 11.69 * 96;
fixedPage.Height = 8.27 * 96;
var pageSize = new System.Windows.Size(11.0 * 96.0, 8.5 * 96.0);
View v = new View();
ViewModel vm = new ViewModel();
vm.Text1 = "MyText1";
vm.Text2 = "MyText2";
v.DataContext = vm;
v.UpdateLayout();
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);
if (File.Exists(path))
    File.Delete(path);
XpsDocument xpsd = new XpsDocument(path, FileAccess.ReadWrite);
XpsDocumentWriter xw = XpsDocument.CreateXpsDocumentWriter(xpsd);
xw.Write(fixedDoc);
xpsd.Close();
+1

, : WPF Printing Part 1

PrintVisual , .

:

:

MainWindow _barcodeWindow = new MainWindow("A1b2c3D4e5");

private void Button_Click(object sender, RoutedEventArgs e)
{
    _barcodeWindow.Print();
    _barcodeWindow.ShowDialog();
}

"":

<StackPanel Name="StuffToPrint">
    <Label>Some sample text</Label>
    <Label Name="BarCodeLabel" FontSize="40" Margin="96" FontFamily="Code 128"/>
    <Label>More sample text</Label>
</StackPanel>

:

public MainWindow(string code)
{
    InitializeComponent();
    BarCodeLabel.Content = BarcodeConverter128.StringToBarcode(code);
}

public void Print()
{
    PrintDialog dlg = new PrintDialog();
    if (dlg.ShowDialog() == true)
    {
        dlg.PrintVisual(StuffToPrint, "Barcode");
    }
}
+3

How to copy top-level print of UIElement instead of the whole window?

For example, if you have a window:

<Window x:Class="WpfApplicationGrid.Window_Template1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window_Template1" Height="300" Width="300">
    <Grid Name="MainGrid" >
        <!--some content....-->
    </Grid>
</Window>

//-------------------------
public Grid GetMyGrid()
{
   return MainGrid;
}

Print

 Window_Template1 myWindow = new Window_Template1();
// clone the grid you want to print, so the exception you mentioned won't happen:
Grid clonedGrid = null;
string uiToSave = XamlWriter.Save(myWindow.GetMyGrid());
using (StringReader stringReader = new StringReader(uiToSave))
{
      using (XmlReader xmlReader = XmlReader.Create(stringReader))
      {
          clonedGrid = (Grid)XamlReader.Load(xmlReader);
      }
}

// for some reason you have to close the window even if there was no Show() called
// to properly dispose of it
// otherwise there may be some unpredictable behaviour (maybe I have something odd in my project settings...)
myWindow.Close();

PrintDialog pd = new PrintDialog();
FixedDocument document = new FixedDocument();
document.DocumentPaginator.PageSize = new System.Windows.Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight);

// remember to set sizes again, if they're not set in xaml, for example:
clonedGrid.Height = document.DocumentPaginator.PageSize.Height;
clonedGrid.Width = document.DocumentPaginator.PageSize.Width;
clonedGrid.UpdateLayout();

FixedPage page1 = new FixedPage();
page1.Width = document.DocumentPaginator.PageSize.Width;
page1.Height = document.DocumentPaginator.PageSize.Height;

// this will add the content of the whole grid to the page without problem
page1.Children.Add(clonedGrid);

PageContent page1Content = new PageContent();
((IAddChild)page1Content).AddChild(page1);
document.Pages.Add(page1Content);

// then print...
+1
source

Source: https://habr.com/ru/post/1619519/


All Articles