Search code examples
c#wpfxamlprintingprintdialog

Error when setting a PrintDialog's PrintQueue to a networked printer - C# WPF XAML


I am trying to set a WPF PrintDialog to use values set by the user on a form to control the basic printing options, without ever showing them a dialog.

The goal is to allow them to set their print options one time, then use those settings hundreds of times during the day without having to change them unless they decide to do so.

The code works for all printers installed on my machine other than network printers. If I select a networked printer from the list, I get the following error -

System.Printing.PrintQueueException: 'An exception occurred while populating the properties for the PrintQueue object. Win32 error: The printer name is invalid.'

The strange thing is that if I set one of the network printers as the default printer, and add logic to print unattended via a LocalPrintServer.DefaultPrintQueue being passed to a PrintDialog, this code works.

I've dissected the way printer names are being handled, and I just cant seem to figure out the correct way to pass in the networked printers.

Here's my current code (I've tried so many iterations with the same results that I've lost count... LOL).

XAML- I'm populating a XAML combo box with the list of installed printers as follows -

    <Window x:Class="PrintDialogTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:Printing="clr-namespace:System.Printing;assembly=System.Printing"
        mc:Ignorable="d"
        Title="MainWindow" Height="550" Width="450">
    <Window.Resources>
        <Printing:LocalPrintServer x:Key="localPrintServer1"/>
        <ObjectDataProvider x:Key="printerCollection"
                        ObjectInstance="{StaticResource localPrintServer1}"
                        MethodName="GetPrintQueues">
            <ObjectDataProvider.MethodParameters>
                <x:ArrayExtension Type="{x:Type Printing:EnumeratedPrintQueueTypes}">
                    <Printing:EnumeratedPrintQueueTypes>Local</Printing:EnumeratedPrintQueueTypes>
                    <Printing:EnumeratedPrintQueueTypes>Connections</Printing:EnumeratedPrintQueueTypes>
                </x:ArrayExtension>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>.............
    <ComboBox x:Name="cboPrinters" HorizontalAlignment="Left" Height="34" Margin="53,414,0,0" VerticalAlignment="Top" Width="354" ItemsSource="{Binding Source={StaticResource printerCollection}}" DisplayMemberPath="Name" FontSize="14"/>

This works fine, and gives me all of the installed printers.

If I run the application, and select anything other than the two networked printers, the code below (from the MainWindow.xaml.cs Print button's click event) works -

    // Create a PrintDialog  
            PrintDialog printDlg = new PrintDialog();
            // Create a PrintQueue and PrintServer - assign the user selected printer to the PrintQueue
            printDlg.PrintQueue = new PrintQueue(new PrintServer(), cboPrinters.Text);

As stated before, I've tried multiple ways of formatting the name value being passed into the PrintQueue with no success.

I though that this may have been the fix, but it also triggers the error when used -

PrintServerException - “…name is invalid” even though I can access the path from windows

Hopefully I've provided enough info, but if not, please let me know and I'll add whatever else is needed.

Any help would be greatly appreciated, and thanks for any replies in advance.

UPDATE - if I hardcode the instantiation of the PrintServer with the network print server's name, the code works for network printers also (with a caveat) -

printDlg.PrintQueue = new PrintQueue(new PrintServer(@"\\NetworkPrintServerNameHere"), (string)cboPrinters.SelectedValue);

But obviously this code is not practical for real world use since we can have multiple PrintServer names in our list of printers. I need to find a way to get the PrintServer name for the currently selected printer.


Solution

  • So, using the code at the top of the request that is creating the record set for the combo box, we can programmatically parse the selected record for the needed PrintSever value for the currently selected printer at runtime by setting it as follows -

    printDlg.PrintQueue = new PrintQueue(new PrintServer(((System.Printing.PrintQueue)cboPrinters.SelectedItem).HostingPrintServer.Name), (string)cboPrinters.SelectedValue);
    

    I no longer get the error, and can print to any of the local and network printers that the user selects.