如何自定义打印设置(UWP 设备应用)

Important

设备元数据已弃用,并将在 Windows 的将来版本中删除。 有关替代功能的信息,请参阅驱动程序包容器元数据

在 Windows 8.1 中,UWP 设备应用允许打印机制造商自定义显示高级打印设置的浮出控件。 本主题介绍高级打印设置浮出控件,并说明 打印设置的 C# 版本和打印通知 示例如何将默认浮出控件替换为自定义浮出控件。 若要了解有关 UWP 设备应用的一般详细信息,请参阅 UWP 设备应用简介

打印设置和打印通知示例的 C# 版本使用 Preferences.xaml 页面来演示高级打印设置的自定义浮出控件的 UI。 打印帮助程序类用于创建设备上下文(IPrinterExtensionContext)并执行设备查询。 The PrinterHelperClass.cs file is in the DeviceAppForPrintersLibrary project and uses APIs defined in the PrinterExtensionLibrary project. 打印机扩展库提供了一种访问 v4 打印驱动程序的打印机扩展接口的便捷方法。 有关详细信息,请参阅 打印机扩展库概述

Note

本主题中显示的代码示例基于 打印设置和打印通知 示例的 C# 版本。 此示例在 JavaScript 和 C++中也可用。 请注意,由于C++可以直接访问 COM,因此示例C++版本不包括代码库项目。 下载示例以查看最新版本的代码。

高级打印设置

高级打印设置体验是在用户想要选择打印窗口中未提供的打印设置时打印机提供的功能。 It is accessible through the More settings link in the Print window. 这不是全屏体验,而是在浮出控件中显示,它是用于显示轻量级上下文用户界面的控件,当用户单击或点击外部时,该界面将被消除。

此体验可用于突出显示打印机的区分功能,例如能够将水印应用于文档页、提供安全打印选项或图像增强选项。

如果未为打印机安装 UWP 设备应用,Windows 将提供默认打印设置体验。 如果 Windows 检测到为打印机安装了 UWP 设备应用,并且该应用已选择加入 windows.printTaskSettings 该扩展,则你的应用将替换 Windows 提供的默认体验。

若要调用高级打印设置的浮出控件,请执行以下作:

  1. 打开支持打印的 UWP 应用

  2. 通过在屏幕右侧轻扫来访问超级按钮(或使用 Windows 徽标键 + C)

  3. Tap the Devices charm

  4. Tap Print

  5. 点击打印机

  6. The Print window opens

  7. Click the More settings link on the Print window

  8. 此时会打开高级打印设置浮出控件

    • The default flyout appears when no UWP device app for the printer is installed

    • A custom flyout appears when a UWP device app for the printer is installed

高级打印设置的默认和自定义浮出控件的示例。

Prerequisites

准备工作:

  1. 请确保使用 v4 打印驱动程序安装打印机。 有关详细信息,请参阅 开发 v4 打印驱动程序

  2. 设置开发电脑。 See Getting started for info about downloading the tools and creating a developer account.

  3. 将应用与应用商店相关联。 有关该应用的信息,请参阅 “创建 UWP 设备应用 ”。

  4. 为将它与应用关联的打印机创建设备元数据。 有关此内容的详细信息,请参阅 “创建设备元数据 ”。

  5. 为应用的主页生成 UI。 可以从“开始”启动启动所有 UWP 设备应用,其中将显示它们全屏。 使用“开始”体验以与设备的特定品牌和功能匹配的方式突出显示产品或服务。 它可以使用的 UI 控件类型没有特殊限制。 若要开始设计全屏体验,请参阅 Microsoft应用商店设计原则

  6. If you're writing your app with C# or JavaScript, add the PrinterExtensionLibrary and DeviceAppForPrintersLibrary projects to your UWP device app solution. 可以在 “打印设置”和“打印通知 ”示例中找到每个项目。

Note

由于C++可以直接访问 COM,C++应用不需要单独的库来处理基于 COM 的打印机设备上下文。

步骤 1:注册扩展

为了使 Windows 能够识别应用可以提供高级打印设置的自定义浮出控件,它必须注册打印任务设置扩展。 此扩展在元素中 Extension 声明,属性 Category 设置为值 windows.printTaskSettings。 在 C# 和C++示例中,属性 Executable 设置为 $targetnametoken$.exe ,并且属性 EntryPoint 设置为 DeviceAppForPrinters.App

You can add the print task settings extension on the Declarations tab of the Manifest Designer in Microsoft Visual Studio. 还可以使用 XML(文本)编辑器手动编辑应用包清单 XML。 Right-click the Package.appxmanifest file in Solution Explorer for editing options.

This example shows the print task settings extension in the Extension element, as it appears in the app package manifest file, Package.appxmanifest.

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">
  <Identity Name="Microsoft.SDKSamples.DeviceAppForPrinters.CS" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="1.0.0.0" />
  <Properties>
    <DisplayName>Device App For Printers C# sample</DisplayName>
    <PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
    <Logo>Assets\storeLogo-sdk.png</Logo>
  </Properties>
  <Prerequisites>
    <OSMinVersion>6.3.0</OSMinVersion>
    <OSMaxVersionTested>6.3.0</OSMaxVersionTested>
  </Prerequisites>
  <Resources>
    <Resource Language="x-generate" />
  </Resources>
  <Applications>
    <Application Id="DeviceAppForPrinters" Executable="$targetnametoken$.exe" EntryPoint="DeviceAppForPrinters.App">
      <VisualElements DisplayName="Device App For Printers C# sample" Logo="Assets\squareTile-sdk.png" SmallLogo="Assets\smallTile-sdk.png" Description="DeviceAppForPrinters C# sample" ForegroundText="light" BackgroundColor="#00b2f0" ToastCapable="true">
<DefaultTile ShowName="allLogos" ShortName="App4PrinterCS" WideLogo="Assets\tile-sdk.png" />
<SplashScreen Image="Assets\splash-sdk.png" BackgroundColor="#00b2f0" />
      </VisualElements>
      <Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTask.PrintBackgroundTask">
  <BackgroundTasks>
    <Task Type="systemEvent" />
  </BackgroundTasks>
</Extension>
<Extension Category="windows.printTaskSettings" Executable="$targetnametoken$.exe" EntryPoint="DeviceAppForPrinters.App" />
      </Extensions>
    </Application>
  </Applications>
</Package>

步骤 2:生成 UI

在构建应用之前,应与设计人员和营销团队合作,设计用户体验。 用户体验应投影公司的品牌方面,并帮助你与用户建立连接。

Design guidelines

在设计自定义浮出控件之前,请务必查看 UWP 应用浮出控件指南 。 这些指南有助于确保浮出控件提供与其他 UWP 应用一致的直观体验。

对于应用的主页,请记住,Windows 8.1 可以在单个监视器上以各种大小显示多个应用。 请参阅以下指南,详细了解应用如何在屏幕大小、窗口大小和方向之间正常重排。

Flyout dimensions

显示高级打印设置的浮出控件宽为 646 像素,高度至少为 768 像素(实际高度取决于用户的屏幕分辨率)。 浮出控件标题区域中的“后退”按钮由 Windows 提供。 “应用标题”文本是应用清单中的应用标题。 游戏区域高 80 像素,为自定义浮出控件的可查看区域留下 688 像素。

高级打印机设置的浮出控件尺寸。

Note

如果自定义浮出控件的高度超过 688 像素,用户可以滑动或滚动以查看显示在可查看区域上方或下方的浮出控件部分。

定义应用标题颜色和图标

标题、背景色、文本颜色和自定义浮出控件上的小徽标取自 VisualElements 应用包清单文件中的元素。

This example shows the title and icon, as defined in the VisualElements element, in the app package manifest file (Package.appxmanifest).

      <VisualElements DisplayName="Device App For Printers C# sample" Logo="Assets\squareTile-sdk.png" SmallLogo="Assets\smallTile-sdk.png" Description="DeviceAppForPrinters C# sample" ForegroundText="light" BackgroundColor="#00b2f0" ToastCapable="true">
        <DefaultTile ShowName="allLogos" ShortName="App4PrinterCS" WideLogo="Assets\tile-sdk.png" />
        <SplashScreen Image="Assets\splash-sdk.png" BackgroundColor="#00b2f0" />
      </VisualElements>

Best practices

  • 保持相同的外观。 将自定义浮出控件与“开始”体验(应用的主页)的设计对齐,包括字体、颜色和控件等元素。 无论用户从何处调用应用,应用都应该对用户感到熟悉。

  • 保持交互简单。 避免耗时或复杂的交互。 在大多数情况下,最好在“开始”体验中完成设置打印机、查看状态、订购墨迹和故障排除等作。

  • 将导航保持在最低水平。 避免让用户在自定义浮出控件中的多个页面之间来回导航。 请改用垂直滚动或内联控件,例如渐进式披露控件、下拉列表和内联错误消息。

  • 不要使用浅色消除浮出控件。 打印体验已使用浅色消除浮出控件。 在自定义浮出控件中包含另一个轻型消除元素可能会使用户感到困惑。

  • 禁用使用户远离打印体验的链接。 当用户正在打印内容时,应采取措施确保它们保留在打印上下文中。 例如,如果你的应用具有指向应用其他区域的链接(例如主页或用于购买墨迹的页面),则应禁用它们,以便用户不会意外地离开高级打印设置体验。

步骤 3:处理激活

如果应用已声明打印任务设置扩展,它必须实现用于 OnActivated 处理应用激活事件的方法。 应用激活是在应用启动时,应用可以选择启动哪个页面。 对于已声明打印任务设置扩展的应用,Windows 会在 Activated 事件参数中传递打印任务扩展上下文:Windows.ApplicationModel.Activation.IActivatedEventArgs。

A UWP device app can determine that the activation is intended for advanced print settings (that someone just tapped More options on the print settings dialog) when the event argument's kind property is equal to Windows.ApplicationModel.Activation.ActivationKind.printTaskSettings.

Note

在某些情况下,如果用户在启动应用后立即关闭应用,可能会在激活处理程序内引发异常。 若要避免这种情况,请确保激活处理程序高效完成,并且不会执行资源密集型处理。

This example shows the activation event handler in the OnActivated method, as it appears in the Constants.cs file. 然后,事件参数将强制转换为 Windows.ApplicationModel.Activation.PrintTaskSettingsActivatedEventArgs。 Although the sample includes this code in the Constants.cs file, it's actually part of the App class that is also defined in the App.xaml.cs file.

partial class App : Application
{
    protected override void OnActivated(IActivatedEventArgs args)
    {
        if (args.Kind == ActivationKind.PrintTaskSettings)
        {
            Frame rootFrame = new Frame();
            if (null == Window.Current.Content)
            {
                rootFrame.Navigate(typeof(MainPage));
                Window.Current.Content = rootFrame;
            }
            Window.Current.Activate();

            MainPage mainPage = (MainPage)rootFrame.Content;

            // Load advanced printer preferences scenario
            mainPage.LoadAdvancedPrintSettingsContext((PrintTaskSettingsActivatedEventArgs)args);
        }
    }
}

步骤 4:显示设置

LoadAdvancedPrintSettingsContext调用该方法时,将打印任务配置上下文分配给 MainPage 类的变量。 这将允许自定义浮出控件在启动时访问打印设置。

