I’ve amassed quite a collection of ebooks in PDF format and I recently had the idea to make them available from my server at home over the internet so I could access them from wherever I happened to be. I wanted to write the application in Silverlight, but how to actually view the PDF’s?
The solution to this problem is possible because of the tight integration between Silverlight and the browser DOM. Recall that if I enter the URL of a PDF document into the browser address bar, and I have a PDF viewer installed, an instance of the PDF viewer is created in the browser and it renders the PDF document. We can use this behavior to render the PDF and make it look as though the PDF document is being viewed from the Silverlight application.
The HTML
Since we want to be able to leverage the browser’s HTML rendering behavior to show the PDF file, we need to create some HTML for the browser to render. In the page that’s hosting the Silverlight application, create an iframe or div tag:
<iframe id="pdfFrame" style="visibility:hidden; position:absolute"><b>No Content</b></iframe>
Control Via Silverlight
No, in the Silverlight application, you need to create a control to “host” the IFrame. You can’t actually insert the IFrame into the Silverlight visual tree, but we’re going to tie the IFrame and this control together in such a way that it looks like this control is hosted inside our application. For our purposes, a grid will do nicely.
<Grid x:Name="LayoutRoot">
<Grid.Background>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#FF101010" Offset="0.98299998044967651" />
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="White" Offset="0.546999990940094" />
</LinearGradientBrush>
</Grid.Background>
<Grid x:Name="pdfHost" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="10" Background="White" LayoutUpdated="Grid_LayoutUpdated">
</Grid>
</Grid>
Notice that I’ve hooked the LayoutUpdated event. In the handler for this event, we can tie the size and shape of our IFrame with that of the grid.
private void Grid_LayoutUpdated(object sender, EventArgs e)
{
ShowPreview();
}
private void ShowPreview()
{
GeneralTransform gt = pdfHost.TransformToVisual(Application.Current.RootVisual as UIElement);
Point offset = gt.Transform(new Point(0, 0)); // Here you find your Panel Top/Left position
int controlLeft = (int)offset.X;
int controlTop = (int)offset.Y;
HtmlElement m = HtmlPage.Document.GetElementById("pdfFrame"); // Find your HTML DIV
if (m != null)
{
m.SetStyleAttribute("left", controlLeft.ToString() + "px"); // Set the Div position
m.SetStyleAttribute("top", controlTop.ToString() + "px");
m.SetStyleAttribute("visibility", "visible");
m.SetStyleAttribute("height", pdfHost.ActualHeight.ToString() + "px");
m.SetStyleAttribute("width", pdfHost.ActualWidth.ToString() + "px");
m.SetStyleAttribute("z-index", "1000");
}
}
Here, you find the location of your grid and set the style of the IFrame to match its position and size. In effect, every time the grid is resized or repositioned, the IFrame will follow it. Notice how we use the HtmlPage class to access the host page’s DOM and find our IFrame.
Show the PDF
No that we have the IFrame tied to our grid, how do we actually show the PDF in the IFrame?
HtmlElement el = HtmlPage.Document.GetElementById("pdfFrame");
el.SetProperty("src", "showpdf.aspx?key=" + key.ToString());
First, get a reference to the IFrame, again, and then set it’s src property. Here, I have an aspx page that pulls the pdf file from another location and streams it to my IFrame. If the PDF is located within your web directory structure, you can bypass the aspx page and just specify the path to you pdf file.