Introduction
Note: You should always download the latest version of SilverlightDesktop.net from http://SilverlightDesktop.net.
SilverlightDesktop.net is a Open-Source ASP.NET framework that allows you to dynamically load Silverlight modules into resizable draggable windows. It will also load modules without a window for easy integration into existing websites. SilverlightDesktop.net is available as a stand-alone program and as a DotNetNuke™ version.
This article will explore the following topics:
- What is SilverlightDesktop and why do I want to use it?
- Installing
- Features
- Security
- Custom modules
- Installing modules
- Custom module development
- The Core
- SilverlightDesktop Project
- Movable draggable windows
- SilverlightCore Project
- SilverlightDesktop.net for DotNetNuke™
What is SilverlightDesktop and why do I want to use it?
Microsoft Silverlight runs client-side; meaning, it does not run on the web server, it runs in a user's web browser. You have to have a method to communicate between the web server that launches the Silverlight application and the Silverlight application running in the user's web browser. This communication is usually performed using Web Services.
When you create Silverlight applications, you may discover that you need security, membership management, email, and logging. Implementing these supporting elements can require more time than creating the Silverlight application itself.
SilverlightDesktop.net provides a framework that allows you to create modules that contain only the custom functionality you need. SilverlightDesktop.net handles the security, and membership and role management, as well as provides email and logging support.
Installing SilverlightDesktop.net
To install or upgrade SilverlightDesktop.net:
- Create a database
- Unzip the SilverlightDesktop installation file into a directory on your hard drive
- Configure the IIS web server to point to the directory (or open the directory in Visual Studio 2008 and run the application to use its built-in web server)
- Open the site in the web browser and follow the wizard to complete the installation
Further installation directions can be found at the following links:
SilverlightDesktop.net Features
When you log into the SilverlightDesktop.net administration with the username and password created during the installation, you will have access to its features:
- A password protected administration section that allows you to configure options such as allowed password attempts and email settings.
- A user manager that allows you to create and edit users as well as set their role and active status.
- Module settings that allow you to configure modules you create or upload. You can find modules to upload on the SilverlightDesktop.net website.
- Multiple Desktop instances that you can create and configure. The AutoLoad module feature allows a single module to be shown without the movable resizable window.
- The Application Log provides administrators with an overview of the application events
SilverlightDesktop.net Security
The security is implemented as follows:
The SilverlightDesktop.net launches the Silverlight control. It uses code to pass the InitParameters
that contains a temporary password (it also stores the IP address).
string strIPAddress = this.Context.Request.UserHostAddress;
int intPassword = Authendication.SetSilverlightKey(_UserID, strIPAddress);
string strWebServiceBase = GetWebServiceBase();
SilverlightDesktop.InitParameters =
String.Format("PortalID={0},ModuleId={1},UserID={2}," +
"Password={3},WebServiceBase={4},AutoLoadModule={5}",
"-1", _DesktopID.ToString(), _UserID.ToString(),
intPassword.ToString(), strWebServiceBase, AutoLoadModule);
In the App.xaml.cs file in the SilverlightDesktop project (the Silverlight application that creates the windows that the other Silverlight modules are loaded into), the code retrieves the parameters...
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Page(e.InitParams["PortalID"],
e.InitParams["ModuleId"],
e.InitParams["UserID"],
e.InitParams["Password"],
e.InitParams["WebServiceBase"],
e.InitParams["AutoLoadModule"]);
}
... and passes them to the Silverlight custom module (when the custom module is dynamically loaded). It passes the parameters to the custom module through its .Tag
property.
private void loadAssembly(Stream s)
{
try
{
Uri uri = new Uri(strXAPName.Replace(".xap", ".dll"), UriKind.Relative);
StreamResourceInfo xapPackageSri = new StreamResourceInfo(s, null);
StreamResourceInfo assemblySri = Application.GetResourceStream(xapPackageSri, uri);
AssemblyPart assemblyPart = new AssemblyPart();
Assembly a = assemblyPart.Load(assemblySri.Stream);
UserControl objUserControl = (UserControl)a.CreateInstance(strAssemblyClassName);
if (objUserControl == null)
{
debug("objUserControl is null " + strAssemblyClassName);
return;
}
objUserControl.Tag = string.Format("{0}, {1}, {2}, {3}, {4}",
intPortalID, intModuleId, intUserID, strPassword, strWebServiceBase);
if (UseWindows)
{
SilverlightWindowControl objSilverlightWindowControl =
new SilverlightWindowControl(objSilverlightDesktopModule.WindowSize);
objSilverlightWindowControl.Window.Content = objUserControl;
objSilverlightWindowControl.WindowName.Text =
objSilverlightDesktopModule.ModuleName;
Canvas.SetLeft(objSilverlightWindowControl, (double)intWindowLocation);
Canvas.SetTop(objSilverlightWindowControl, (double)intWindowLocation);
this.LayoutRoot.Children.Add(objSilverlightWindowControl);
}
else
{
this.LayoutRoot.Children.Add(objUserControl);
}
}
catch (Exception ex)
{
debug("Exception when loading the assembly part:");
debug(ex.ToString());
}
}
When a web service call is made by the custom module, the temporary password is passed to the Web Service.
#region ShowWhoAmI
private void ShowWhoAmI()
{
BasicHttpBinding bind = new BasicHttpBinding();
EndpointAddress MyEndpointAddress =
new EndpointAddress(strWebServiceBase + "WhoAmI.asmx");
var proxy = new WhoAmISoapClient(bind, MyEndpointAddress);
proxy.WhoIAmCompleted +=
new EventHandler<whoiamcompletedeventargs>(proxy_WhoIAmCompleted);
proxy.WhoIAmAsync(intPortalID, intModuleId, intUserID, strPassword);
}
void proxy_WhoIAmCompleted(object sender, WhoIAmCompletedEventArgs e)
{
UserInfo UserInfo = (UserInfo)e.Result;
DisplayWhoIAm(UserInfo);
}
private void DisplayWhoIAm(UserInfo UserInfo)
{
this.txtFirstName.Text = UserInfo.FirstName;
this.txtLastName.Text = UserInfo.LastName;
this.txtEmail.Text = UserInfo.Email;
}
#endregion
The Web Service passes this temporary password and the IP address of the Silverlight application calling it (remember, the Silverlight application is running in the user's web browser) to the Authentication
class that is part of he SilverlightDesktopCore project.
#region WhoIAm
[WebMethod(Description = "WhoIAm")]
[ScriptMethod()]
public UserInfo WhoIAm(int PortalID, int ModuleId, int UserID, string Password)
{
string strIPAddress = this.Context.Request.UserHostAddress;
SilverlightDesktopAuthendicationHeader
SilverlightDesktopAuthendicationHeader =
new SilverlightDesktopAuthendicationHeader();
SilverlightDesktopAuthendicationHeader.PortalID = PortalID;
SilverlightDesktopAuthendicationHeader.UserID = UserID;
SilverlightDesktopAuthendicationHeader.Password = Password;
SilverlightDesktopAuthendicationHeader.ModuleId = ModuleId;
SilverlightDesktopAuthendicationHeader.IPAddress = strIPAddress;
UserInfo response = new UserInfo();
Authendication Authendication =
new Authendication(SilverlightDesktopAuthendicationHeader);
if (Authendication.IsUserValid("WhoAmI"))
{
response = Authendication.GetUserInfo();
}
else
{
response.FirstName = "Visitor";
response.LastName = DateTime.Now.ToLongDateString();
}
return response;
}
#endregion
The Authentication
class compares the username and password to determine if the user should be authenticated (note: Strings.Left(result.IPAddress,6)
is used to only match the first part of an IP address in case a user is behind a proxy server that is constantly changing the IP address).
var SilverlightKey =
(from ASilverlightDesktopUser in SilverlightDesktopDAL.SilverlightDesktopUsers
where ASilverlightDesktopUser.UserID == _UserID
select ASilverlightDesktopUser).FirstOrDefault();
if (SilverlightKey.SilverlightKey == ConvertToSilverlightkey(_Password) &
Strings.Left(SilverlightKey.IPAddress, 6) == Strings.Left(_IPAddress, 6))
{
return true;
}
else
{
if (Strings.Left(SilverlightUser.IPAddress,6) == Strings.Left(_IPAddress,6))
{
SetSilverlightKey();
}
return
}
Each time the user logs into the SilverlightDesktop.net site, they are given a new temporary password.
Custom Modules
Modules can be installed by uploading a .zip file. The package manifest is described here: Creating a module package.
Custom Module Development
Custom modules are created by making a Silverlight project that creates a .xap file.
The .xap is stored in the ClientBin directory of the SilverlightDesktop website. Other module elements such as Web Services and data access layers can also be created.
The module is configured using the Module settings in administration. More information on creating modules can be found at this link.
The Core
The SilverlightDesktop.net source consists of five projects:
- SilverlightDesktop_Web - This project contains the website that users interact with. This website is contained in the "Install Version" of SilverlightDesktop.net.
- SilverlightDesktop - This project contains the Silverlight project that dynamically loads the Silverlight custom modules and optionally places them in movable resizable windows.
- SilverlightDesktopCore - This project contains the authentication, email, and logging features. This project can also be referenced by the server-side code in custom modules.
- WhoAmI - A sample Silverlight custom module that displays the identity of the currently logged in user.
- wsWhoAmI - A sample Web Service project for the WhoAmI sample custom module.
SilverlightDesktop Project
The SilverlightDesktop project is compiled into SilverlightDesktop.xap and resides in the ClientBin directory of the SilverlightDesktop website. It is loaded when a visitor views their SilverlightDesktop or an AutoLoad module. When displaying a module in the SilverlightDesktop window mode, the project loads the class specified in the module definition, in the SilverlightWindowControl
.
The SilverlightWindowControl
consists of a collection of Canvas
controls, Rectangle
controls, and Window
controls arranged to resemble a resizable window control. Event handlers are wired-up to the various elements to support the drag and resizing functionality.
The following is the code used to manipulate the left side:
#region LeftSide
private void LeftSide_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement LeftSide = (FrameworkElement)sender;
LeftSide.CaptureMouse();
StartingDragPoint = e.GetPosition(LeftSide);
Point ControlPoint = e.GetPosition(this);
StartingDragPoint.X = StartingDragPoint.Y - ControlPoint.Y - .5;
LeftSide.MouseMove += new MouseEventHandler(LeftSide_MouseMove);
LeftSide.MouseLeftButtonUp +=
new MouseButtonEventHandler(LeftSide_MouseLeftButtonUp);
}
void LeftSide_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
FrameworkElement LeftSide = (FrameworkElement)sender;
LeftSide.ReleaseMouseCapture();
LeftSide.MouseMove -= new MouseEventHandler(LeftSide_MouseMove);
LeftSide.MouseLeftButtonUp -=
new MouseButtonEventHandler(LeftSide_MouseLeftButtonUp);
}
void LeftSide_MouseMove(object sender, MouseEventArgs e)
{
Point LeftSidePoint = e.GetPosition(LeftSide);
double PointDifference = LeftSidePoint.X - StartingDragPoint.X;
if (PointDifference < 0)
{
PointDifference = PointDifference * -1;
if (LeftSidePoint.X < (Window.Width + (PointDifference) - 60))
{
this.SetValue(WidthProperty, (double)(PointDifference));
Window.SetValue(WidthProperty, (double)Window.Width + (PointDifference));
DragStackPanel.SetValue(WidthProperty,
(double)DragStackPanel.Width + (PointDifference));
DragBar.SetValue(WidthProperty, (double)DragBar.Width + (PointDifference));
TopSide.SetValue(WidthProperty, (double)TopSide.Width + (PointDifference));
BottomSide.SetValue(WidthProperty,
(double)BottomSide.Width + (PointDifference));
Canvas Canvas = (Canvas)this.Parent;
Point Point = e.GetPosition(Canvas);
Canvas.SetLeft(this, Point.X - StartingDragPoint.X);
Canvas.SetLeft(RightSide, (double)TopSide.Width + StartingDragPoint.X);
}
}
else
{
if (LeftSidePoint.X < (Window.Width - (PointDifference) - 60))
{
this.SetValue(WidthProperty, (double)(PointDifference));
Window.SetValue(WidthProperty, (double)Window.Width - (PointDifference));
DragStackPanel.SetValue(WidthProperty,
(double)DragStackPanel.Width - (PointDifference));
DragBar.SetValue(WidthProperty, (double)DragBar.Width - (PointDifference));
TopSide.SetValue(WidthProperty, (double)TopSide.Width - (PointDifference));
BottomSide.SetValue(WidthProperty,
(double)BottomSide.Width - (PointDifference));
Canvas Canvas = (Canvas)this.Parent;
Point Point = e.GetPosition(Canvas);
Canvas.SetLeft(this, Point.X - StartingDragPoint.X);
Canvas.SetLeft(RightSide, (double)TopSide.Width + StartingDragPoint.X);
}
}
}
#endregion
SilverlightDesktopCore Project
The SilverlightDesktopCore project is used by the main SilverlightDesktop website, but can also be referenced by custom modules.
Some of the available classes and methods in the SilverlightDesktopCore project are shown above.
The primary functionality it provides custom modules is authentication, logging:
ApplicationLog.AddToLog(String.Format("Module: {0} - File deleted {1}.",
objModules.FirstOrDefault().ModuleName, strFileToDelete));
and emailing:
#region Send Test Email
protected void lnkTestEmail_Click(object sender, EventArgs e)
{
string[] arrAttachments = new string[0];
string strEmailResponse = Email.SendMail(txtSMTPFrom.Text.Trim(),
txtSMTPFrom.Text.Trim(), "", "",
txtSMTPFrom.Text.Trim(), MailPriority.Normal,
"SilverlightDesktop Email", Encoding.UTF8,
"A test email sent from SilverlightDesktop",
arrAttachments, txtSMTPEmailServer.Text.Trim(),
rbAuthendication.SelectedValue, txtSMTPUsername.Text.Trim(),
txtSMTPPassword.Text.Trim(), chkSecureAccess.Checked);
lblUpdated.Text = (strEmailResponse.Trim() == "") ?
"Email Sent." : strEmailResponse;
}
#endregion
SilverlightDesktop.net for DotNetNuke™
SilverlightDesktop.net is also available in a version that runs inside DotNetNuke™. More information can be found here:
Note: Both versions require ASP.NET 3.5 or higher and SQL Server (or Express) 2005 or higher.