left-icon

Direct2D Succinctly®
by Chris Rose

Previous
Chapter

of
A
A
A

CHAPTER 10

Printing Direct2D

Printing Direct2D


In this chapter we will walk through how to add printing to a standard Direct2D (XAML) application, which users can activate from the charms bar by selecting the Devices icon. I have described how to add printing to a new Direct2D (XAML) project, as opposed to adding the functionality to our existing application, because printing Direct2D is applicable beyond graphing and charting data.

Note: Most of the code I have presented here is either based heavily on the Direct2Dapp printing sample from Microsoft, or taken directly from this sample. The Microsoft sample is designed as a standalone application. In this walk-through we will examine how to add printing to a Direct2D (XAML) application, rather than writing a completely new application. If you are starting a new project from scratch, it may be beneficial to more closely model the structure of your application after the Microsoft sample called "Direct2Dapp printing sample." See Appendix A for Microsoft's license for the use of the code from its samples.

Create a New Direct2D XAML Project

Create a new Direct2D (XAML) app. Even if you are adding printing functionality to an existing project, it is recommended that you step through this process with a new Direct2D (XAML) app in order to familiarize yourself with the structure of a Direct2D printing application.

Open the DirectXPage.xaml file and delete the text that says "Hello XAML." This example will not print out the contents of the XAML controls.

Note: Args in Unity uses delegates for implementation, whereas EventArgs uses events.

Make Your Application Multithreaded

Direct2D printing occurs on a separate thread with another device context. There is also a separate thread for rendering the print preview. The screen, the preview, and the printing can all occur at once without interfering with each other. The main Direct2D factory must be created multithreaded. The option to create a multithreaded application is set in the DirectXBase class in the CreateDeviceIndependentResources method of the DirectXBase.cpp file.

// These are the resources required independent of the device.

void DirectXBase::CreateDeviceIndependentResources(){

     D2D1_FACTORY_OPTIONS options;

     ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));

#if defined(_DEBUG)

      // If the project is in a debug build, enable Direct2D debugging via SDK Layers.

     options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;

#endif

     DX::ThrowIfFailed(

          D2D1CreateFactory(

               D2D1_FACTORY_TYPE_MULTI_THREADED,

               __uuidof(ID2D1Factory1),&options,&m_d2dFactory

               )

          );

Add the Print Manager

Add a "using namespace" directive to the top of the DirectXPage class in the DirectXPage.cpp file to use the Printing namespace. There are several other changes that must be made to this class. See Appendix B for a complete listing of the DirectXPage class.

using namespace Windows::UI::Xaml::Media;

using namespace Windows::UI::Xaml::Navigation;

using namespace Windows::Graphics::Printing;

DirectXPage::DirectXPage() :

Add a print manager member variable to your DirectXPage class in the DirectXPage.h file.

BasicTimer^ m_timer;        

     Windows::Graphics::Printing::PrintManager^ m_printManager;

};

Initialize the Print Manager

Initialize the print manager in the constructor of the DirectXPage class in the DirectXPage.cpp file. This can occur after the other code. I have placed it directly under the initialization of the m_timer. To initialize it we need to do two things: grab the print manager for the current program, and register an event handler so we know when the user requests to print something.

m_timer = ref new BasicTimer();

// Grab the print manager for the current view

m_printManager = Windows::Graphics::Printing::PrintManager::GetForCurrentView();

// Add an event handler to capture when the user requests a print task

m_printManager->PrintTaskRequested +=

     ref new TypedEventHandler<PrintManager^, PrintTaskRequestedEventArgs^>(this,

     &DirectXPage::SetPrintTask);

In this code we have specified that there is a handler method called SetPrintTask, which the print manager will use to make new printing tasks.

Create the SetPrintTask Method

This method is very important, as it determines when the user has requested to print something from the charms bar. It will be called when the user selects Devices from the charms bar, and depending on what the event does, it will either enable the application to print, or the charms bar will say "this app can't send to other devices at the moment." Add the SetPrintTask prototype to the DirectXPage.h header.

     BasicTimer^ m_timer;        

     Windows::Graphics::Printing::PrintManager^ m_printManager;

internal:

     // Print task requested event handler method

     void SetPrintTask(_In_ Windows::Graphics::Printing::PrintManager^ sender,

          _In_ Windows::Graphics::Printing::PrintTaskRequestedEventArgs^ args);

     };

Note: The declaration of the SetPrintTask (as well as many other methods in this sample) must be marked as internal, since it contains native data types in its prototype, and the class is marked as ref, meaning it is a .NET Framework Reference class. Reference classes cannot contain public methods or data based on native types.

The following listing is the method body for the SetPrintTask method. I have placed it after the LoadInternalState method as the final method in the DirectXPage.cpp file.

void DirectXPage::LoadInternalState(IPropertySet^ state) {

     m_renderer->LoadInternalState(state);

}

