Introduction
TeamLive (the project I have been working on recently) blends ASP.NET, Ajax and Silverlight in a number of places and I was interested to see how far I could push the HTML/Silverlight blend - the lightbox seemed like a good component for my experiment.
For those of you wondering, a Lightbox is an effect that fades the page in the background to show you new content in the foreground.
This article isn't intended to be a Silverlight tutorial. If you are new to this technology, then I recommend reading some of the beginners' tutorials before using this code.
Background
TeamLive blends ASP.NET, Ajax and Silverlight in a number of places. E.g. during a multi-file upload, we adjust the OBJECT's styles giving it more screen space for displaying the progress bars.
One of the controls I have been working on for the next iteration of TeamLive is a lightbox to help users navigate their image grids.
Some of the requirements for the lightbox are listed below:
- Only load the big image if needed (duh!)
- Small xap (the js lightboxes we've used in the past have required a lot of js and/or associated images) - this xap is only 8K
- Only one Silverlight lightbox object per page (we didn't want lots of objects being created)
- Simple to use (drop a xap on a page and call some js)
- Degrade cleanly when Silverlight 2 is not present
Demo
If you have Silverlight 2 installed, visit http://www.devprocess.com and click one of the images...
Back?
I hope you saw something like this:
If you just got an ugly window.open
, then you don't have Silverlight 2.
Using the Code
Firstly, the PageBlanket
Silverlight class. A simple class that can be used to disable the page with an overlay.
A similar technique is used by the modal popup overlay (see the ModalPopup
on the AjaxControlToolkit: http://www.asp.net/AJAX/AjaxControlToolkit/Samples/ModalPopup/ModalPopup.aspx)
public class PageBlanket
{
HtmlElement _blanket = null;
private void Initialize()
{
if (_blanket == null)
{
_blanket = HtmlPage.Document.CreateElement("div");
HtmlPage.Document.Body.AppendChild(_blanket);
_blanket.SetStyleAttribute("position", "fixed");
_blanket.SetStyleAttribute("top", "0px");
_blanket.SetStyleAttribute("left", "0px");
_blanket.SetStyleAttribute("height", "100%");
_blanket.SetStyleAttribute("width", "100%");
_blanket.SetStyleAttribute("backgroundColor", "#808080");
_blanket.SetStyleAttribute("filter", "alpha(opacity=50)");
_blanket.SetStyleAttribute("opacity", "0.5");
_blanket.SetStyleAttribute("z-index", "3");
_blanket.SetStyleAttribute("display", "none");
}
}
public void Show()
{
Initialize();
_blanket.SetStyleAttribute("display", "");
}
public void Hide()
{
_blanket.SetStyleAttribute("display", "none");
}
}
Page blanket creates a div
, sets the style, jams it in the page, and toggles the display style to show/hide the div
. This effectively disables the page while we do the Silverlight stuff.
Dynamically showing/hiding the Silverlight control turned out to be harder.
Firefox gets upset if you start moving the control around, changing zIndex
, altering display style dynamically.
I was getting working code in Internet Explorer 7 and “Trying to get unsupported property on scriptable plugin object!” errors in Firefox.
The solution I found (works on Internet Explorer 7, Firefox2 and Safari (not tested on anything else yet)) was to set a small Width and Height (yeah, yeah - I know). I make sure I set the style of the object at the start (changing position and zIndex
dynamically in the C# screwed up the scriptableobject
).
The Lightbox Code
Note it positions the lightbox in the center of the screen. Originally the code positioned the lightbox over the item that launched it - but it never looked quite right so I tried centering the lightbox. I’m not sure it’s much better in the center - so I’m waiting for inspiration.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Windows.Media.Imaging;
namespace dpLightbox
{
[ScriptableType]
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Page_Loaded);
HtmlPage.Window.AttachEvent("onresize", new EventHandler(Window_Resize));
}
void Window_Resize(object sender, EventArgs e)
{
PositionToCenter();
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.RegisterScriptableObject("LightboxController", this);
bClose.MouseEnter += new MouseEventHandler(bClose_MouseEnter);
bClose.MouseLeave += new MouseEventHandler(bClose_MouseLeave);
bClose.MouseLeftButtonUp +=
new MouseButtonEventHandler(bClose_MouseLeftButtonUp);
}
bool closing = false;
void bClose_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
closing = true;
rCloseGlow.Opacity = 0.0;
sbLightbox.Completed += new EventHandler(sbLightbox_Completed);
sbLightbox.AutoReverse = true;
sbLightbox.Stop();
sbLightbox.Begin();
sbLightbox.Seek(new TimeSpan(0, 0, 0, 0, 500));
}
void sbLightbox_Completed(object sender, EventArgs e)
{
if (closing)
{
_blanket.Hide();
Width = 0;
Height = 0;
HtmlPage.Plugin.SetStyleAttribute("width", "1px");
HtmlPage.Plugin.SetStyleAttribute("height", "1px");
}
closing = false;
}
void bClose_MouseLeave(object sender, MouseEventArgs e)
{
rCloseGlow.Opacity = 0.0;
}
void bClose_MouseEnter(object sender, MouseEventArgs e)
{
rCloseGlow.Opacity = 1.0;
}
double paddingTop = 55;
double padding = 25;
PageBlanket _blanket = new PageBlanket();
[ScriptableMember]
public void showLightbox(string imgTitle, string imgSrc,
double imgWidth, double imgHeight)
{
_blanket.Show();
txtTitle.Text = imgTitle;
imgMain.Source = new BitmapImage
(new Uri(imgSrc, UriKind.RelativeOrAbsolute));
imgMain.ImageFailed += new ExceptionRoutedEventHandler(imgMain_ImageFailed);
double dWidth = imgWidth + (padding * 2);
double dHeight = imgHeight + (padding + paddingTop);
Width = dWidth;
Height = dHeight;
kfWidth.Value = dWidth;
kfHeight.Value = dHeight;
HtmlPage.Plugin.SetStyleAttribute("height", Height.ToString() + "px");
HtmlPage.Plugin.SetStyleAttribute("width", Width.ToString() + "px");
sbLightbox.Stop();
sbLightbox.Seek(new TimeSpan(0));
sbLightbox.AutoReverse = false;
sbLightbox.Begin();
HtmlPage.Plugin.SetStyleAttribute("display", "");
PositionToCenter();
}
void imgMain_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
}
private void PositionToCenter()
{
Size clientBounds = BrowserHelper.GetClientBounds();
Point ptScrollPos = BrowserHelper.GetScrollPosition();
double dTop = ptScrollPos.Y + ((clientBounds.Height - Height) / 2);
double dLeft = ptScrollPos.X + ((clientBounds.Width - Width) / 2);
HtmlPage.Plugin.SetStyleAttribute("top", dTop.ToString() + "px");
HtmlPage.Plugin.SetStyleAttribute("left", dLeft.ToString() + "px");
}
}
}
Using the Lightbox
In your aspx:
<asp:Silverlight ID="slLb" runat="server"
Source="~/ClientBin/dpLightbox.xap" Version="2.0"
Width="1px" Height="1px" PluginBackground="Transparent" Windowless="true"
style="position:absolute;z-index:4;">
<PluginNotInstalledTemplate></PluginNotInstalledTemplate>
</asp:Silverlight>
The PluginNotInstalledTemplate
gets rid of the install Silverlight 2 prompt when Silverlight 2 isn’t installed - which is nice as the Lightbox
isn’t essential functionality.
Put this bit of JavaScript in the head to simplify usage. This gets the Silverlight xap to show the lightbox or (shudder) a window.open
if Silverlight 2 isn’t there.
<script type="text/javascript">
function lightbox(imgTitle,imgSrc,imgWidth,imgHeight)
{
var lbContent=document.getElementById("slLb").Content;
if(lbContent!=null)
{
lbContent.LightboxController.showLightbox
(imgTitle,imgSrc,imgWidth,imgHeight);
}
else
{
window.open(imgSrc,"noSilverlight","menubar=no,
toolbar=no,height="+imgHeight+",width="+imgWidth);
}
}
</script>
Showing a lightbox - to show a lightbox call the function above.
<a href="javascript:lightbox('TeamLive designer',
'http://www.devprocess.com/App_Themes/Default/lb_designer.jpg',600,296);">
<img src="App_Themes/default/designer.jpg" class="photosmall"
width="264" height="130" alt="Screenshot from devProcess TeamLive" />
</a>
You can keep up to date with any updates (I will also try to make them here) at our blog: TeamLive Blog.
History
- 28th April, 2008: Article submitted