传递给 LoadAdvancedPrintSettingsContext 该方法的事件参数公开用于访问和控制打印机的属性:

  • The args.configuration property provides an object of type Windows.Devices.Printers.Extensions.PrintTaskConfiguration. 此对象提供对打印任务扩展上下文的访问权限,还可以添加事件处理程序来更新打印票证。
  • The args.configuration.printerExtensionContext property provides an object of type Windows.Devices.Printers.Extensions.PrinterExtensionContext. 此对象是指向 Print Schema、PrintTicket 和打印队列信息的 PrinterExtensionLibrary 接口的指针。 如果未公开任何接口,则为 null。 有关详细信息,请参阅 打印机扩展库概述

This example shows the LoadAdvancedPrintSettingsContext method, as it appears in the Constants.cs file.

public PrintTaskConfiguration Config;
public Object Context;

public void LoadAdvancedPrintSettingsContext(PrintTaskSettingsActivatedEventArgs args)
{
    Config = args.Configuration;
    Context = Config.PrinterExtensionContext;
    LoadScenario(typeof(DeviceAppForPrinters.Preferences));
}

On the custom flyout page, Preferences.xaml.cs, a class named rootPage acts as a pointer to the MainPage class so that the print task extension context and the printer device context can be accessed from the flyout.

This example shows the pointer in a portion of Preferences class, from the Preferences.xaml.cs file. 下载 “打印设置”和“打印通知” 示例以查看完整代码。

public sealed partial class Preferences : SDKTemplate.Common.LayoutAwarePage
{
    // A pointer back to the main page.  
    MainPage rootPage = MainPage.Current;

    // To listen for save requests.
    PrintTaskConfiguration configuration;

    // To create the printer device context.
    Object printerExtensionContext;
    PrintHelperClass printHelper;

    // The features in this sample were chosen because they're available on a wide range of printer drivers.
    private string[] features = { "PageOrientation", "PageOutputColor", "PageMediaSize", "PageMediaType" };
    private string[] selections = { null, null, null, null };

