Click here to Skip to main content
15,867,141 members
Articles / Web Development / XHTML

Silverlight integrated into ASP.NET AJAX Control (Fifteen Puzzle Game)

Rate me:
Please Sign up or sign in to vote.
4.28/5 (13 votes)
17 Jun 2008CPOL4 min read 80K   1.2K   65   13
Silverlight integrated into an ASP.NET AJAX control (Fifteen Puzzle Game).

Screenshot2.jpg

Introduction

Recently, I got interested in a very amazing technology called Silverlight, and I'm trying to learn it because it's very powerful.

Actually, Silverlight has two versions:

  • Silverlight 1.0 release
  • Silverlight 2.0 beta 1

The difference between 1.0 and 2.0b1 is quite big:

  • Supports .NET languages on the client-side (C#, VB.NET,..) instead of JavaScript
  • A lot of controls from WPF
  • Subset of framework 3.5 (LINQ, ...)

For a complete list of the differences, see here.

Unfortunately, to easily use Silverlight 2.0 beta1, Framework 3.5 and Visual Studio 2008 personal or above are required.

My idea is to integrate a Silverlight control into an ASP.NET AJAX control, but it's seem quite difficult without Visual Studio 2008. For this reason, I integrated a Silverlight 1.0 control into an ASP.NET AJAX control using Visual Studio 2005.

Background

Normally, when I start to learn a new technology, I always try to implement a very simple example, but to make it all the more interesting, I implement a complete application or game.

When I was a child and there were no computers at home, I spent a bit of my free time playing with a game called Fifteen Puzzle. It's a very simple game, but not absolutely easy to solve at the beginning. More details about it at this link. I implemented it here in Silverlight 1.0, integrated into a ASP.NET AJAX control.

Using the Code

The solution is divided into the following projects:

  • Web Application: Containing the ASP.NET page that hosts the control
  • Class Library: Containing the control

In the web application, we have an ASP.NET web form that contains the control and a ScriptManager (the core of the ASP.NET AJAX Framework).

In the control, we have the following files:

  • FifteenPuzzle.cs: Server side control that extends ScriptControl;
  • FifteenPuzzle.js: JavaScript that contains the JavaScript class that implements the Silverlight control into an ASP.NET AJAX control;
  • FifteenPuzzle.xaml: XAML that describes the fixed part of the control (the dynamic part is generated within the JavaScript class);
  • Helper.js: JavaScript that contains the same useful functions;
  • Silverlight.js: Standard JavaScript used to instantiate the Silverlight control into an HTML div;

The class FifteenPuzzle extends ScriptControl (the base class for all ASP.NET AJAX controls on the server side) and overrides two methods:

  • GetScriptDescriptors: method used to pass parameters to the JavaScript class using JSON format.
    • xamlUrl: The URL from the Resource used to generate the static part of the XAML in Silverlight.
    • width, height: The size of the control.
    • imageUrl: Used when the game is in the puzzleRenderMode.Image mode.
    • puzzleRenderMode: enum used to define the two different modes to render the game (Number or Image).
  • GetScriptReferences: method used to pass the script URL (I used GetWebResourceUrl because I embedded all the scripts in the assembly)
    • Controls.Resources.FifteenPuzzle.js: Contains the JavaScript class of the AJAX control, and extends Sys.UI.Control.
    • Controls.Resources.Helper.js: Contains some utility functions.
    • Controls.Resources.Silverlight.js: Default Silverlight JavaScript to instantiate the plug-in into a div.
C#
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
    ScriptControlDescriptor descriptor = 
         new ScriptControlDescriptor("Controls.FifteenPuzzle", this.ClientID);
    descriptor.AddProperty("xamlUrl", this.Page.ClientScript.GetWebResourceUrl(
         this.GetType(), "Controls.Resources.FifteenPuzzle.xaml"));
    descriptor.AddProperty("width", this.Width.ToString());
    descriptor.AddProperty("height", this.Height.ToString());
    descriptor.AddProperty("imageUrl", this.ImageUrl);
    descriptor.AddProperty("puzzleRenderMode", this.PuzzleRenderMode);

    yield return descriptor;
}

protected override IEnumerable<ScriptReference> GetScriptReferences()
{
    List<ScriptReference> scripts = new List<ScriptReference>();

    ScriptReference scriptReference1 = new ScriptReference();
    scriptReference1.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), 
                            "Controls.Resources.FifteenPuzzle.js");
    scripts.Add(scriptReference1);

    ScriptReference scriptReference2 = new ScriptReference();
    scriptReference2.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), 
                            "Controls.Resources.Helper.js");
    scripts.Add(scriptReference2);

    ScriptReference scriptReference3 = new ScriptReference();
    scriptReference3.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), 
                            "Controls.Resources.Silverlight.js");
    scripts.Add(scriptReference3);

    return scripts;
}

The file Controls.Resources.FifteenPuzzle.js contains the JavaScript class Controls.FifteenPuzzle the encapsulates most of the logic.

The position of each piece of puzzle is contained in a 4x4 array. At the beginning, the array is initialized to the normal order, and using JavaScript, I render the pieces dynamically.

When puzzleRenderMode is set to Number, the following is the XAML generated for each piece:

XML
<Canvas Name="FifteenPuzzle1_Cell_15" Canvas.Left="219" 
         Canvas.Top="370" Canvas.ZIndex="3" Width="100" 
         Height="100" Background="White" Cursor="Hand">
  <Rectangle Fill="#80000000" Width="98" Height="98" 
     Canvas.Top="2" Canvas.Left="2" 
     RadiusX="15" RadiusY="15">
  </Rectangle>
  <Rectangle Width="100" Height="100" 
       Canvas.Top="0" Canvas.Left="0" 
       RadiusX="15" RadiusY="15">
    <Rectangle.Fill><LinearGradientBrush 
             StartPoint="0,0" EndPoint="0,1">
      <GradientStop Offset="0" Color="Green"/>
      <GradientStop Offset="0.6" Color="Lime"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle><Rectangle Width="98" Height="98" 
         Canvas.Top="1" Canvas.Left="1" 
         RadiusX="14" RadiusY="14">
    <Rectangle.Fill>
      <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Offset="0" Color="#FFFFFFFF"/>
        <GradientStop Offset="1" Color="#00000000"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle>
  <TextBlock Name="FifteenPuzzle1_TextBlock_3_2" 
     Canvas.Left="0" Canvas.Top="5" 
     Canvas.ZIndex="1" FontFamily="Verdana" 
     FontSize="70" Text="15" 
     FontWeight="Bold" Cursor="Hand" />
</Canvas>

When puzzleRenderMode is set to Image, the following is the XAML generated for each piece:

XML
<Image Name="FifteenPuzzle1_Cell_15" Canvas.Left="19" 
     Canvas.Top="70" Canvas.ZIndex="3" Width="400" 
     Height="400" Cursor="Hand" Source="Resources/Photo.jpg">
  <Image.Clip>
    <RectangleGeometry Rect="200,300,100,100" 
            RadiusX="15" RadiusY="15">
    </RectangleGeometry>
  </Image.Clip>
  <Image.RenderTransform>
    <TranslateTransform Name="FifteenPuzzle1_Cell_15_Transform" 
             X="0" Y="0" />
  </Image.RenderTransform>
</Image>

The algorithm used to shuffle the pieces is very easy, and probably not really optimized, but seems to work fine. I just create a random number between 1 and 15 and try if the move is available. The problem with this algorithm is that, in theory, it could take a long time, but for the "Law of large numbers", it works fine. :)

The most important part of the JavaScript class is the integration with Silverlight.

In the _renderControl function is called Silverlight.createObject that instantiates the plug-in attached to the HTML DIV. When the plug-in is instantiated, the event handler _onXamlLoaded is called and the dynamic part of the XAML is rendered.

JavaScript
_renderControl : function()
{
    this.get_element().innerHTML = String.format("<div id='{0}_Content' " + 
            "style='width:{1};height:{2}'></div>", 
            this.get_id(), this.get_width(), this.get_height());
        
    var hostId = String.format("{0}_Host", this.get_id());
    var bounds = Sys.UI.DomElement.getBounds(this._getContentElement());
        
    Silverlight.createObject(this.get_xamlUrl(), this._getContentElement(), hostId, 
    { width:bounds.width.toString(), height:bounds.height.toString(), version:'1.0' },
    { onError:null, onLoad:Function.createDelegate(this, this._onXamlLoaded) }, 
    null);
},

_onXamlLoaded : function(plugIn, userContext, rootElement)
{
    this._plugIn = plugIn;
    this._rootElement = rootElement;
    
    this._renderNumbers();
    this._drawShuffleButton();
},

I stored the references to the plug-in and the rootElement in the local JavaScript variable because I use it to dynamically generate the XAML using the createFromXaml method. In fact, when I generate the pieces, I used the following code to attach it to the static part of the XAML:

JavaScript
var numberElement = this._plugIn.content.createFromXaml(sb.toString(), false);
this._rootElement.children.add(numberElement);

and the following code to get the element using the name and to attach the event handler on mouse click:

JavaScript
var elementNumber = this._rootElement.findName(String.format("{0}_Cell_{1}", 
                                               this.get_id(), n));
elementNumber.AddEventListener("MouseLeftButtonDown", this._onNumberClick)

Points of Interest

I know that this is a very poor example considering the possibilities available when using this framework, but it can be useful to start to understand the technology and to understand the possibility to integrate it into an existing ASP.NET application.

History

  • 26 May 2008 - First release.
  • 30 May 2008 - Added possibility to switch between Number and Image, and improved article details.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) sparesFinder
Italy Italy
I'm an Italian Software Developer from about 15 years.
I worked a long time in south Italy (where I was born) and after 2 years in Milan and an year in UK, I'm working remotely from Italy as Senior ASP.NET C# Developer using ASP.NET Ajax technology for a UK company.

Check out my personal blog:
http://techcookies.net/

and my first Android game (Fifteen Puzzle X):
https://play.google.com/store/apps/details?id=it.megasoft78.fifteenpuzzlex

Comments and Discussions

 
General3 points for at least having an online demo Pin
defwebserver27-May-08 6:24
defwebserver27-May-08 6:24 
AnswerRe: 3 points for at least having an online demo Pin
Ferreri Gabriele (Megasoft78)27-May-08 11:55
Ferreri Gabriele (Megasoft78)27-May-08 11:55 

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

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