void DirectXPage::SetPrintTask(_In_ PrintManager^ sender,

     _In_ PrintTaskRequestedEventArgs^ args){

// Create a new source requested handler

PrintTaskSourceRequestedHandler^ sourceRequestedHandler = ref new PrintTaskSourceRequestedHandler(

     [this](PrintTaskSourceRequestedArgs^ args)-> void {

          Microsoft::WRL::ComPtr<CDocumentSource> documentSource;

          DX::ThrowIfFailed (

               Microsoft::WRL::MakeAndInitialize<CDocumentSource>(

                &documentSource,reinterpret_cast<IUnknown*>(m_renderer)

                )

               );

               // Cast the document to an object

               IPrintDocumentSource^ objSource(     reinterpret_cast<IPrintDocumentSource^>(documentSource.Get())

               );

args->SetSource(objSource);

          });

// Create the print task

PrintTask^ printTask = args->Request->CreatePrintTask(L"Direct 2D Printing Example",

          sourceRequestedHandler);

}

Note: This method uses the new (C++11) syntax for lambda expressions since the new event handler needs access to the data members of the outer DirectXPage class. These expressions are similar in functionality to anonymous inner types in Java. The code is from Microsoft. Please refer to Appendix A for the license and reuse of this code.

The DirectX (XAML) template has several references to methods in the SimpleTextRenderer, which will be removed when we rewrite the class. It also references the CDocumentClass from Microsoft, which I have included as Appendix C. It creates and registers a PrintTaskSourceRequestedHandler event handler. This event is fired when the print task requests a document to be printed. It must be called to finish the initialization of the PrintTask object. As mentioned, for a full listing of the final altered DirectXPage class without these methods, refer to Appendix B.

Add DocSource Class

Next, we will create the document source class referenced in the previous code. Add an #include to the top of the DirectXPage.cpp file to include the header for a new class called DocSource.h.

#include "pch.h"

#include "DirectXPage.xaml.h"

#include "DocSource.h"

Add a header and a code file for the new Document Source class called DocSource.h and DocSource.cpp. The complete code listing (from Microsoft’s printing sample) for the Document Source class is included as Appendix C.

Split the SimpleTextRenderer

The next step is to enable the SimpleTextRenderer class to be created more than once to create multiple contexts. The multiple device contexts will run in parallel. The rendering method itself must also be updated to consider the format of the output. The dimensions of a computer monitor are usually different from those of a standard A4 piece of paper. The Rendering method of this class must now consider the differences in these dimensions, and alter the layout of the image if required. There is no standard way to format output from a monitor such that it perfectly suits a physical piece of paper. Some suggestions would be that the background not be colored when printing, since printing a solid colored background is very slow and wastes a lot of ink.

Tip: The standard orientation of the printed page is portrait. It may match the image on the monitor better if this orientation of the printed page is changed to landscape.

The SimpleTextRenderer class contains a lot of code that is specific to this Visual Studio template. The background changing colors, loading and saving of the internal state, and the member variables for displaying the “Hello DirectX!” can be removed, since they have nothing to do with printing. The final listing for the two files is included as Appendix D. Much of the following code comes from Microsoft; Appendix A is the license agreement for use of the Microsoft code.

The actual drawing is exactly the same as we have previously examined; it is made of perfectly normal Direct2D context method calls. The method is called Draw in the example code. I have highlighted it in green. The App class must also be updated and the Visual Studio template methods dealing with loading and saving the internal state can be removed.

The final listing for App.xaml.cpp is as follows.

//

// App.xaml.cpp

// Implementation of the App class.

//

#include "pch.h"

#include "DirectXPage.xaml.h"

using namespace DXPrinting;

using namespace Platform;

using namespace Windows::ApplicationModel;

using namespace Windows::ApplicationModel::Activation;

using namespace Windows::Foundation;

using namespace Windows::Foundation::Collections;

using namespace Windows::Storage;

using namespace Windows::UI::Xaml;

using namespace Windows::UI::Xaml::Controls;

using namespace Windows::UI::Xaml::Controls::Primitives;

using namespace Windows::UI::Xaml::Data;

using namespace Windows::UI::Xaml::Input;

using namespace Windows::UI::Xaml::Interop;

using namespace Windows::UI::Xaml::Media;

using namespace Windows::UI::Xaml::Navigation;

App::App(){

     InitializeComponent();

     Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);

}

void App::OnLaunched(LaunchActivatedEventArgs^ args){

     m_directXPage = ref new DirectXPage();

     // Place the page in the current window and ensure that it is active.

     Window::Current->Content = m_directXPage;

     Window::Current->Activate();

}

void App::OnSuspending(Object^ sender, SuspendingEventArgs^ args){

     (void) sender; // Unused parameter.

     (void) args; // Unused parameter.

}

After implementing the code in this chapter and the appendices, you should be able to run the application and print a pattern of circles (rendered in the Draw method). The same pattern of colored circles can be drawn to the screen, the print preview, or printed to a page using the charms bar.

The code for this chapter was lengthy and mostly taken from the Microsoft Direct2D printing sample. Interacting with printers is a complex task, and all the boilerplate code for this type of activity should be taken from the Microsoft samples. Printing DirectX is really a job for the operating system, and has very little to do with DirectX. Minor deviations from the standard code as presented in the Microsoft samples will not work.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.