Chapter 6. Interacting with the Host Platform

Up until now we have only dealt with Silverlight itself. Now it's time to take things to a higher scope and deal with the environment of Silverlight. Not every piece of your application can be part of your Silverlight solution. Sometimes you may want to store data on the user's computer, interact with a JavaScript function, or even create an application that runs on the user's computer directly without the need for a browser. In this chapter we are going to deal with all of these things. We are going to discuss all the ways Silverlight can interact with its host platform.

In this chapter we will cover the following topics:

  • Implementing the printing API
  • Creating out-of-browser applications
  • Accessing the isolated storage
  • Interacting with the HTML DOM
  • Accessing the clipboard
  • Reading from and writing to the host filesystem
  • Handling alternative input methods

Implementing the printing API

You've just created the perfect LOB application. Everything works great and users are happy. After a while, a few of them come to you with a request that seems quite trivial—they need to print forms and pages off your application. As trivial as this request sounds, up until Silverlight 3 this would have been impossible. Silverlight 3 introduced the WriteableBitmap class, which allows you to save any element in your application as a bitmap and then print it. Silverlight 4, however, was the first Silverlight release to introduce a real solution to the printing problem, known as the printing API.

The entire printing process in Silverlight relies on an object called PrintDocument. This object contains three events that cover the entire lifespan of the printing process—BeginPrint, PrintPage, and EndPrint.

The BeginPrint event is raised as soon as the Print method gets called on the PrintDocument object. Then, for each page that needs to be printed, PrintDocument raises the PrintPage event. This is the most important handler, as it enables you to build and design the printing area and set if there are any more pages to be printed.

Once Silverlight lays out the content to be printed, a bitmap is rasterized and sent to the printer driver. A note to be taken here is that as Silverlight sends a bitmap to the printer driver, its size might be bigger than expected.

Once the page is sent to the printer driver, Silverlight checks if there are any more pages to be printed. If there are, the PrintPage event gets raised again. If not, the EndPrint event is raised.

Other than these three events, the PrintDocument object exposes a property called DocumentName, which defines the name of the print job, and HasMorePages, which is a Boolean type property that can inform whether or not there are more pages to print.

Let's go ahead and print some pages!

Creating your first print job

To get started, open the Chapter6-PrintingAPI project in Visual Studio 2010 from the downloadable content of the book. The project's UI consists of a simple layout of text and an image and a print button.

To get started with the printing API, we first have to initialize the PrintDocument object. Switch to MainPage.xaml.cs and add the following line of code above the MainPage constructor:

private PrintDocument _pd = new PrintDocument();

As discussed earlier, the most important event of the PrintDocument object is the PrintPage event. The handler of this event enables us to set what is going to get printed. Let's add the event handler right now. Add the following line of code inside the MainPage constructor:

_pd.PrintPage += new EventHandler<PrintPageEventArgs>(_pd_PrintPage);

Inside the _pd.PrintPage handler, we can access the event arguments of the event. The arguments enable us to specify what we want to print (using the PageVisual property), get the size and margin of the currently printing page (using the PrintableArea and PageMargins properties), and set if we have anything else to print (using the HasMorePages Boolean property). As we have a single element that we wish to print—the LayoutRoot Grid element—let's set the event handler as follows:

void _pd_PrintPage(object sender, PrintPageEventArgs e)
{
e.PageVisual = LayoutRoot;
e.HasMorePages = false;
}

Now that we have set the visual to print, and told the framework it's the only page we wish to print, all that's left to do is call the Print method of the PrintDocument object to actually print the page. Add the following code snippet in your MainPage.xaml.cs file:

private void btnPrint_Click(object sender, RoutedEventArgs e)
{
_pd.Print("Sample Silverlight Document");
}

Build and run the application, and you should get the result, as shown in the following screenshot:

Creating your first print job

Click the Print me! button, and you should be greeted with the familiar print dialog box.

If you print the page, you will get the exact same visual as your application:

Creating your first print job

Now, this Print me! button has no place in the printed material. What if we want to hide it from the printed version of our page?

This is where the BeginPrint and EndPrint events come into play. As mentioned earlier, BeginPrint happens just before the PrintPage event is called, and EndPrint is called as soon as the page is sent to the printer. If we wish to hide the Print me! button from the printed page, all we have to do is hide it in the BeginPrint handler and show it again on the EndPrint handler.

Add the following handlers to your code, just below the PrintPage handler:

_pd.BeginPrint += new EventHandler<BeginPrintEventArgs>(_pd_BeginPrint);
_pd.EndPrint += new EventHandler<EndPrintEventArgs>(_pd_EndPrint);

Add the actual handler methods to your code:

