다음을 통해 공유


앱에서 인쇄하기

이 항목에서는 Windows 앱에서 인쇄하는 방법을 설명합니다.

고급 기능은 인쇄 미리 보기 UI를 사용자 지정하기를 참조하세요.

인쇄 등록하기

앱에 인쇄를 추가하는 첫 번째 단계는 현재 창에 대한 PrintManager 개체를 가져오면 인쇄에 등록하는 것입니다. PrintManager 클래스는 앱의 인쇄 흐름을 오케스트레이션합니다. 이 클래스를 사용하려면 먼저 현재 활성 창과 관련된 PrintManager 개체를 반환하는 메서드를 호출해야 합니다.

사용자가 인쇄할 수 있도록 모든 화면에 대해 앱이 이 작업을 수행해야 합니다. 사용자에게 표시되는 화면만 인쇄용으로 등록할 수 있습니다. 앱의 한 화면이 인쇄에 등록되어 있는 경우, 종료 시 인쇄 등록을 취소해야 합니다. 다른 화면으로 바뀐 경우 다음 화면이 열리면 인쇄를 위해 등록해야 합니다.

 앱에서 하나 이상의 페이지 인쇄를 지원해야 하는 경우, 이 인쇄 코드를 공통 도우미 클래스에 넣고 앱 페이지에서 이를 재사용할 수 있습니다. 이 작업을 수행하는 방법에 대한 예시는 UWP 인쇄 샘플PrintHelper 클래스를 참조하세요.

사용자가 인쇄를 시작한 후 PrintDocument사용하여 프린터로 보낼 페이지를 준비합니다. 이 형식은 PrintDocument 인쇄를 위해 XAML 콘텐츠 준비를 지원하는 다른 형식과 함께 Microsoft.UI.Xaml.Printing 네임스페이스에 있습니다.

PrintDocument 클래스는 앱과 PrintManager 간의 상호 작용의 대부분을 처리하는 데 사용되지만 자체적으로 여러 콜백을 노출합니다. 등록하는 동안 인쇄 이벤트에 대한 인스턴스를 PrintManager PrintDocument 만들고 처리기를 등록합니다.

이 예제에서는 페이지의 로드된 RegisterForPrinting 이벤트 처리기에서 호출되는 메서드에서 등록이 수행됩니다.

using Microsoft.UI.Xaml.Printing;
using Windows.Graphics.Printing;

PrintDocument printDocument = null;
IPrintDocumentSource printDocumentSource = null;
List<UIElement> printPreviewPages = new List<UIElement>();

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    RegisterForPrinting();
}

private void RegisterForPrinting()
{
    var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
    PrintManager printManager = PrintManagerInterop.GetForWindow(hWnd);
    printManager.PrintTaskRequested += PrintTask_Requested;

    printDocument = new PrintDocument();
    printDocumentSource = printDocument.DocumentSource;
    printDocument.Paginate += PrintDocument_Paginate;
    printDocument.GetPreviewPage += PrintDocument_GetPreviewPage;
    printDocument.AddPages += PrintDocument_AddPages;
}

Warning

UWP 인쇄 예제에서는 OnNavigatedTo 메서드 재정의에서 인쇄에 등록하는 것이 좋습니다. UWP가 아닌 앱에서는 PrintManagerInterop.GetForWindow 호출에서 창 핸들을 사용해야 하므로 Loaded 이벤트를 사용하여 Windows 핸들이 아닌지 null확인해야 합니다. 이는 OnNavigatedTo의 경우일 수 있습니다.

여기서 이벤트 처리기는 OnNavigatedFrom 메서드에서 호출되는 메서드에서 등록 UnregisterForPrinting 취소됩니다.

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);
    UnregisterForPrinting();
}


private void UnregisterForPrinting()
{
    if (printDocument == null)
    {
        return;
    }

    printDocument.Paginate -= PrintDocument_Paginate;
    printDocument.GetPreviewPage -= PrintDocument_GetPreviewPage;
    printDocument.AddPages -= PrintDocument_AddPages;

    var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
    PrintManager printManager = PrintManagerInterop.GetForWindow(hWnd);
    printManager.PrintTaskRequested -= PrintTask_Requested;
}

참고 항목

다중 페이지 앱이 있고 인쇄 연결을 끊지 않은 경우 사용자가 페이지를 떠났다가 다시 돌아올 때 예외가 발생합니다.

인쇄 버튼 만들기

인쇄 버튼을 표시할 앱의 화면에 추가합니다. 추가한 인쇄 버튼이 인쇄하려는 콘텐츠에 방해가 되지 않는지 확인합니다.

<Button x:Name="InvokePrintingButton"
        Content="Print"
        Click="InvokePrintingButton_Click"/>

단추의 Click 이벤트 처리기에서 사용자에게 Windows 인쇄 UI를 표시합니다.

var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
await PrintManagerInterop.ShowPrintUIForWindowAsync(hWnd);

