Save Image Button

Developer
Jun 22, 2009 at 7:56 AM

One of the common complaints (or features, depending on your perspective) of Silverlight photo galleries is that the viewer is no longer able to right click on the image and save it to their computer.  If you are hosting copyrighted content, that can be great.  But, if you are hosting a personal photo album and want to allow your family members and friends to download the pictures on your site, Silverlight photo albums like Slide.Show 2 aren't user friendly in that regard.  For my personal website, adding this ability to Slide.Show was an important feature requirement.  Besides, I figured I’d learn something about Silverlight in the process.  I just didn’t realize how much…

The purpose in writing this discussion entry is to a) provide details about how I added this requirement and b) to share some rather painful lessons learned that I had to overcome along the way so that you, dear reader, might be spared from my pain.

My first attempt at doing this involved creating a new Save PathButton in the Navigation tray that would download the current image by calling an image download handler (.ashx) on my web site using HtmlPage.Window.Navigate.  This worked great for Firefox.  But unfortunately, IE does not recognize the file download from the Silverlight app as user initiated and will block the first download attempt and prompt the user with the familiar file download notification bar.  If the user accepts to download the file, the Silverlight app actually restarts.  The user then has to manually navigate back to the picture they want to download and then download it again.  Awesome.  (Does anyone else find it utterly absurd that Firefox behaves correctly but IE doesn’t?)  Subsequent downloads within the same IE session don't initiate the file download bar, so the user could now download the file.  This is obviously not an ideal behavior, and with unsophisticated computer users, they may not think to try again (when the Silverlight app restarts, they might just think the download feature doesn't work and give up). 

I attempted to solve this problem by having the app save its current state to isolated storage when attempting to download a file, and then restoring the state back to the correct album and slide and auto-initiating the file download when the Silverlight app restarts.  There was no way to prevent the IE file download notification on the first download, but if the user chooses to download the file, the Silverlight app would restore itself, initiate the download, and basically work as you'd expect.

Or so I thought.  It worked fine for that first download in IE where the user would get blocked by the worthless file download “security” bar, but there were a number of problems.  If the user downloaded a second file, I was still storing the state.  However, the app wasn’t restarting.  So the saved state information wasn’t getting cleared.  This meant that when the user closed their browser and then came back to the photo gallery a couple of minutes, days, weeks, later, it would immediately try to restore back to the state when they last downloaded a file and try to re-download it.  I also had to account for the fact that Firefox didn’t have the problem and didn’t need to ever save or restore the state.  Dealing with Firefox was easy (using the HtmlPage.BrowerInformation to detect the browser type), but I couldn’t come up with a way to reliably restore the state in IE.  I couldn’t count on it being needed only the first time a file was downloaded, because if they closed the file download bar instead of downloading the file, then clicked save again they would again get hit with the file download bar.  As far as I could determine, there was no way to determine when the file download bar might interfere.  I only needed to save and restore state if the file download bar ACTUALLY interfered, so this was a major problem.  So, yes, I could save and restore the state, but it didn’t do me any good because I didn’t know exactly when I needed to do this.

OK, so after a lot of floundering, I stumbled onto the solution.  It turns out that if I initiated the call to the image download handler by dynamically setting the NavigateUri property on a HyperlinkButton wrapped around the PathButton, IE would work correctly.  If the call initiated from a HyperlinkButton instead of HtmlPage.Window.Navigate, IE wouldn’t throw up that stupid file download bar (which does NOTHING to improve security, btw, as the user is already prompted for what they want to do with the file – Microsoft really needs to learn that throwing up annoying prompts does not actually improve security), and would work just like Firefox.  So, the Silverlight app doesn’t restart and there is no need to save state at all.  Woo-hoo!

Because this is fairly involved, I have entered a new item in the Issue Tracker and have attached the modified files to that issue (Work Item #3323  - “Save Image button”, http://slideshow2.codeplex.com/WorkItem/View.aspx?WorkItemId=3323).  You can download all of the modified files from there.  You can then just overwrite the files available with the base download package with these and recompile the Slide.Show project, or you can use a tool like WinMerge to merge in the changes into your code.

Themes\Generic.xaml

·         Add a HyperlinkButton element named SaveHyperlinkButtonElement with an embedded disabled PathButton element called SaveButtonElement to the NavigationTray before the AlbumButtonElement

Navigation\Navigation.cs

·         Modify the ToggleAlbumView method in Navigation.cs to disable the Save Hyperlink Button when in album view.

Controls\ImageViewer.cs

·         Modify OnShourceChanged to set SaveHyperlinkButtonElement’s NavigateUri property

Controls\NavigationTray.cs

·         Add TemplateParts for SaveHyperlinkButton and SaveButton before the class declaration

·         Add code to the NavigationTray() constructor to set up the ToggleSaveButton options

·         Add HyperlinkButton and PathButton properties in the Template Parts region

·         Add the SaveHyperlinkButton and SaveButtonElement public properties

·         Add the SaveButton properties in the Dependency Properties region

·         Modify OnApplyTemplate to setup the SaveHyperlinkButton and SaveButton

Configuration\Configuration.cs

·         Add new ToggleSaveButtonOptions property to Options class.

·         Add new SaveButtonOptions property to Options class.

·         Add new ToggleSaveButtonOptions subclass to Options class.

·         Add new SaveButtonOptions subclass to Options class

Configuration\XmlConfigurationProvider.cs

·         Add new ToggleSaveButton Options.

Configuration\*Theme.cs

·         Set the default Options.ToggleSaveButton options in each of the themes.

·         NOTE:  The DownloadImageHandler property is an ABSOLUTE URI to the image download handler on your web site.  It is NOT a relative URI based on the Silverlight app’s location like most references are.  BE SURE TO UPDATE THE THEME FILES to set the default DownloadImageHandler appropriately for your site, or overwrite it using XML configuration.

DownloadImage.ashx

·         A new image download handler that should be added to your site.  It takes the relative path to the image to download as a query string parameter.  I have this located in the same directory as the silde.show .xap file.  Any image download handler or page will work, I just offer this one up as an example if you don’t already have one.  Note that you should modify DowhloadImage.ashx.cs so that the paths are correct for your web site.

 

I hope this helps,

--Scott

 

Developer
Jun 24, 2009 at 10:49 PM

Based on feedback from another user, a couple of additional modifications were required:

Page.xaml.cs

--Add event handlers for OnMouseEnter and OnMouseLeave events for the SaveHyperLinkButtonElement.

Additional tweaks have been made to the Generic.xaml and theme files.  See the issue tracker for the latest files.

Developer
Jul 6, 2009 at 4:47 AM

 Fixed in change set 25804. Note that the change set includes additional enhancements and bug fixes as well.

http://slideshow2.codeplex.com/SourceControl/changeset/view/25804
Note that in the change set code, the default for all themes is to have the new button disabled by default. This can be overwritten using a custom theme or via XmlConfiguration (which was also fixed as part of the change set).