Overriding XtraReport or print preview toolbar commands

I happened upon a cool question on the Support Center the other day, asking how to override the default functionality in the Print Preview window to email a document via Gmail. If you look at the default Print Preview (in either ribbon or standard toolbar configuration), you’ll see that you’re given the option to email the document in various formats (Pdf, Xls, RTF etc…): Print Preview window Doing so will typically open a dialog window which prompts you to save the file to your computer before your default email program is opened and the file is attached. This is all done via MAPI, which may not be the method you want to take. It opens the user’s default email client and constructs a blank message with the file attached. Historically, DevExpress used the 32-bit version of MAPI which causes issues if you were to build your application to 64 bit. Additionally, you may not want to save the file to your computer before sending it because then you’ll need to delete it afterwards. And if your end user doesn’t have a default email program set up on their PC, they’ll be prompted with the Windows email wizard which attempts to walk them through the process of setting up an email account. So what if you’d prefer the document to be emailed through your server or a third-party server like Gmail? At first glance you have a couple of options:

  • Create a form, place a DocumentViewer component on the form and then create your own toolbar. You’ll have to provide your own implementation for each of the various commands that are offered by the built-in Document Viewer toolbar.
  • Do the same as above, but create the default Document Viewer toolbar (or ribbon control) via the smart tag. Then, assign an ItemClick event handler to the functions you’d like to control. The problem is that your code will run first and then the default button command will run afterwards. Hardly ideal.
  • Again, create a form with a Document Viewer but remove the BarButtomItems that you want to override. Insert your own BarButtonItems for the commands that you’d like to control. There’s nothing inherently wrong with this approach unless you want to use the built-in print-preview methods offered by controls like the XtraGrid or the XtraTreeList. In this case, they’ll use the default print preview window rather than your custom one.

So it can be done, but I’m not sure if any of these options are really optimal. Fortunately for us, all of the BarButtomItems in the Document Viewer window implement an ICommandHandler interface which, when implemented, tells the Document Viewer window two important things:

  1. Can I handle this command? (done by implementing the CanHandleCommand method)
  2. How should I handle this command? (done by implementing the HandleCommand method)

All of this is detailed in the documentation topic for How to: Execute and Modify Commands in a Print Preview. So how can we apply this to the topic that was originally presented?

Assuming we have a simple project containing an XtraReport and we want to send a Pdf copy via Gmail through the standard toolbar, we’ll need to first create our own CommandHandler.

To create your own CommandHandler, create a new class in Visual Studio and name it “MyCommandHandler”. Your class will need to implement ICommandHandler. You can then tell Visual Studio to implement the interface, which will automatically insert a CanHandleCommand and a HandleCommand method.

Let’s start with the CanHandleCommand method:

public virtual bool CanHandleCommand(PrintingSystemCommand command, IPrintControl control)
{
    //This handler overrides the Send Pdf command.
    return command == PrintingSystemCommand.SendPdf;
}

This crux of this method is to tell the Document Viewer (or printing system) if this handler can process the provided command. The PrintingSystemCommand enum contains all of the commands that are available by the default toolbar/ribbon control. In this case, we’re telling the printing system that we can handle the SendPdf command ourselves and no default action is necessary.

That being said, we now have to do the heavy lifting ourselves and actually do something when the button is clicked. To do that, we provide an implementation for the HandleCommand method.

public virtual void HandleCommand(PrintingSystemCommand command, object[] args, IPrintControl control, ref bool handled)
{

    const string Username = "YOUR_GMAIL_USERNAME_HERE";   //your username here
    const string Password = "YOUR_GMAIL_PASSWORD_HERE"; ///your password here

    if (CanHandleCommand(command, control) == false)
        return;

    using (MailMessage mailMessage = new MailMessage())
    {
        //Set to/from
        mailMessage.From = new MailAddress("email address here");
        mailMessage.To.Add(new MailAddress("email address here"));
        mailMessage.Subject = "Test message";
        mailMessage.Body = "This is my test email!";

        //Create an attachment
        using (MemoryStream ms = new MemoryStream())
        {
            control.PrintingSystem.ExportToPdf(ms);
            ms.Position = 0;
            mailMessage.Attachments.Add(new Attachment(ms, "MyFile.pdf", "application/pdf"));

            using (SmtpClient client = new SmtpClient())
            {
                client.Host = "smtp.gmail.com";
                client.Port = 587;
                client.EnableSsl = true;
                client.DeliveryMethod = SmtpDeliveryMethod.Network;
                client.UseDefaultCredentials = false;
                client.Credentials = new NetworkCredential(Username, Password);

                client.Send(mailMessage);
            }
        }
    }

    //Ensure the default action isn't fired by setting Handled to True
    handled = true;
}

So here we’ve created a nice implementation that sends a Pdf copy of the document via email.