void _pd_BeginPrint(object sender, BeginPrintEventArgs e)
{
btnPrint.Visibility = Visibility.Collapsed;
}
void _pd_EndPrint(object sender, EndPrintEventArgs e)
{
btnPrint.Visibility = Visibility.Visible;
}

Build and run your application again. Print the page, and you should get the result, as shown in the following screenshot:

Creating your first print job

Printing multiple pages with page numbers

To demonstrate how to handle multiple pages with Silverlights printing API, open the Chapter6-MutliPrinting project in Visual Studio 2010.

We are going to focus on the printing part, so the UI and corresponding code have already been written for us. The application is a simple agenda viewer based on a ViewBox control and binding to an entity.

The application contains the following two entities that the UI binds to:

  • AgendaEntity: This is the main entity of the application. It contains the attendee's name, the currently viewed page number, the width of the printing area, and a collection of the MeetingEntity entities, each of which represent a meeting.
  • MeetingEntity: This is an entity that represents a meeting. It contains the meeting title, abstract, and time.

In addition, the application contains a user control named PrintPage.xaml. This control will be used as the visual for our printing needs.

To better understand what we are going to print, build and run the application now. You should get the result, as shown in the following screenshot:

Printing multiple pages with page numbers

By binding the meetings ObservableCollection to ViewBox and setting its DataTemplate, we get a nicely charted list of meetings. While this entire agenda can be printed on one page, for the sake of the example, we will assume only three meetings can fit into a single page. As this is the case, we need to handle multipage printing and set the right page number for the printed page.

First, we call the Print method of the PrintDocument object when the user clicks on the Print button. Add the following line of code to the printBtn_Click event handler in the MainPage.xaml.cs file:

_pd.Print("My Agenda");

Next, we need to handle the PrintPage event. Change the _pd_PrintPage event handler as follows:

void _pd_PrintPage(object sender, PrintPageEventArgs e)
{
PrintPage pp = new PrintPage();
ObservableCollection<MeetingEntity> meetings = new ObservableCollection<MeetingEntity>();
while (_rowCounter < _entity.Meetings.Count)
{
meetings.Add(_entity.Meetings[_rowCounter]);
_rowCounter++;
if (_rowCounter % 3 == 0)
{
if (_rowCounter == _entity.Meetings.Count)
break;
else
{
e.HasMorePages = true;
break;
}
}
}
AgendaEntity entity = new AgendaEntity();
entity.Meetings = meetings;
entity.AttendeeName = _entity.AttendeeName;
entity.PageNumber = _entity.PageNumber;
entity.PrintWidth = e.PrintableArea.Width;
pp.DataContext = entity;
e.PageVisual = pp;
_entity.PageNumber++;
}

Even though it seems like a lot of code, the logic is pretty simple.

We first create an instance of PrintPage, which is the user control we have mentioned earlier. The user control contains the same UI as MainPage.xaml minus the print button. We then create a new ObservableCollection of the MeetingEntity items to hold the meetings we wish to print on a particular page. Next, we have a while loop that checks whether or not the _rowCounter parameter is less than the total sum of meetings the main entity holds, and if it is less, it adds the meeting to the meetings collection, increments the _rowCounter parameter by 1, and checks if it can be divided by 3 without a remainder. If it can, that means we have added three items to the new collection. Then, we check if the counter equals the number of meetings the agenda has. If it does, that means we've finished and we should just break the loop and continue with the rest of the method. If it doesn't, that means we have more to print and, thus, we set the HasMorePages Boolean property of the event to true. That will cause the PrintPage method to run again for the new page that needs to be printed. Finally, we create a new AgendaEntity instance and set its different properties, such as the collection of items (meetings), attendee name, page number, and the width of the printing area. It is important to note that we progress the page number counter by 1 at the end of the method, so that we can get the correct number for the currently printed page.

Finally, we have to deal with the EndPrint handler. This handler contains a single line of code that simply resets the PageNumber property to 1, as the UI shows all the meetings on one page, no matter how many meetings are shown. Add the following line of code to the _pd_EndPrint handler:

_entity.PageNumber = 1;

That's it. Handling multiple pages in the printing API can be a bit tricky at first so it's highly recommended you go over the code again, just to make sure you understand what's going under the hood there.

Build and run the application, and click on the Print button. If printed to an XPS, you should get the result, as shown in the following screenshot:

Printing multiple pages with page numbers

There are a total of two pages, each with three meetings.

Note

You don't have to add the entire LayoutRoot as the PageVisual to be printed. You can also specify specific elements, both on and off the screen. For example, if we only want to print a ViewBox control named vb, all we have to do is set the PageVisual property of the PrintPageEventArgs argument of the PrintPage handler to the name of the ViewBox control. In other words, e.PageVisual = vb.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.2.240