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 a DotNetNuke™ version.
This article will explore the following topics:
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, membership and role management, as well as providing email and logging support.
To install or upgrade SilverlightDesktop.net:
Further installation directions can be found at the following links:
When you log into the SilverlightDesktop.net administration with the username and password created during the installation, you will have access to it's features:
The security is implemented as follows:
The SilverlightDesktop.net launches the Silverlight control. It uses code to pass the InitParameters that contain 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) { // Load the main control 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) { // from http://silverlight.net/forums/p/14941/49437.aspx (BlueAquarius) // From http://joel.neubeck.net/2008/03/silverlight-how-to-on-demand-assembly-deployment/ 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; } // Pass the parameters to the control that it will need to connect to the website 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(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.
// Get the SilverlightKey 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)) { // The correct IP address was used // To prevent a brute force attack scramble the password // A hacker is now chasing a moving target SetSilverlightKey(); } return }
Each time the user logs into the SilverlightDesktop.net site they are given a new temporary password.
Modules can be installed by uploading a .zip file. The package manifest is described here: Creating a module package
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 SilverlightDesktop.net source consists of 5 projects:
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 shows the code used to manipulate the Left side:
#region LeftSide private void LeftSide_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { //Start Drag FrameworkElement LeftSide = (FrameworkElement)sender; LeftSide.CaptureMouse(); // Set the starting point for the drag 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) { //Stop Drag 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
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:
// Log the event 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 is also available in a version that runs inside DotNetNuke™ . More information can be found here: