透過使用列印工作流程應用程式建立自定義列印工作流程體驗。
概觀
列印工作流程應用程式是 UWP 應用程式,它擴展了 Microsoft Store 裝置應用程式(WSDAs)的功能,因此在進一步了解之前,對 WSDAs 有所熟悉會很有幫助。
就像 WSDA 的情況一樣,當來源應用程式的使用者選擇列印某個專案並瀏覽印表對話框時,系統會檢查工作流程應用程式是否與該印表機相關聯。 如果是,列印工作流程應用程式會啟動(主要是作為背景工作,稍後介紹)。 工作流程應用程式能夠改變列印票證(用於設定目前列印工作的印表機裝置設置的 XML 文件),以及要列印的實際 XPS 內容。 它可以選擇性地透過程式中途啟動UI,向用戶公開這項功能。 執行完工作後,它會將列印內容和列印票證傳遞給驅動程式。
因為它牽涉到背景和前景元件,而且因為它的功能與其他應用程式結合,所以列印工作流程應用程式可以比其他 UWP app 類別更複雜。 建議您在閱讀本指南時檢查 工作流程應用程式範例,以進一步瞭解如何實作不同的功能。 為了簡單起見,本指南中缺少一些功能,例如各種錯誤檢查和UI管理。
入門指南
工作流程應用程式必須指出其進入點至列印系統,才能在適當的時間啟動。 這可藉由在 UWP 專案 Application/Extensions
檔案的 元素中插入下列宣告來完成。
<uap:Extension Category="windows.printWorkflowBackgroundTask"
EntryPoint="WFBackgroundTasks.WfBackgroundTask" />
這很重要
有許多案例顯示列印自定義不需要用戶輸入。 因此,根據預設,列印工作流程應用程式會以背景工作的形式執行。
如果工作流程應用程式與啟動列印作業的來源應用程式相關聯(如需相關指示,請參閱稍後的章節),列印系統會檢查其指令清單檔案中是否有背景工作進入點。
在列印票證上執行背景工作
列印系統對工作流程應用程式所做的第一件事是啟動其背景任務(在此情況下,WfBackgroundTask
命名空間中的 WFBackgroundTasks
類別)。 在背景工作的 Run
方法中,您應該將工作的觸發程式詳細數據轉換成 PrintWorkflowTriggerDetails 實例。 這將為列印工作流程背景任務提供特殊功能。 它會公開 PrintWorkflowSession 屬性,這是 PrintWorkFlowBackgroundSession的實例。 列印工作流程會話類別——無論是背景種類還是前景種類——將控制列印工作流程應用程式的循序步驟。
然後,為這個會話類別所引發的兩個事件註冊處理程式方法。 您稍後會定義這些方法。
public void Run(IBackgroundTaskInstance taskInstance) {
// Take out a deferral here and complete once all the callbacks are done
runDeferral = taskInstance.GetDeferral();
// Associate a cancellation handler with the background task.
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
// cast the task's trigger details as PrintWorkflowTriggerDetails
PrintWorkflowTriggerDetails workflowTriggerDetails = taskInstance.TriggerDetails as PrintWorkflowTriggerDetails;
// Get the session manager, which is unique to this print job
PrintWorkflowBackgroundSession sessionManager = workflowTriggerDetails.PrintWorkflowSession;
// add the event handler callback routines
sessionManager.SetupRequested += OnSetupRequested;
sessionManager.Submitted += OnXpsOMPrintSubmitted;
// Allow the event source to start
// This call blocks until all of the workflow callbacks complete
sessionManager.Start();
}
呼叫 Start
方法時,會話管理員會先引發 SetupRequested 事件。 此事件會公開列印工作的一般資訊,以及列印票證。 在這個階段,列印票證可以在背景中編輯。
private void OnSetupRequested(PrintWorkflowBackgroundSession sessionManager, PrintWorkflowBackgroundSetupRequestedEventArgs printTaskSetupArgs) {
// Take out a deferral here and complete once all the callbacks are done
Deferral setupRequestedDeferral = printTaskSetupArgs.GetDeferral();
// Get general information about the source application, print job title, and session ID
string sourceApplicationName = printTaskSetupArgs.Configuration.SourceAppDisplayName;
string jobTitle = printTaskSetupArgs.Configuration.JobTitle;
string sessionId = printTaskSetupArgs.Configuration.SessionId;
// edit the print ticket
WorkflowPrintTicket printTicket = printTaskSetupArgs.GetUserPrintTicketAsync();
// ...
重要的是,在處理 SetupRequested 時,應用程式會判斷是否要啟動前景元件。 這可能取決於先前儲存至本機記憶體的設定,或列印票證編輯期間發生的事件,或可能是特定應用程式的靜態設定。
// ...
if (UIrequested) {
printTaskSetupArgs.SetRequiresUI();
// Any data that is to be passed to the foreground task must be stored the app's local storage.
// It should be prefixed with the sourceApplicationName string and the SessionId string, so that
// it can be identified as pertaining to this workflow app session.
}
// Complete the deferral taken out at the start of OnSetupRequested
setupRequestedDeferral.Complete();
在列印工作上執行前景工作 (選擇性)
如果呼叫 SetRequiresUI 方法,列印系統會檢查資訊清單文件以尋找前景應用程式的進入點。
Application/Extensions
檔案的 元素必須具有下列幾行。 將 EntryPoint
的值替換成前景應用程式的名稱。
<uap:Extension Category="windows.printWorkflowForegroundTask"
EntryPoint="MyWorkFlowForegroundApp.App" />
接下來,印刷系統會針對指定的應用程式入口點呼叫 OnActivated 方法。 在其 App.xaml.cs 檔案的 OnActivated 方法中,工作流程應用程式應檢查啟用種類,以確認這是工作流程啟用。 如果是,工作流程應用程式可以將啟用自變數轉換成 PrintWorkflowUIActivatedEventArgs 物件,此物件會將 PrintWorkflowForegroundSession 對象作為屬性公開。 這個物件,就像上一節的背景對應項目一樣,包含列印系統所引發的事件,而且您可以將處理程式指派給這些事件。 在此情況下,事件處理功能將會在稱為 WorkflowPage
的個別類別中實作。
首先,在 App.xaml.cs 檔案中:
protected override void OnActivated(IActivatedEventArgs args){
if (args.Kind == ActivationKind.PrintWorkflowForegroundTask) {
// the app should instantiate a new UI view so that it can properly handle the case when
// several print jobs are active at the same time.
Frame rootFrame = new Frame();
if (null == Window.Current.Content)
{
rootFrame.Navigate(typeof(WorkflowPage));
Window.Current.Content = rootFrame;
}
// Get the main page
WorkflowPage workflowPage = (WorkflowPage)rootFrame.Content;
// Make sure the page knows it's handling a foreground task activation
workflowPage.LaunchType = WorkflowPage.WorkflowPageLaunchType.ForegroundTask;
// Get the activation arguments
PrintWorkflowUIActivatedEventArgs printTaskUIEventArgs = args as PrintWorkflowUIActivatedEventArgs;
// Get the session manager
PrintWorkflowForegroundSession taskSessionManager = printTaskUIEventArgs.PrintWorkflowSession;
// Add the callback handlers - these methods are in the workflowPage class
taskSessionManager.SetupRequested += workflowPage.OnSetupRequested;
taskSessionManager.XpsDataAvailable += workflowPage.OnXpsDataAvailable;
// start raising the print workflow events
taskSessionManager.Start();
}
}
一旦 UI 附加事件處理程式,且 OnActivated 方法已結束,列印系統就會引發 SetupRequested 事件,讓 UI 進行處理。 此事件提供背景工作設定事件所提供的相同數據,包括列印作業資訊和列印票證檔,但無法要求啟動其他UI。 在 WorkflowPage.xaml.cs 檔案中:
internal void OnSetupRequested(PrintWorkflowForegroundSession sessionManager, PrintWorkflowForegroundSetupRequestedEventArgs printTaskSetupArgs) {
// If anything asynchronous is going to be done, you need to take out a deferral here,
// since otherwise the next callback happens once this one exits, which may be premature
Deferral setupRequestedDeferral = printTaskSetupArgs.GetDeferral();
// Get information about the source application, print job title, and session ID
string sourceApplicationName = printTaskSetupArgs.Configuration.SourceAppDisplayName;
string jobTitle = printTaskSetupArgs.Configuration.JobTitle;
string sessionId = printTaskSetupArgs.Configuration.SessionId;
// the following string should be used when storing data that pertains to this workflow session
// (such as user input data that is meant to change the print content later on)
string localStorageVariablePrefix = string.Format("{0}::{1}::", sourceApplicationName, sessionID);
try
{
// receive and store user input
// ...
}
catch (Exception ex)
{
string errorMessage = ex.Message;
Debug.WriteLine(errorMessage);
}
finally
{
// Complete the deferral taken out at the start of OnSetupRequested
setupRequestedDeferral.Complete();
}
}
接下來,列印系統會為UI觸發 XpsDataAvailable 事件。 在此事件的處理程式中,工作流程應用程式可以存取安裝程式事件可用的所有數據,並且可以直接以原始位元組數據流或物件模型的形式讀取 XPS 數據。 存取 XPS 數據可讓 UI 提供列印預覽服務,以及為使用者提供工作流程應用程式對數據執行之作業的其他資訊。
在此事件處理程式中,如果工作流程應用程式會繼續與用戶互動,就必須取得延遲物件。 如果沒有延遲,則當 XpsDataAvailable 事件處理程序結束時或呼叫非同步方法時,列印系統會認為 UI 工作已完成。 當應用程式從使用者與UI的互動中收集所有必要的資訊時,它應該完成延遲,以便列印系統可以繼續進行。
internal async void OnXpsDataAvailable(PrintWorkflowForegroundSession sessionManager, PrintWorkflowXpsDataAvailableEventArgs printTaskXpsAvailableEventArgs)
{
// Take out a deferral
Deferral xpsDataAvailableDeferral = printTaskXpsAvailableEventArgs.GetDeferral();
SpoolStreamContent xpsStream = printTaskXpsAvailableEventArgs.Operation.XpsContent.GetSourceSpoolDataAsStreamContent();
IInputStream inputStream = xpsStream.GetInputSpoolStream();
using (var inputReader = new Windows.Storage.Streams.DataReader(inputStream))
{
// Read the XPS data from input stream
byte[] xpsData = new byte[inputReader.UnconsumedBufferLength];
while (inputReader.UnconsumedBufferLength > 0)
{
inputReader.ReadBytes(xpsData);
// Do something with the XPS data, e.g. preview
// ...
}
}
// Complete the deferral taken out at the start of this method
xpsDataAvailableDeferral.Complete();
}
此外,由事件參數所公開的 PrintWorkflowSubmittedOperation 實例提供了一個選項,可以取消列印作業,或是指出作業成功但不需要執行輸出列印作業。 做法是透過將 Complete 方法與 PrintWorkflowSubmittedStatus 值一同呼叫來完成。
備註
如果工作流程應用程式取消列印作業,強烈建議它提供快顯通知,以指出作業取消的原因。
對列印內容執行最終背景工作
一旦 UI 在 PrintTaskXpsDataAvailable 事件中完成延遲(或略過 UI 步驟),列印系統就會為背景任務觸發 提交 事件。 在此事件的處理程式中,工作流程應用程式可以存取 XpsDataAvailable 事件所提供的所有相同數據。 不過,與先前任何事件不同,送出 也會 透過 PrintWorkflowTarget 實例,提供最終列印作業內容的寫入 存取權。
用來卷軸處理最終列印資料的物件,取決於源資料是以原始位元組串流存取或在 XPS 物件模型中存取。 當工作流程應用程式透過位元組資料流存取源資料時,會提供輸出位元組數據流來寫入最終作業數據。 當工作流程應用程式透過物件模型存取源數據時,會提供檔寫入器以將物件寫入輸出作業。 不論是哪一種情況,工作流程應用程式都應該讀取所有源數據、修改所需的任何數據,並將修改過的數據寫入輸出目標。
當背景工作完成寫入數據時,它應該在對應的 PrintWorkflowSubmittedOperation 物件上呼叫 Complete。 工作流程應用程式完成此步驟並結束 送出 事件處理程序之後,工作流程會話就會關閉,而且使用者可以透過標準列印對話框監視最終列印作業的狀態。
最終步驟
向印表機註冊列印工作流程應用程式
您的工作流程應用程式會使用與 WSDA 相同的元數據檔案提交類型與印表機相關聯。 事實上,單一 UWP 應用程式可以同時作為工作流程應用程式和 WSDA,以提供列印工作設定功能。 請遵循對應的 WSDA 步驟來建立元數據關聯。
差別在於,雖然 WSDA 會自動為使用者啟用(當該使用者在相關聯的裝置上列印時,應用程式會自動啟動),但工作流程應用程式則不會自動啟用。 它們有必須設定的個別原則。
設定工作流程應用程式的原則
工作流程應用程式原則是由要執行工作流程應用程式的裝置上的Powershell命令所設定。 Set-Printer、Add-Printer(現有埠)和 Add-Printer (新的 WSD 埠) 命令將會修改為允許設定工作流程原則。
-
Disabled
:工作流程應用程式將不會啟動。 -
Uninitialized
:如果工作流程 DCA 安裝在系統中,工作流程應用程式將會啟動。 如果未安裝應用程式,列印仍會繼續進行。 -
Enabled
:如果工作流程 DCA 安裝在系統中,則會啟動工作流程合約。 如果未安裝應用程式,列印將會失敗。
下列命令會使工作流程應用程式成為指定印表機上的必需品。
Set-Printer –Name "Microsoft XPS Document Writer" -WorkflowPolicy Enabled
本機用戶可以在本機印表機上執行此原則,或針對企業實作,印表機管理員可以在列印伺服器上執行此原則。 政策將同步到所有用戶端連線。 每當新增印表機時,印表機系統管理員可以使用此原則。