    // . . .
    // . . .
    // . . .

When the page constructor for Preferences.xaml.cs is called, objects are created for the print task extension context (a PrintTaskConfiguration object named configuration) and the printer device context (a PrintHelperClass object named printHelper).

创建这些对象后,打印机设备上下文用于 DisplaySettings 加载 TextBlocks 和 ComboBoxes 的方法。 请注意,与 JavaScript 不同,选择中的更改不会在与应用的其余线程上触发。 必须保留用户选择的本地缓存,以供以后使用。

This example shows the custom flyout page constructor, DisplaySettings, and other helper methods in the Preferences.xaml.cs file.

public Preferences()
{
    this.InitializeComponent();

    configuration = rootPage.Config;
    printerExtensionContext = rootPage.Context;
    printHelper = new PrintHelperClass(printerExtensionContext);

    // Disable scenario navigation by hiding the scenario list UI elements
    ((UIElement)rootPage.FindName("Scenarios")).Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    ((UIElement)rootPage.FindName("ScenarioListLabel")).Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    ((UIElement)rootPage.FindName("DescriptionText")).Visibility = Windows.UI.Xaml.Visibility.Collapsed;

    DisplaySettings();
}


private void DisplaySettings(bool constraints=false)
{
    PrintOptions.Visibility = Windows.UI.Xaml.Visibility.Visible;
    WaitPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

    // Fill in the drop-down select controls for some common printing features.
    TextBlock[] featureLabels = { PageOrientationLabel, PageOutputColorLabel, PageMediaSizeLabel, PageMediaTypeLabel };
    ComboBox[] featureBoxes = { PageOrientationBox, PageOutputColorBox, PageMediaSizeBox, PageMediaTypeBox };

    for (int i = 0; i < features.Length; i++)
    {
        // Only display a feature if it exists
        featureLabels[i].Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        featureBoxes[i].Visibility = Windows.UI.Xaml.Visibility.Collapsed;

        string feature = features[i];

        // Check whether the currently selected printer's capabilities include this feature.
        if (!printHelper.FeatureExists(feature))
        {
            continue;
        }

        // Fill in the labels so that they display the display name of each feature.
        featureLabels[i].Text = printHelper.GetFeatureDisplayName(feature);
        string[] index = printHelper.GetOptionInfo(feature, "Index");
        string[] displayName = printHelper.GetOptionInfo(feature, "DisplayName");
        string selectedOption = printHelper.GetSelectedOptionIndex(feature);

        // Unless specified, do not get constraints
        bool[] constrainedList = constraints ? printHelper.GetOptionConstraints(feature) : new bool[index.Length];

        // Populate the combo box with the options for the current feature.
        PopulateBox(featureBoxes[i], index, displayName, selectedOption, constrainedList);
        selections[i] = selectedOption;

        // Every time the selection for a feature changes, we update our local cached set of selections.
        featureBoxes[i].SelectionChanged += OnFeatureOptionsChanged;

        // Show existing features
        featureLabels[i].Visibility = Windows.UI.Xaml.Visibility.Visible;
        featureBoxes[i].Visibility = Windows.UI.Xaml.Visibility.Visible;
    }
}

void PopulateBox(ComboBox box, string[] index, string[] displayName, string selectedOption, bool[] constrainedList)
{
    // Clear the combobox of any options from previous UI refresh before repopulating it.
    box.SelectionChanged -= OnFeatureOptionsChanged;
    box.Items.Clear();
    // There should be only one displayName for each possible option.
    if (index.Length == displayName.Length)
    {
        for (int i = 0; i < index.Length; i++)
        {
            // Create a new DisplayItem so the user will see the friendly displayName instead of the index.
            ComboBoxItem newItem = new ComboBoxItem();
            newItem.Content = displayName[i];
            newItem.DataContext = index[i];
            newItem.Foreground = constrainedList[i] ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Black);
            box.Items.Add(newItem);

            // Display current selected option as selected in the combo box.
            if (selectedOption == index[i])
            {
                box.SelectedIndex = i;
                box.Foreground = newItem.Foreground;
            }
        }
    }
}

private void OnFeatureOptionsChanged(object sender, SelectionChangedEventArgs args)
{
    ComboBox comboBox = sender as ComboBox;

    for (int i = 0; i < features.Length; i++)
    {
        if (features[i] + "Box" == comboBox.Name)
        {
            selections[i] = (comboBox.SelectedItem as ComboBoxItem).DataContext as string;
        }
    }
}

步骤 5:保存设置

When the user has finished setting advanced print settings, the Microsoft Store device app needs to save the changes before the user goes back to the Print window. To do that, the app needs to listen for when the user taps the Back button (from the custom flyout page). 发生这种情况时, SaveRequested 将触发打印任务扩展上下文(对象 configuration )的事件。

This example shows the event listener for SaveRequested, being added in the OnNavigatedTo event handler of the custom flyout, in the Preferences.xaml.cs file. When the SaveRequested event is triggered, the OnSaveRequested method will be invoked (that method is also in the Preferences.xaml.cs file).

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (null == configuration)
    {
        rootPage.NotifyUser("Configuration arguments cannot be null", NotifyType.ErrorMessage);
        return;
    }

    // Add an event listener for saverequested (the back button of the flyout is pressed).
    configuration.SaveRequested += OnSaveRequested;
}

OnSaveRequested 方法中,应用首先使用 printHelper 对象为打印机扩展上下文上的每项功能设置当前选定的选项。 然后,它会对作为参数OnSaveRequested传入的对象调用Saverequest该方法。 该方法 Save 来自 Windows.Devices.Printers.Extensions.PrintTaskConfigurationSaveRequest 类,使用打印机扩展上下文验证打印票证并保存打印任务配置。

Important

如果打印票证以任何方式无效,该方法 Save 将引发应用必须处理的异常。 如果应用未处理异常,则会停止流,迫使用户轻视浮出控件并重启打印流。

