自訂列印工作流程
透過使用列印工作流程應用程式建立自訂列印工作流程體驗。
概觀
列印工作流程應用程式是擴展了 Microsoft Store 裝置應用程式 (WSDA) 功能的 UWP 應用程式,因此在進一步了解之前熟悉 WSDA 將會很有幫助。
正如 WSDA 的情況一樣,當來源應用程式的使用者選擇列印某些內容並瀏覽列印對話方塊時,系統會檢查工作流程應用程式是否與該印表機關聯。 如果是,列印工作流程應用程式將啟動 (主要作為背景工作;更多資訊請參閱下文)。 工作流程應用程式能夠變更列印票證 (為目前列印工作設定印表機裝置設定的 XML 文件) 和要列印的實際 XPS 內容。 它可以選擇透過在流程中途啟動 UI 來向使用者公開此功能。 完成工作後,它會將列印內容和列印票據傳遞給驅動程式。
由於它涉及背景和前景組件,並且由於它在功能上與其他應用程式耦合,因此列印工作流程應用程式的實作可能比其他類別的 UWP 應用程式更複雜。 建議您在閱讀本指南時檢查工作流程應用程式範例,以進一步了解如何實作不同的功能。 為了簡單起見,本指南中沒有提供某些功能,例如各種錯誤檢查和 UI 管理。
開始使用
工作流程應用程式必須指示其列印系統的入口點,以便可以在適當的時間啟動。 這可藉由在 UWP 專案的 Application/Extensions
package.appxmanifest 檔案的元素中插入下列宣告來完成。
<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 方法,則列印系統會檢查進入點到前景應用程式的指令清單檔。 package.appxmanifest
檔案的 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 資料。 存取 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 執行個體會提供取消列印作業的選項,或指出作業成功,但不需要輸出列印作業。 這是藉由使用 PrintWorkflowSubmittedStatus 值呼叫 Complete 方法來完成。
注意
如果工作流程應用程式取消列印作業,強烈建議它提供快顯通知,指出作業取消的原因。
對列印內容執行最終背景工作
UI 完成 PrintTaskXpsDataAvailable 事件中的延遲之後(或如果略過 UI 步驟),列印系統就會針對背景工作引發送出 事件。 在此事件的處理常式中,工作流程應用程式可以存取 XpsDataAvailable 事件所提供的所有相同資料。 不過,與先前任何事件不同,提交也透過 PrintWorkflowTarget 執行個體提供最終列印作業內容的寫入 授權。
用來多工作緩衝處理最終列印之資料的物件取決於源資料是以原始位元組資料流或 XPS 物件模型存取。 當工作流程應用程式透過位元組流存取來源資料時,會提供輸出位元組流來寫入最終作業資料。 當工作流程應用程式透過物件模型存取來源資料時,會提供文件編寫器將物件寫入輸出作業。 無論哪種情況,工作流程應用程式都應讀取所有來源資料,修改所需的任何資料,並將修改後的資料寫入輸出目標。
當背景工作完成寫入資料時,它應該在對應的 PrintWorkflowSubmittedOperation 物件上呼叫 Complete。 工作流程應用程式完成此步驟並結束提交 事件處理常式之後,工作流程會話就會關閉,而且使用者可以透過標準列印對話框監視最終列印作業的狀態。
最後步驟
向印表機註冊列印工作流程應用程式
您的工作流程應用程式會使用與 WSDA 相同的中繼資料檔案提交類型與印表機相關聯。 事實上,單一 UWP 應用程式可以同時作為工作流程應用程式和 WSDA,以提供列印工作設定功能。 請遵循對應的 WSDA 步驟來建立中繼資料關聯。
不同之處在於,雖然 WSDA 會自動為使用者啟動 (當使用者在關聯裝置上列印時,應用程式將始終啟動),但工作流程應用程式則不會。 它們有必須設定的個別原則。
設定工作流程應用程式的原則
工作流程應用程式原則是由要執行工作流程應用程式的裝置上的 Powershell 命令所設定。 將修改「設定印表機」、「新增印表機」(現有連接埠) 和「新增印表機」(新 WSD 連接埠) 指令以允許設定工作流程原則。
Disabled
:工作流程應用程式將不會啟動。Uninitialized
:如果工作流程 DCA 安裝在系統中,工作流程應用程式將會啟動。 如果未安裝應用程式,列印仍會繼續進行。Enabled
:如果工作流程 DCA 安裝在系統中,工作流程將會啟動。 如果未安裝應用程式,列印將會失敗。
下列命令會讓指定的印表機上所需的工作流程應用程式。
Set-Printer –Name "Microsoft XPS Document Writer" -WorkflowPolicy Enabled
本機使用者可以在本機印表機上執行此策略,或者,對於企業實施,印表機管理員可以在印表機伺服器上執行此原則。 原則接著會同步處理至所有用戶端連線。 每當新增印表機時,印表機系統管理員可以使用此原則。