// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved // // // Abstract: // // This file contains as the interaction logic and data-binding code/sources for the WPF print preferences window. using System; using System.ComponentModel; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Reflection; using System.IO; using System.Xml.Linq; using System.Runtime.InteropServices; using Microsoft.Samples.Printing.PrinterExtension.Types; using Microsoft.Samples.Printing.PrinterExtension.Helpers; namespace Microsoft.Samples.Printing.PrinterExtension { /// /// Interaction logic for PrintPreferenceWindow.xaml. /// public partial class PrintPreferenceWindow : Window, INotifyPropertyChanged { public PrintPreferenceWindow() { InitializeComponent(); } /// /// This method sets up data binding sources and performs other initialization tasks. /// /// public void Initialize(PrinterExtensionEventArgs eventArgs) { // // Populate the data binding sources. // DataContext = this; printerExtensionEventArgs = eventArgs; PrinterQueue = eventArgs.Queue; displayedPrintTicket = eventArgs.Ticket; // // Send a bidi query requesting ink levels. // // Please note: As this event will fire many times, it is recommended to maintain event // listeners for the life time of the application. Furthermore, the relationship to this // being invoked and the calling SendBidiQuery() is not 1:1; in fact, it is *:N, where the // listener may be called several times with bidi updates. // // PrinterQueue.OnBidiResponseReceived += OnBidiResponseReceived; PrinterQueue.SendBidiQuery("\\Printer.consumables"); } #region UI code /// /// This event handler is invoked when the window is closing. It is important to Cancel or Complete the request when the window is closing. /// /// /// void PrintPreferenceWindow_Closing(object sender, EventArgs e) { // Since we are a different process from the printing application we need to hand focus back when complete. WindowHelper.SetForegroundWindow(printerExtensionEventArgs.WindowParent); if (!requestCompleted) { printerExtensionEventArgs.Request.Cancel((int)HRESULT.S_FALSE, "The user canceled the operation."); requestCompleted = true; } } /// /// Button click event handler. /// /// /// private void Button_Click(object sender, RoutedEventArgs e) { Button clickedButton = (Button)sender; switch (clickedButton.Name) { case "CancelButton": CancelRequestAndCloseWindow(); break; case "OkButton": // // Validate the print ticket asynchronously. The event handler is invoked when the validation is completed. // IPrintSchemaAsyncOperation asyncOperation = displayedPrintTicket.ValidateAsync(); // // Pop up a modal dialog that prevents the user from changing selections when validation is in progress. // Since this dialog lives as long as the parent window, it is not mandatory to unregister the delegate for the // 'Completed' event. // ValidationModalDialog.Completed += PrintTicketValidateCompleted; // This operation is idempotent. ValidationModalDialog.StartAsyncOperation(asyncOperation); break; case "VerifyButton": // // Force WPF Data binding to refresh the UI. This operation retrieves // a fresh print capabilities for the print ticket based on the user selections. // PropertyChanged(this, new PropertyChangedEventArgs("PrintSchemaHelperSource")); break; } } /// /// Close the window in a thread-safe way. /// private void CloseWindow() { this.Dispatcher.BeginInvoke(new Action(() => { this.Close(); })); } #endregion #region Data binding sources /// /// The Print queue for which this window is being displayed. /// public IPrinterQueue PrinterQueue { get; private set; } /// /// The title for the ink status display. /// public string InkStatusTitle { get; private set; } /// /// Retrieve a new instance of PrintSchemaHelper, based on the current print ticket being displayed. /// PrintSchemaHelper encapsulates all the features and options required to populate the print preferences UI. /// public PrintSchemaHelper PrintSchemaHelperSource { get { // // Below is the list of features that will be displayed in the print preferences window. // The features are declared here for convenience/readability. // // In performant code, this array would be allocated only once per run of the application. // string[] featureNames = { "DocumentNUp", "PageMediaSize", "DocumentInputBin", "PageOrientation", "PageMediaType", "PageBorderless", "JobInputBin", "PageOutputColor", "DocumentCollate", "DocumentDuplex" }; return new PrintSchemaHelper(displayedPrintTicket, featureNames); } } /// /// Encapsulates the information required to populate ink level. /// public BidiHelper BidiHelperSource { get; private set; } /// /// This event is raised when a data from a binding source is modified. /// public event PropertyChangedEventHandler PropertyChanged; #endregion #region Ink level display /// /// This is the method invoked when a bidi response is received. /// /// IPrinterQueue object. /// The results of the bidi response. private void OnBidiResponseReceived(object sender, PrinterQueueEventArgs e) { if (e.StatusHResult != (int)HRESULT.S_OK) { MockInkStatus(); return; } // // Display the ink levels from the mock data. // BidiHelperSource = new BidiHelper(e.Response); if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("BidiHelperSource")); } InkStatusTitle = "Ink status (Live data)"; } /// /// This method is invoked when there is an error retrieving Bidi information. /// A mock bidi response is loaded from resource and displayed. /// private void MockInkStatus() { // // Load mock bidi response resource. // Assembly a = Assembly.GetExecutingAssembly(); Stream xmlData = a.GetManifestResourceStream("PrinterExtensionSample.bidi_Ink_mock.xml"); StreamReader sr = new StreamReader(xmlData); string xmlString = sr.ReadToEnd(); // // Display the ink levels from the mock data. // BidiHelperSource = new BidiHelper(xmlString); if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("BidiHelperSource")); } InkStatusTitle = "Ink status (Mocked data)"; } #endregion #region PrintSchema-related code /// /// Cancel the current printer extension event and close the current window. /// private void CancelRequestAndCloseWindow() { printerExtensionEventArgs.Request.Cancel((int)HRESULT.S_FALSE, "User canceled the operation"); requestCompleted = true; CloseWindow(); } /// /// Invoked when asynchronous print ticket validation is complete. /// /// /// private void PrintTicketValidateCompleted(object sender, PrintSchemaAsyncOperationEventArgs e) { // // Print ticket validation completed successfully i.e. print ticket selections are not constrained. // The print ticket needs to be committed, and then window can be closed. // if (e.StatusHResult == (int)HRESULT.S_PT_NO_CONFLICT) { this.Dispatcher.Invoke(new Action(() => { CommitPrintTicketAsync(e.Ticket); })); } else { // // The ticket selections are constrained. // this.Dispatcher.Invoke(new Action(() => { HandleTicketConstraints(e.Ticket); })); } } /// /// Invoked when there are constraints in the print ticket selections. /// /// private void HandleTicketConstraints(IPrintSchemaTicket validatedTicket) { // // Retrieved localized display strings from a resource file/ // string selectionConflictsFound = PrinterExtensionSample.Strings.SelectionConflictsFound; string selectionConflictsTitle = PrinterExtensionSample.Strings.SelectionConflictsTitle; MessageBoxResult result = MessageBox.Show( this, selectionConflictsFound, selectionConflictsTitle, MessageBoxButton.YesNoCancel); if (result == MessageBoxResult.Yes) { CommitPrintTicketAsync(validatedTicket); } else { PropertyChanged(this, new PropertyChangedEventArgs("PrintSchemaHelperSource")); } } /// /// Commits the input print ticket asynchronously. The completed event handler is expected to close the window. /// /// private void CommitPrintTicketAsync(IPrintSchemaTicket validatedTicket) { IPrintSchemaAsyncOperation commitAsyncOperation = printerExtensionEventArgs.Ticket.CommitAsync(validatedTicket); commitAsyncOperation.Completed += PrintTicketCommitCompleted; commitAsyncOperation.Start(); } /// /// Invoked when the user's selections have been committed into the print ticket. /// /// /// private void PrintTicketCommitCompleted(object sender, PrintSchemaAsyncOperationEventArgs e) { CompleteRequestAndCloseWindow(); } /// /// Complete the current printer extension request and close the current window. /// private void CompleteRequestAndCloseWindow() { // // It is important to invoke the IPrinterExtensionRequest::Complete method from the thread the // class instance was create on (i.e. the UI thread). // this.Dispatcher.Invoke(new Action(() => { printerExtensionEventArgs.Request.Complete(); })); requestCompleted = true; CloseWindow(); } /// /// Demonstrates how to modify print ticket XML. This piece of code does not perform any functionality. /// It serves to demonstrate the usage of IPrintSchemaTicket::GetReadStream()/GetWriteStream() /// private void ModifyPrintTicketXml() { // // Load the ticket XML (as a Stream) into an XElement object. // XElement ticketRootXElement = null; using (Stream ticketReadStream = displayedPrintTicket.GetReadStream()) { ticketRootXElement = XElement.Load(ticketReadStream); } // // Perform any modifications on the XElement object. // // // Write the changes back to the print ticket. // using (Stream ticketWriteStream = displayedPrintTicket.GetWriteStream()) { ticketRootXElement.Save(ticketWriteStream); } } #endregion /// /// The arguments passed in for this print preferences event. /// private PrinterExtensionEventArgs printerExtensionEventArgs = null; /// /// Reflects the currently displayed print preference options. /// private IPrintSchemaTicket displayedPrintTicket = null; /// /// Determines if IPrinterExtensionRequest::Complete()/Cancel() has been invoked for this Window. /// instance /// private bool requestCompleted = false; } /// /// This class transforms the boolean 'IPrintSchemaOption.Constrained' into a visual form. /// public class OptionConstrainedToDisplayColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return "Black"; } // // If the option is not constrained, it will be diplayed in black. // PrintSchemaConstrainedSetting constrained = (PrintSchemaConstrainedSetting)value; if (constrained == PrintSchemaConstrainedSetting.None) { return "Black"; } // // If the option is constrained, it will be displayed in red. // return "Red"; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } /// /// Provides a friendlier way to use HRESULT error codes. /// enum HRESULT : int { S_OK = 0x0000, S_FALSE = 0x0001, S_PT_NO_CONFLICT = 0x40001, E_INVALIDARG = unchecked((int)0x80070057), E_OUTOFMEMORY = unchecked((int)0x8007000E), ERROR_NOT_FOUND = unchecked((int)0x80070490) } }