(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .
        Login with Facebook

Save the Web

(4 votes)
Jeff Paries
>
Jeff Paries
Joined Jan 26, 2009
Articles:   1
Comments:   0
More Articles
2 comments   /   posted on Jan 26, 2009
Categories:   General

Note: This article is submitted by Jeff Paries for Silverlight: Write and Win contest.Thanks a lot, Jeff! Hello All, Please drop a comment if you like it.

This article is compatible with the latest version of Silverlight.

Introduction

There have been many requests online regarding methods to go about sending content to a user from within a Silverlight application. Many Silverlight users have asked for a “Save File” dialog that can be leveraged to pass files out of an application. The technique described in this tutorial shows how you can leverage a standard HTML right-click dialog to allow a user to select the “Save Link/Target As” menu and pass any type of file from any type of object to the user, allowing them to save the file to any location they wish.

A project that demonstrates this technique can be seen here.

The source code for the project above can be downloaded here.

Step #1: Prepare the Silverlight Object

Add a “windowless” parameter to the Silverlight object tag in the hosting page:

<param name="windowless" value="true"  />

Since we will be leveraging an HTML div to collect the right-click, the Silverlight application needs to be made windowless, or else the div tag will not show on top of the application.

Step #2: Prepare the Silverlight control host

Add the following div before the closing div tag of the SilverlightControlHost within the hosting page:

<div id="RightClick" style="position:absolute; border-color:#FF0000;">
    <a id="ObjectLink" href="" target="_blank"><img id="LinkImage" src="linkImage.gif" border="0" /></a>
</div>

This HTML block creates a div named “RightClick”, within which is a transparent .gif image. We will be adding code that relocates and resizes these objects when the mouse is placed over an object within the Silverlight application.

Step #3: Prepare the page

This technique leverages HTML objects via JavaScript, so it is necessary to add a bit of JavaScript to the page. In the demonstration project, this script can be found in the ContentSharingTestPage.html file, and is shown below for reference.

The XOffset and YOffset variables are used to maintain the location of the SilverlightControlHost div on the page. In this example, the div is centered via auto margins – if the browser is stretched, the div will move to remain centered, and the offsets need to be updated. The “findPos” function does the work of calculating the offsets.

The “SetDiv” function will be called from C#, and needs to be passed an object’s X and Y location, width, height, the link target (file to be downloaded) and alt tag content. Notice that this function starts out by calling the “findPos” function. This will ensure the offsets are always current.

Once the offsets have been updated, the function changes the width and height of the image within the anchor tag to match the size passed to the function. Next, the div containing the anchor tag is moved to match the absolute position of the object within the application space. Finally, the link target (href) and title tags are updated with the passed information.

This function will be called on the “MouseEnter” event for any right-click enabled object within an application. The single div/anchor tag combination will work to enable the right-click menu for as many objects as necessary.

<script language="javascript" type="text/javascript">
    
    var XOffset = 0;
    var YOffset = 0;
 
    // adapted from:
    // http://www.quirksmode.org/js/findpos.html
    function findPos(obj) {
        var curleft = curtop = 0;
 
        if (obj.offsetParent) {
            do {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
            } while (obj = obj.offsetParent);
        }
 
        XOffset = curleft;
        YOffset = curtop;
    }
 
    function SetDiv(LocX, LocY, ObjW, ObjH, Link, AltTag) {
 
        findPos(document.getElementById("silverlightControlHost"));
        
        linkImg = document.getElementById("LinkImage");
        linkDiv = document.getElementById("RightClick");
 
        linkImg.style.width = ObjW + "px";
        linkImg.style.height = ObjH + "px";
 
        linkDiv.style.left = LocX + XOffset + 'px';
        linkDiv.style.top = LocY + YOffset + 'px';
 
        document.getElementById('ObjectLink').href = Link;
        document.getElementById('ObjectLink').title = AltTag;
    }
 
</script>

Step #4: Prepare your objects

Decide which objects within your application will be used to share content. In the example project, hyperlinks, images, and XAML controls are all demonstrated.

Name the objects that will be right-click enabled in a meaningful way, as the object name will be passed to the HTML anchor tag as a “title” attribute (tooltip). In the demonstration app, object names use underscores, which are replaced with spaces when passed to the scripts on the hosting page.

Add content to the objects “Tag” field that specifies the name of the file to download. If the object is a hyperlink button, use the “NavigateURI” field instead – this will cover the case where a user may simply click a link rather than right-clicking. We’ll handle the checking for object type via code.

To get an idea of how they look, following is the XAML for one of the image objects within the sample application:

<Image Margin="0,0,0,0" 
       Grid.ColumnSpan="1" 
       Grid.Row="1" 
       Source="crab.jpg" 
       Cursor="Hand" 
       x:Name="Crab_Image" 
       Grid.Column="1" 
       Tag="crab.jpg" />
    

Step #5: Enable browser access from code

Since you will be calling the JavaScripts on the hosting page, we’ll need to make use of the browser library within the Silverlight application. In the demonstration project, the following using was added near the top of the Page.xaml.cs file:

using System.Windows.Browser;

Step #6: Add event listeners

Within the Page() constructor, add “MouseEnter” listener to the objects you wish to have right-click enabled. For the image shown in step 4, the event listener looks like the following:

Crab_Image.MouseEnter += new MouseEventHandler(Object_MouseEnter);

Why MouseEnter?

The MouseEnter event is used to make an object “hot”. As soon as the mouse enters an object, we will use code to move the HTML div between the mouse and the underlying object. This means it will be in place to intercept any right-clicks a user may make.

Step #7: Add the MouseEnter event handler

The MouseEnter event handler in the demonstration program is generalized – it’s a single method used for all right-click enabled objects in the application. From the event listeners, you can see that it supports twelve right-click enabled objects within the application.

The event handler starts out by grabbing the sender as both a FrameworkElement and UIElement. This is done to gain access to all of the necessary methods and properties.

FrameworkElement s = sender as FrameworkElement;
UIElement anObj = sender as UIElement;

The next few lines of code transform the coordinates of an object to an absolute coordinate within the Silverlight application. This means even objects nested within various container types will return their XY offset within the application.

UIElement root = App.Current.RootVisual;
GeneralTransform gt = anObj.TransformToVisual(root);
Point LinkPoint = gt.Transform(new Point(0, 0));

The function then declares a string to hold the object’s Tag field, and checks to see if the object type is a HyperlinkButton. If the object is a HyperlinkButton, the NavigateUri field is assigned to the string, otherwise, the Tag field is used. Recall that the Tag or NavigateUri fields contain the name of the file that will be downloaded.

string theTag = "";
 
if (sender.GetType().ToString() == "System.Windows.Controls.HyperlinkButton")
{
    HyperlinkButton hb = sender as HyperlinkButton;
    theTag = hb.NavigateUri.ToString();
}
else if (s.Tag != null)
{
   theTag = s.Tag.ToString();
}
else
{
   theTag = "";
}

The next step is to form the file location. In the demonstration application, a “content” folder was created within the ClientBin folder. All of the downloadable assets are placed in the “content” folder. This code creates a string that will be passed to the JavaScript, and assigned to the anchor tag’s href property.

string downloadString = "ClientBin/content/" + theTag;

Finally, the object’s location, width, height, download file location and name are passed to a function called CallSetDiv that will call out to the JavaScript. Note the character replacement in the name string – swapping out underscores for spaces so the anchor’s title tag looks appropriate.

CallSetDiv(LinkPoint, s.ActualWidth, s.ActualHeight, downloadString, s.Name.Replace("_", " "));

Step #8: Call the JavaScript

The CallSetDiv method is separated, but could easily be included as part of the event handler if necessary. This method accepts the passed variables, and calls to the “SetDiv” script on the page. The only item of note within this method is that the value of the XY location points are rounded up – the HTML DOM does not recognize sub-pixels.

Summary

So there you have it – any file type, from any object, at any time, to anywhere the user wants to save, in 8 steps. It’s relatively easy to leverage HTML and JavaScript to achieve the desired results, and there are some additional benefits. In the case of hyperlinks, a left-click will open an image or invoke the browser’s own save file dialog in the case of a .zip file. For other known file types (such as .wmv), the default browser behavior will come into play.


Subscribe

Comments

  • -_-

    RE: Save the Web


    posted by Zákányi Balázs on Jan 27, 2009 06:34

    awsome

  • -_-

    RE: Save the Web


    posted by Jeff GArrison on Feb 07, 2009 19:13
    That is awesome. This gives me a LOT of ideas for other uses :) thanks

Add Comment

Login to comment:
  *      *       
Login with Facebook