이 메서드는 적절한 인쇄 창을 표시하는 비동기 메서드이므로 Click 처리기에 비동기 키워드를 추가해야 합니다. 인쇄를 지원하는 디바이스에서 앱이 실행되고 있음을 확인하기 위해 먼저 IsSupported 메서드를 호출하는 것이 좋습니다(그렇지 않은 경우 처리함). 다른 이유로 해당 시간에 인쇄를 수행할 수 없는 경우 메서드는 예외를 throw합니다. 이러한 예외를 catch하고 인쇄를 계속할 수 없는 경우 사용자에게 알리는 것이 좋습니다.

이 예시에서는 버튼 클릭에 대한 이벤트 처리기에 인쇄 창이 표시됩니다. 메서드가 예외(해당 시간에 인쇄를 수행할 수 없음)를 throw하는 경우, ContentDialog 컨트롤이 사용자에게 상황을 알릴 수 있습니다.

private async void InvokePrintingButton_Click(object sender, RoutedEventArgs e)
{
    if (PrintManager.IsSupported())
    {
        try
        {
            // Show system print UI.
            var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
            await Windows.Graphics.Printing.PrintManagerInterop.ShowPrintUIForWindowAsync(hWnd);
        }
        catch
        {
            // Printing cannot proceed at this time.
            ContentDialog noPrintingDialog = new ContentDialog()
            {
                Title = "Printing error",
                Content = "\nSorry, printing can' t proceed at this time.",
                PrimaryButtonText = "OK"
            };
            await noPrintingDialog.ShowAsync();
        }
    }
    else
    {
        // Printing is not supported on this device.
        ContentDialog noPrintingDialog = new ContentDialog()
        {
            Title = "Printing not supported",
            Content = "\nSorry, printing is not supported on this device.",
            PrimaryButtonText = "OK"
        };
        await noPrintingDialog.ShowAsync();
    }
}

앱 콘텐츠 서식 지정하기

ShowPrintUIForWindowAsync 호출되면 PrintTaskRequested 이벤트가 발생합니다. PrintTaskRequested 이벤트 처리기에서 PrintTaskRequest.CreatePrintTask 메서드를 호출하여 PrintTask를 만듭니다. 인쇄 페이지의 제목과 PrintTaskSourceRequestedHandler 대리자의 이름을 전달합니다. 제목은 인쇄 미리 보기 UI에 표시됩니다. PrintTaskSourceRequestedHandler 콘텐츠를 제공할 링크 PrintTask 입니다PrintDocument.

이 예시에서는 완료 처리기도 오류를 catch하도록 정의됩니다. 완료 이벤트를 처리하는 것이 좋습니다. 그러면 앱이 사용자에게 오류가 발생했는지 알리고 가능한 솔루션을 제공할 수 있기 때문입니다. 마찬가지로 앱은 완료 이벤트를 사용하여 인쇄 작업이 성공한 뒤 사용자가 수행할 후속 단계를 나타낼 수 있습니다.

private void PrintTask_Requested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
    // Create the PrintTask.
    // Defines the title and delegate for PrintTaskSourceRequested.
    PrintTask printTask = args.Request.CreatePrintTask("WinUI 3 Printing example", PrintTaskSourceRequested);

    // Handle PrintTask.Completed to catch failed print jobs.
    printTask.Completed += PrintTask_Completed;

    DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
    {
        InvokePrintingButton.IsEnabled = false;
    });
}

private void PrintTaskSourceRequested(PrintTaskSourceRequestedArgs args)
{
    // Set the document source.
    args.SetSource(printDocumentSource);
}

private void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
    string StatusBlockText = string.Empty;

    // Notify the user if the print operation fails.
    if (args.Completion == PrintTaskCompletion.Failed)
    {
        StatusBlockText = "Failed to print.";
    }
    else if (args.Completion == PrintTaskCompletion.Canceled)
    {
        StatusBlockText = "Printing canceled.";
    }
    else
    {
        StatusBlockText = "Printing completed.";
    }


    DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
    {
        StatusBlock.Text = StatusBlockText;
        InvokePrintingButton.IsEnabled = true;
    });
}

인쇄 작업을 만든 뒤, 인쇄 미리 보기 UI에 표시할 인쇄 페이지 컬렉션을 요청하기 위해 PrintManager페이지 매김 이벤트를 발생시킵니다. (인터페이스의 Paginate 메서드에 해당합니다 IPrintPreviewPageCollection .) 등록 중에 만든 이벤트 처리기가 현재 호출됩니다.

Important

 사용자가 인쇄 설정을 변경한 경우에는 콘텐츠를 재배치할 수 있도록 페이지 매김 이벤트 처리기가 다시 호출됩니다. 최상의 사용자 환경을 위해 콘텐츠를 재배치하기 전에 설정을 체크하여 필요하지 않은 경우 페이지를 매긴 콘텐츠를 다시 초기화하지 않는 것이 좋습니다.

페이지 매김 이벤트 처리기에서 인쇄 미리 보기 UI에 표시하고 프린터로 보낼 페이지를 만듭니다. 인쇄하기 위해 앱의 콘텐츠를 준비하는 데 사용하는 코드는 앱 및 인쇄 콘텐츠와 관련이 있습니다.