This example shows the OnSaveRequested method in the Preferences.xaml.cs file. SaveRequested由于该事件未在 UI 线程上引发,因此它需要使用 Windows.UI.Core.CoreDispatcher 将消息发布到 UI 线程,以在验证和保存票证时显示相应的消息。

async private void OnSaveRequested(object sender, PrintTaskConfigurationSaveRequestedEventArgs args)
{
    if (null == printHelper || null == printerExtensionContext || null == args)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            rootPage.NotifyUser("onSaveRequested: args, printHelper, and context cannot be null", NotifyType.ErrorMessage);
        });
        return;
    }

    // Get the request object, which has the save method that allows saving updated print settings.
    PrintTaskConfigurationSaveRequest request = args.Request;

    if (null == request)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            rootPage.NotifyUser("onSaveRequested: request cannot be null", NotifyType.ErrorMessage);
        });
        return;
    }

    PrintTaskConfigurationSaveRequestedDeferral deferral = request.GetDeferral();

    // Two separate messages are dispatched to:
    // 1) put up a popup panel,
    // 2) set the each options to the print ticket and attempt to save it,
    // 3) tear down the popup panel if the print ticket could not be saved.
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        PrintOptions.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        WaitPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
    });

    // Go through all the feature select elements, look up the selected
    // option name, and update the context
    // for each feature
    for (var i = 0; i < features.Length; i++)
    {
        // Set the feature's selected option in the context's print ticket.
        // The printerExtensionContext object is updated with each iteration of this loop
        printHelper.SetFeatureOption(features[i], selections[i]);
    }

    bool ticketSaved;
    try
    {
        // This save request will throw an exception if ticket validation fails.
        // When the exception is thrown, the app flyout will remain.
        // If you want the flyout to remain regardless of outcome, you can call
        // request.Cancel(). This should be used sparingly, however, as it could
        // disrupt the entire the print flow and will force the user to
        // light dismiss to restart the entire experience.
        request.Save(printerExtensionContext);

        if (configuration != null)
        {
            configuration.SaveRequested -= OnSaveRequested;
        }
        ticketSaved = true;
    }
    catch (Exception exp)
    {
        // Check if the HResult from the exception is from an invalid ticket, otherwise rethrow the exception
        if (exp.HResult.Equals(unchecked((int)0x8007000D))) // E_INVALID_DATA
        {
            ticketSaved = false;
        }
        else
        {
            throw;
        }
    }

    // If ticket isn't saved, refresh UI and notify user
    if (!ticketSaved)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            rootPage.NotifyUser("Failed to save the print ticket", NotifyType.ErrorMessage);
            DisplaySettings(true);
        });
    }
    deferral.Complete();
}

保存需要用户输入的选项

