Search code examples
c#wpfclonexps

Create a virtual copy of Grid Element and add to XPS document


My app is creating from some user input, some unique numbers and displaying those as QR codes, so that they can be printed out.

This is the function where I'm facing an issue.

private void Button_Click(object sender, RoutedEventArgs e)
{
     listBox10.Visibility = Visibility.Collapsed;
     PrintGrid.Visibility = Visibility.Visible;

     GenerateUniqueNumber_v2.PrintTemplate printTemplate = new GenerateUniqueNumber_v2.PrintTemplate();

     // Create a temporary ListBox to hold items for printing
     ListBox tempListBox = new ListBox();

     // Create a list to hold the filled templates
     List<FrameworkElement> filledTemplates = new List<FrameworkElement>();

     // Iterate over the items in the ListBox
     for (int i = 0; i < listBox10.Items.Count; i++)
     {
         // Add the current item to the temporary ListBox
         tempListBox.Items.Add(listBox10.Items[i]);

         // Check if we have collected 6 items or if we are at the last item
         if ((i + 1) % 6 == 0 || i == listBox10.Items.Count - 1)         
         {
             // Call the print method with the current batch of items
             var filledTemplate = printTemplate.FillPrintGridTemplate(tempListBox);                    
             filledTemplates.Add(filledTemplate); // Add the filled template to the list
             filledTemplate = null;
             //filledTemplates.Add(printTemplate.FillPrintGridTemplate(tempListBox));
             //Print_WPF_Preview(printTemplate.FillPrintGridTemplate(tempListBox));
             // Clear the temporary ListBox for the next batch
             tempListBox.Items.Clear();
             tempListBox = new ListBox();
             //printTemplate.Close();
         }
     }

     Print_WPF_Preview(filledTemplates);

     //printTemplate.Show();
     // Close the print template if needed
     //printTemplate.Close();
}

I have a listbox with the items included. As on one print page only 6 elements can be displayed, I check when I have 6 elements or I have the last item and pass them to the FillPrintGridTemplate method.

In this window I've created a template grid, where I fill every time the it passes the WPF elements, and return afterwards the grid.

When I only have <=6 items, all works fine, and I get one page with the correct QR codes to be printed out. But as soon at there are more than 6 items, I get 2 or more pages in my XPS document which is correct, but on every page, I get the same QR codes and information.

I figured out, when I pass here:

var filledTemplate = printTemplate.FillPrintGridTemplate(tempListBox);

the scecond time, the first element in my filledTemplates list gets overwritten. I assume that this is because I use the same grid and overwrites it every time.

public Grid FillPrintGridTemplate(ListBox listBox)
{
    Grid virtualGrid = new Grid();

    Label[] wnrLabels = { lbl_WNR1, lbl_WNR2, lbl_WNR3, lbl_WNR4, lbl_WNR5, lbl_WNR6 };
    Label[] UniqueNumberLabels = { lbl_UniqueNumber1, lbl_UniqueNumber2, lbl_UniqueNumber3, lbl_UniqueNumber4, lbl_UniqueNumber5, lbl_UniqueNumber6 };
    Label[] MATNRLabels = { lbl_MATNR1, lbl_MATNR2, lbl_MATNR3, lbl_MATNR4, lbl_MATNR5, lbl_MATNR6 };
    Image[] QRCodeImage = { qrCodeImage1, qrCodeImage2, qrCodeImage3, qrCodeImage4, qrCodeImage5, qrCodeImage6 };

    ResetElements(wnrLabels, UniqueNumberLabels, MATNRLabels, QRCodeImage);

    // Iterate over each item in the ListView
    int i = 0;

    foreach (var obj in listBox.Items)
    {
        // Cast the item to UniqueNumberItem  
        UniqueNumberItem item = obj as UniqueNumberItem;

        if (item != null)
        {
            // Now that we've ensured 'item' is a UniqueNumberItem, we can access its properties  
            wnrLabels[i].Content = item.WNR;
            UniqueNumberLabels[i].Content = item.UniqueNumber;
            MATNRLabels[i].Content = item.Matnr;

            // Generate QR code for the Unique Number  
            BitmapImage qrCodeImage = GenerateQRCode(item.UniqueNumber);
            // Set the Image control's Source to the QR code  
            QRCodeImage[i].Source = qrCodeImage;

            i++; // Move to the next index for the next item  
        }
    }

    return MainGrid;
}

I was thinking about if it's possible to create something like a clone of the grid or some temporary grid elements, of course with their child elements. So I won't overwrite the same grid every time, and get my problem solved. Or is there any better way to get this done?

Just to give you an idea, that's what it's looking like. I also get a random blank paper between every time, but I guess that's another problem

enter image description here