Note that I’ve decided to user a MemoryStream here instead of saving the Pdf file to the user’s PC first. This alleviates the need to save a file to the disk, but it does remove the ability to customize the Pdf. If you want to change some of the Pdf properties upon export, you can use the PdfExportOptions class offered by the ExportToPdf overload.

The only thing left to do is tell our PrintPreview window to actually use this CommandHandler. We do that via the PrintingSystem.AddCommandHandler method:

// Create a report instance, assigned to a Print Tool.
ReportPrintTool pt = new ReportPrintTool(new XtraReport1());

// Generate the report's document. This step is required
// to activate its PrintingSystem and access it later.
pt.Report.CreateDocument(false);

// Override the Send Pdf command.
pt.PrintingSystem.AddCommandHandler(new SendToGmailCommandHandler());

// Show the report's print preview.
pt.ShowPreview();

And just like that, we’ve overrided the default behavior and created our own implementation.

Going forward, we can also use this CommandHandler for print previewing other DevExpress controls such as the GridControl. By handling the GridView’s PrintInitialize event, we can insert our CommandHandler and reuse the same implementation:

private void gridView1_PrintInitialize(object sender, DevExpress.XtraGrid.Views.Base.PrintInitializeEventArgs e)
{
    (e.PrintingSystem as PrintingSystemBase).AddCommandHandler(new MyCommandHandler());
}

The full source code is available here: CustomCommandHandler

Overriding XtraReport or print preview toolbar commands

Getting started

If you’ve never worked with the DevExpress products before, you’ll want to download the free 30 day trial. The trial allows you full access to all of the DevExpress products and DevExpress graciously extends full support to you for those 30 days via the Support Center. The trial will work with Visual Studio and Visual Studio Express, but VS Express does not support all of the templates/wizards. Additionally, the newest version of DevExpress (14.1) requires .NET 4.0 and VS 2010. You may want to review the .NET Framework Support History documentation and the Visual Studio Support History documentation.

Once you’re up and running, try running the Demo Center to have a look at the various products and controls. There’s a lot going on so it can be a bit daunting, but most of the demos come with full C# and VB.NET source code, which is installed by default to C:\Users\Public\Documents\DevExpress Demos 14.1\Components\WinForms (at least for WinForms demos).

Note: Many of the DevExpress demos tend to be a bit obfuscated and do a lot of things that aren’t documented. While they’re a good example of what CAN be done, I don’t find them to be the best example of HOW to do a task.

Fortunately, DevExpress provides a lot of resources to help you learn. Unfortunately, they provide a lot of resources to help you learn. It can be a bit daunting at first, but you’ll find that you will have the most success if you can learn where to look for help.

Here are some great starting points for learning the ins-and-outs of the DevExpress products:

As a final tip, I recommend that users looking to search the support center/knowledge base use Google instead of the DevExpress search function. The DevExpress search feature leaves a bit to be desired, but searching Google with the -site:devexpress.com option works incredibly well.

Ready to go?

Getting started

Let’s explore together!

Welcome to my new blog, DX-Developers.com! I started this blog to share some tips, tricks and cool sample applications with developers who are using the DevExpress suite of products..

First, a little about me:

I’m the Director of Information Technology for Logistic Dynamics, Inc. (LDi), a logistics brokerage firm headquartered in Buffalo, NY. I started with LDi in September of 2005 as the senior developer and have loved coming to work every day since. I’m primarily responsible for development work but I have my hand in a little bit of everything from accounting to marketing to golfing.

I’m also a DevExpress MVP (since 2009) and I spend quite a bit of time answering questions and providing support on the DevExpress Support Center.

I’ve used many of the various DevExpress products, but I’m most skilled with the WinForms, XtraReports, eXpress Persistent Objects (XPO) and ASP.NET products and this blog will primarily target those products.

With that out of the way, here’s what you can expect from my blog:

  1. How to perform commonly-requested tasks with DevExpress products
  2. How to extend the DevExpress products to provide new functionality
  3. Some cool tricks that you might not know you can do with the DevExpress products

The aim of this blog is not to be a general programming site. I’m assuming you already have intermediate knowledge in .NET, WinForms, HTML, ASP.NET and Sql. I’m not here to teach design patterns and best practices–I certainly don’t know them and even if I do, I probably don’t follow them. My samples will be provided in C# only; please do not ask me to convert it to VB.NET for you. I promise you that if you were to really look at a the sample it will make sense to you. The syntax isn’t THAT different or hard to discern and there are plenty of online converters available.

I’m also not going to respond to comments that are not related to the topic posted. Please don’t hijack a topic to ask how to do your homework. If you have a question and you think it would make a good topic for discussion, contact me!

I’m a bit new to blogging & WordPress, so bear with me as I get over these initial learning curves.

With all of that out of the way, let’s go going!

Let’s explore together!