“打印设置”和“打印通知”示例演示了如何设置定义的功能,这些功能涵盖大多数打印选项。 但是,某些选项需要自定义 UI 才能获取用户指定的值。 例如,如果应用使用高级打印设置来指定自定义页面大小,则会执行以下步骤来保存用户指定的值:

  1. 在应用激活期间检索打印票证。 步骤 3:处理激活之前介绍了打印设置的应用激活。

  2. 检查是否指定了页面大小选项。 在 C# 或 JS 应用中,打印帮助程序类可以检查此选项。 在C++应用中,调用 IPrintSchemaOption 上的 QueryInterface 以检索 IPrintSchemaPageMediaSizeOption。

    此示例演示打印帮助程序类中的一种方法,用于检查是否指定了页面大小选项。

    public bool ShouldShowCustomUI(string index)
    {
        if (null != index)
        {
            string feature = "PageMediaSize";
            int i = int.Parse(index);
            IPrintSchemaOption selectedOption = GetCachedFeatureOptions(feature)[i];
            if (selectedOption.Name.Equals("CustomMediaSize", StringComparison.CurrentCulture)
                || selectedOption.Name.Equals("PSCustomMediaSize", StringComparison.CurrentCulture))
            {
                return true;
            }
        }
        return false;
    }
    
  3. 在自定义浮出控件中,显示一个自定义 UI,要求用户输入页面高度和宽度,并从 IPrintSchemaPageMediaSizeOption 检索用户指定的高度和宽度。

    此示例显示了一个自定义浮出控件的方法,该浮出控件要求用户输入页面高度和宽度。

    private void ShowCustomPageMediaSizeUI(string index, bool keepValue)
    {
        //Hide custom media size UI unless needed
        if (IsCustomSizeSelected(index))
        {
           if (keepValue && (!customWidth.Equals("")) && (!customHeight.Equals("")))
           {
                        CustomWidthBox.Text = customWidth;
                        CustomHeightBox.Text = customHeight;
           }
           else
           {
              // Use a helper function from the WinRT helper component
              CustomWidthBox.Text = printHelper.GetCustomWidth(index);
              CustomHeightBox.Text = printHelper.GetCustomHeight(index);
           }
           CustomUIPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
           CustomWidthBox.KeyDown += OnCustomValueEntered;
           CustomHeightBox.KeyDown += OnCustomValueEntered;
        }
        else
        {
           CustomUIPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
           CustomWidthBox.KeyDown -= OnCustomValueEntered;
           CustomHeightBox.KeyDown -= OnCustomValueEntered;
        }
    }
    
  4. 使用 IPrintSchemaPageMediaSizeOption 用户指定的值更新对象,并验证高度和宽度是否与用户指定的值匹配。

    此示例是用于更新 IPrintSchemaPageMediaSizeOption 打印机帮助程序类中的对象的帮助程序方法。 OnSaveRequested如果自定义浮出控件中的处理程序确定请求了自定义页面大小选项,则会调用此函数。

    public void SetCustomMediaSizeDimensions(string width, string height)
    {
      if ((null == width) && (null == height) && (null == Capabilities))
      {
                    return;
      }
      try
      {
                    CheckSizeValidity(width, height);
      }
      catch (FormatException e)
      {
                    throw new ArgumentException(e.Message);
      }
      catch (OverflowException e)
      {
                    throw new ArgumentException(e.Message);
      }
    
      // The context is retrieved during app activation.
      IPrintSchemaTicket ticket = context.Ticket;
    
      //
      // Input XML as Stream
      //
      XElement ticketRootXElement = null;
      using (Stream ticketReadStream = ticket.GetReadStream())
      {
         ticketRootXElement = XElement.Load(ticketReadStream);
      }
    
      XNamespace psfNs = PrintSchemaConstants.FrameworkNamespaceUri;
      XNamespace pskNs = PrintSchemaConstants.KeywordsNamespaceUri;
      string pskPrefix = ticketRootXElement.GetPrefixOfNamespace(pskNs);
    
      // Modify the MediaSizeHeight and MediaSizeWidth
      IEnumerable<XElement> parameterInitCollection =
        from c in ticketRootXElement.Elements(psfNs + "ParameterInit")
    
      select c;
    
      foreach (XElement parameterInit in parameterInitCollection)
      {
        if (0 == String.Compare((string)parameterInit.Attribute("name"), pskPrefix + ":PageMediaSizePSWidth"))
        {
          IEnumerable<XElement> valueCollection = from c in parameterInit.Elements(psfNs + "Value")
          select c;
          valueCollection.ElementAt(0).Value = width;
        }
    
         else if (0 == String.Compare((string)parameterInit.Attribute("name"), pskPrefix + ":PageMediaSizePSHeight"))
        {
          IEnumerable<XElement> valueCollection = from c in parameterInit.Elements(psfNs + "Value")
          select c;
          valueCollection.ElementAt(0).Value = height;
         }
      }
    
      //
      // Write XLinq changes back to DOM
      //
       using (Stream ticketWriteStream = ticket.GetWriteStream())
       {
         ticketRootXElement.Save(ticketWriteStream);
       }
    }
    

Testing

在测试 UWP 设备应用之前,必须使用设备元数据将其链接到打印机。

  • 需要打印机的设备元数据包的副本,才能将设备应用信息添加到其中。 如果没有设备元数据,可以使用 设备元数据创作向导 生成它,如主题 “为 UWP 设备应用创建设备元数据”中所述。

    Note

    若要使用 设备元数据创作向导,必须先安装 Microsoft Visual Studio Professional、Microsoft Visual Studio Ultimate 或 适用于 Windows 8.1 的独立 SDK,然后才能完成本主题中的步骤。 安装 Microsoft Visual Studio Express for Windows 会安装不包含向导的 SDK 版本。

以下步骤生成应用并安装设备元数据。

  1. 启用测试签名。

    1. 通过双击DeviceMetadataWizard.exe,从 %ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 启动设备元数据创作向导

    2. From the Tools menu, select Enable Test Signing.

  2. 重新启动计算机

  3. 通过打开解决方案(.sln)文件生成解决方案。 按 F7 或在示例加载后从顶部菜单中转到 生成>解决方案

  4. 断开连接并卸载打印机。 此步骤是必需的,以便 Windows 将在下次检测到设备时读取更新的设备元数据。

  5. 编辑和保存设备元数据。 若要将设备应用链接到设备,必须将设备应用与设备相关联

    Note

    如果尚未创建设备元数据,请参阅 为 UWP 设备应用创建设备元数据

    1. 如果设备元数据创作向导尚未打开,请双击 DeviceMetadataWizard.exe从%ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 启动它。

    2. 单击“ 编辑设备元数据”。 这样,就可以编辑现有的设备元数据包。

    3. In the Open dialog box, locate the device metadata package associated with your UWP device app. (It has a devicemetadata-ms file extension.)

    4. “指定 UWP 设备应用信息 ”页上,在 “UWP 设备应用 ”框中输入Microsoft应用商店应用信息。 单击“ 导入 UWP 应用清单”文件 ,自动输入 包名称发布服务器名称和UWP 应用 ID

    5. If your app is registering for printer notifications, fill out the Notification handlers box. In Event ID, enter the name of the print event handler. In Event Asset, enter the name of the file where that code resides.

    6. When you're done, click Next until you get to the Finish page.

    7. 在“ 查看设备元数据包 ”页上,确保所有设置都正确,并选择“ 将设备元数据包复制到本地计算机上的元数据存储 ”复选框。 Then click Save.

  6. 重新连接打印机,以便 Windows 在设备连接时读取更新的设备元数据。

Troubleshooting

问题:高级打印设置显示默认浮出控件而不是自定义浮出控件

如果高级打印设置浮出控件显示默认浮出控件,而不是应用实现的自定义浮出控件...

  • Possible cause: Test signing is not turned on. 有关打开调试的信息,请参阅本主题中的“调试”部分。

  • Possible cause: The app is not querying for the right Package Family Name. 在代码中检查包系列名称。 Open up package.appxmanifest in Visual Studio and make sure that the package family name you are querying for matches the one in the Packaging tab, in the Package Family Name field.

  • Possible cause: The device metadata is not associated with the Package Family Name. 使用 设备元数据创作向导 打开设备元数据并检查包系列名称。 Start the wizard from %ProgramFiles(x86)%\Windows Kits\8.1\bin\x86, by double-clicking DeviceMetadataWizard.exe.

问题:应用在浮出控件中启动,然后立即消除

如果高级打印设置的自定义浮出控件在启动后立即消失...

  • Possible cause: In Windows 8, there is a known issue that within a flyout, UWP apps will be dismissed under the debugger. 知道激活有效后,请关闭调试。 如果需要调试保存打印票证,在激活后附加调试器。