이 예제에서는 화면에 표시된 페이지에서 이미지와 캡션을 인쇄하는 단일 인쇄 페이지를 만드는 기본 단계를 보여 줍니다.

  • 인쇄할 UI 요소(페이지)를 보관할 목록을 만듭니다.
  • 페이지 매김이 발생할 때마다 페이지가 중복되지 않도록 미리 보기 페이지 목록을 지웁니다.
  • PrintPageDescription을 사용하여 프린터 페이지의 크기를 가져옵니다.
  • 프린터 페이지에 맞게 XAML 콘텐츠의 서식을 지정합니다. 인쇄할 각 페이지는 XAML UI 요소(일반적으로 다른 콘텐츠가 포함된 컨테이너 요소)입니다. 이 예제에서는 코드에서 요소를 만들고 화면에 표시된 요소와 동일한 데이터를 사용합니다.
  • 필요에 따라 콘텐츠를 추가 페이지로 흐릅니다. 이 기본 예제에서는 여러 페이지가 표시되지 않지만 페이지를 페이지로 나누는 것은 페이지 매김 이벤트의 중요한 부분입니다.
  • 인쇄할 페이지 목록에 각 페이지를 추가합니다.
  • PrintDocument에서 미리 보기 페이지 수를 설정합니다.
List<UIElement> printPreviewPages = new List<UIElement>();

private void PrintDocument_Paginate(object sender, PaginateEventArgs e)
{
    // Clear the cache of preview pages.
    printPreviewPages.Clear();

    // Get the PrintTaskOptions.
    PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);
    // Get the page description to determine the size of the print page.
    PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);

    // Create the print layout.
    StackPanel printLayout = new StackPanel();
    printLayout.Width = pageDescription.PageSize.Width;
    printLayout.Height = pageDescription.PageSize.Height;
    printLayout.BorderBrush = new Microsoft.UI.Xaml.Media.SolidColorBrush(Microsoft.UI.Colors.Black);
    printLayout.BorderThickness = new Thickness(48);

    Image printImage = new Image();
    printImage.Source = printContent.Source;

    printImage.Width = pageDescription.PageSize.Width / 2;
    printImage.Height = pageDescription.PageSize.Height / 2;

    TextBlock imageDescriptionText = new TextBlock();
    imageDescriptionText.Text = imageDescription.Text;
    imageDescriptionText.FontSize = 24;
    imageDescriptionText.HorizontalAlignment = HorizontalAlignment.Center;
    imageDescriptionText.Width = pageDescription.PageSize.Width / 2;
    imageDescriptionText.TextWrapping = TextWrapping.WrapWholeWords;

    printLayout.Children.Add(printImage);
    printLayout.Children.Add(imageDescriptionText);

    // Add the print layout to the list of preview pages.
    printPreviewPages.Add(printLayout);

    // Report the number of preview pages created.
    PrintDocument printDocument = (PrintDocument)sender;
    printDocument.SetPreviewPageCount(printPreviewPages.Count,
                                          PreviewPageCountType.Intermediate);
}

다음은 앱 UI의 스크린샷과 인쇄 미리 보기 UI에 콘텐츠가 표시되는 방식입니다.

인쇄할 이미지와 캡션을 보여 주는 시스템 인쇄 미리 보기 UI 옆에 있는 앱 UI의 스크린샷.

특정 페이지가 인쇄 미리 보기 창에 표시되는 경우, PrintManagerGetPreviewPage 이벤트를 발생시킵니다. 인터페이스의 메서드에 MakePage 해당합니다 IPrintPreviewPageCollection . 등록 중에 만든 이벤트 처리기가 현재 호출됩니다.

GetPreviewPage 이벤트 처리기에서 인쇄 문서에서 적절한 페이지를 설정합니다.

private void PrintDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    PrintDocument printDocument = (PrintDocument)sender;
    printDocument.SetPreviewPage(e.PageNumber, printPreviewPages[e.PageNumber - 1]);
}

마지막으로 사용자가 인쇄 단추를 클릭하면 PrintManager는 인터페이스의 메서드 IDocumentPageSource 를 호출 MakeDocument 하여 프린터로 보낼 최종 페이지 컬렉션을 요청합니다. 이는 XAML에서 AddPages 이벤트를 발생시킵니다. 등록 중에 만든 이벤트 처리기가 현재 호출됩니다.

AddPages 이벤트 처리기에서 페이지 컬렉션의 페이지를 프린터로 보낼 PrintDocument 개체에 추가합니다. 사용자가 인쇄할 특정 페이지 또는 페이지 범위를 지정하는 경우, 실제로 프린터로 전송되는 페이지만 추가하기 위해 여기에서 해당 정보를 사용합니다.

private void PrintDocument_AddPages(object sender, AddPagesEventArgs e)
{
    PrintDocument printDocument = (PrintDocument)sender;

    // Loop over all of the preview pages and add each one to be printed.
    for (int i = 0; i < printPreviewPages.Count; i++)
    {
        printDocument.AddPage(printPreviewPages[i]);
    }

    // Indicate that all of the print pages have been provided.
    printDocument.AddPagesComplete();
}