public static void Print_WPF_Preview(List<FrameworkElement> wpf_Elements)
{
    string sPrintFileName = "print_preview.xps";

    if (File.Exists(sPrintFileName))
    {
        File.Delete(sPrintFileName);
    }

    // Create XPS document
    using (XpsDocument doc = new XpsDocument(sPrintFileName, FileAccess.ReadWrite))
    {
        XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
        SerializerWriterCollator output_Document = writer.CreateVisualsCollator();
        output_Document.BeginBatchWrite();

        // Iterate over each FrameworkElement and write it to the document
        foreach (var element in wpf_Elements)
        {
            output_Document.Write(element);
            // Add a new page for each element
            output_Document.Write(new PageContent());
        }

        output_Document.EndBatchWrite();

        // Open the document for preview
        FixedDocumentSequence preview = doc.GetFixedDocumentSequence();
        GenerateUniqueNumber_v2.PrintWindow printWindow = new GenerateUniqueNumber_v2.PrintWindow(preview);
        printWindow.Show();
    }
}

Solution

  • That will work for me

    
    private void PrintButton_Click(object sender, RoutedEventArgs e)        
    {
    
             InsertToDB();
    
             PrintDialog printDialog = new PrintDialog();
             if (printDialog.ShowDialog() == true)
    
             {
                 currentItemIndex = 0; // Reset the index for a new print job
                 DrawingVisual visual = new DrawingVisual();
    
                 using (DrawingContext dc = visual.RenderOpen())
                 {
                     DrawPage(dc);
    
                 }
    
                 
                 printDialog.PrintVisual(visual, "Print Document");
             }
         }
    
         private void DrawPage(DrawingContext dc)
         {
             double x = 50; // Starting X position
             double y = 50; // Starting Y position
             double rowHeight = 200; // Height of each row (adjusted for QR code and text)
             double columnWidth = 220; // Width of each column
    
             int itemsPerPage = 15; // 12 items per page
             int totalItems = Items.Count;
    
             for (int i = 0; i < itemsPerPage; i++)
             {
                 if (currentItemIndex >= totalItems)
                 {
                     // No more items to print
                     return;
                 }
    
                 UniqueNumberItem item = Items[currentItemIndex];
    
                 // Generate the QR code for the current item's UniqueNumber
                 BitmapImage qrCodeImage = GenerateQRCode(item.UniqueNumber);
    
                 // Calculate the position for the QR code
                 int row = i / 3; // 3 columns
                 int col = i % 3; // 0, 1, or 2 for the column index
    
                 // Draw the QR code
                 dc.DrawImage(qrCodeImage, new System.Windows.Rect(x + col * columnWidth, y + row * rowHeight, 100, 100)); // Adjust size and position as needed
    
                 // Prepare the text to be drawn
                 string wnrText = $"WNR: {item.WNR}";
                 string uniqueNumberText = $"Unique Number: {item.UniqueNumber}";
                 string matnrText = $"MATNR: {item.Matnr}";
    
                 // Calculate the Y position for the text
                 double textYPosition = y + 110 + row * rowHeight; // Adjust the base Y position for text
    
                 // Draw the WNR text
                 FormattedText wnrFormattedText = new FormattedText(
                     wnrText,
                     System.Globalization.CultureInfo.CurrentCulture,
                     FlowDirection.LeftToRight,
                     new Typeface("Arial"),
                     12,
                     System.Windows.Media.Brushes.Black); // Use WPF Brushes
                 dc.DrawText(wnrFormattedText, new System.Windows.Point(x + col * columnWidth, textYPosition));
    
                 // Draw the Unique Number text
                 FormattedText uniqueNumberFormattedText = new FormattedText(
                     uniqueNumberText,
                     System.Globalization.CultureInfo.CurrentCulture,
                     FlowDirection.LeftToRight,
                     new Typeface("Arial"),
                     12,
                     System.Windows.Media.Brushes.Black); // Use WPF Brushes
                 dc.DrawText(uniqueNumberFormattedText, new System.Windows.Point(x + col * columnWidth, textYPosition + 20)); // Adjust Y position for Unique Number
    
                 // Draw the MATNR text
                 FormattedText matnrFormattedText = new FormattedText(
                     matnrText,
                     System.Globalization.CultureInfo.CurrentCulture,
                     FlowDirection.LeftToRight,
                     new Typeface("Arial"),
                     12,
                     System.Windows.Media.Brushes.Black); // Use WPF Brushes
                 dc.DrawText(matnrFormattedText, new System.Windows.Point(x + col * columnWidth, textYPosition + 40)); // Adjust Y position for MATNR
    
                 currentItemIndex++; // Move to the next item
             }
    
             // Check if there are more pages
             if (currentItemIndex < totalItems)
             {
                 // More items to print, so we need to print another page
                 DrawingVisual newVisual = new DrawingVisual();
                 using (DrawingContext newDc = newVisual.RenderOpen())
                 {
                     DrawPage(newDc); // Draw the next page
                 }
    
                 // Print the new visual
                 PrintDialog printDialog = new PrintDialog();
                 printDialog.PrintVisual(newVisual, "Print Document");
             }
         }