Click here to Skip to main content
Click here to Skip to main content

Introducing SilverlightDesktop.net

, 19 Oct 2008 BSD
Rate this:
Please Sign up or sign in to vote.
SilverlightDesktop.net is a Open-Source ASP.NET framework that allows you to dynamically load Silverlight modules into resizable draggable windows.

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
      • Email
      • Logging
  • 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)
{
    // 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<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).

//  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.

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)
{
    //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

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:

// 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 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.

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

defwebserver
Software Developer (Senior) http://ADefWebserver.com
United States United States
Michael Washington is a Microsoft MVP. He is a ASP.NET and
C# programmer.
He is the founder of
LightSwitchHelpWebsite.com

He has a son, Zachary and resides in Los Angeles with his wife Valerie.

He is the Author of:
Follow on   Twitter

Comments and Discussions

 
Generalgood job PinmemberMember 440374920-Oct-08 3:09 
GeneralRe: good job Pinmemberdefwebserver20-Oct-08 3:16 
Generalproblem with SQL 2000 PinmemberMember 440374920-Oct-08 1:36 
GeneralRe: problem with SQL 2000 Pinmemberdefwebserver20-Oct-08 2:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 19 Oct 2008
Article Copyright 2008 by defwebserver
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid