WPF/E, ASP.NET AJAX and Web Services - hooking it all together

Recently I've been working with WPF/E and extending some code that I have to make it more visually enhanced.  To display the proper data on the page I wanted to do the following: call a web service asynchronously, have it return to me an arrary of a complex type which I could then work with in javascript to get the proper data so that I could create XAML to be displayed by my webpage.  Here's what I did to get it to work.

Before you run this make sure you have the latest downloadeds: the latest WPF/E SDK bits (December CTP 2006) as well as the latest release of ASP.NET AJAX (1.0 RC). 

For demonstration purposes I'll just return some date information.  Now the first thing, create the web service and return a complex type.  For this I created a web service and referenced the System.Web.Script.Services namespace.  Here's a look at the code.

using System;

using System.Data;

using System.Configuration;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Collections;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Web.Script.Services;

namespace MyComplexType

{

public class MyInformation

{

public string Day;

public string Month;

public string Year;

}

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

// Here's where you tell ASP.NET AJAX to create a proxy for your complex type with the GenerateScriptType attribute

[GenerateScriptType(typeof(MyInformation))]

[ScriptService]

public class MyComplexTypeService

{

public MyComplexTypeService()

{

}

[WebMethod]

public ArrayList GetMyInformation()

{

ArrayList list = new ArrayList();

for (int counter = 0; counter < 2; counter++)

{

MyInformation info = new MyInformation();

info.Day = System.DateTime.Today.Day.ToString();

info.Month = System.DateTime.Today.Month.ToString();

info.Year = System.DateTime.Today.Year.ToString();

list.Add(info);

}

return list;

}

}

}

I also want to produce some XAML for my page that displays my data. So, I create a simple XAML file that has a couple of canvas elements: 

<?xml version="1.0" encoding="utf-8"?>

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="216" Height="292" Background="LightGray">

<Canvas x:Name="DataCanvas" Height="70" Width="200" Canvas.Left="8" Canvas.Top="200"></Canvas>

</Canvas>

The next thing I thought about was how my page would use the javascript to interact with the page and the XAML. So, in my page I have a simple input control that the onclick method is set to call a javascript method called "OnLookup()" From here this kicks off my call to the web service. Lets take a look at the javascript file:

var _results;

function OnLookup()

{

ClearList();

MyComplexType.MyInformation.MyComplexTypeService.GetMyInformation(OnSucceeded);

}

function OnSucceeded(result)

{

_results = result;

Array.forEach(result,AddXamlElement);

}

function ClearList()

{

//This gets a reference to the aghost control on the HTML page that holds the XAML object.

var canvas = $get("WpfeControl");

//next I get the canvas I want to add the data to in the form of a XAML rectangle.

var properCanvas = canvas.findName("DataCanvas");

//clear all the elements

properCanvas.children.clear();

}

function AddXamlElement(element,index)

{

//index automatically increments for each call, so I use that to name my rectangles

//This gets a reference to the aghost control on the page that holds the XAML object.

var canvas = $get("WpfeControl");

//next I get the canvas I want to add the data to in the form of a XAML rectangle.

var properCanvas = canvas.findName("DataCanvas");

//Create a new XAML rectangle and give it a namespace and a name

var xamlRect = '<Rectangle xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="';

xamlRect += "Rect_"+index;

xamlRect += '" Height="30" Width="170" Canvas.Left="2" Canvas.Top="';

if(index === 0){xamlRect += '2';}

else{xamlRect += (32 * index);}

xamlRect += '" Stroke="White" StrokeThickness="1" MouseLeftButtonDown="javascript:DoSomething">';

xamlRect += '<Rectangle.Fill>';

xamlRect += '<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">';

xamlRect += '<GradientStop Color="Black" Offset="0.0" />';

xamlRect += '<GradientStop Color="Blue" Offset="0.50" />';

xamlRect += '<GradientStop Color="Black" Offset="1.0" />';

xamlRect += '</LinearGradientBrush>';

xamlRect += '</Rectangle.Fill>';

xamlRect += '</Rectangle>';

var xamlText = '<TextBlock xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="';

xamlText += "TextBlock_"+index;

xamlText += '" Text="';

xamlText += element.Day;

xamlText += '/' + element.Month;

xamlText += '/' + element.Year;

xamlText += '" FontSize="10" Foreground="White" Canvas.Left="8" Canvas.Top="';

xamlText += (32 * index)+4;

xamlText += '" MouseLeftButtonDown="javascript:DoSomething"/>';

//add my rectangle with an event

var newControl = canvas.CreateFromXaml(xamlRect);

//add my text with an event

var newControl2 = canvas.CreateFromXaml(xamlText);

properCanvas.children.Add(newControl);

properCanvas.children.Add(newControl2);

}

function DoSomething(sender,args)

{

//parse the element in the array stored globally and use that to find the element in the array

}

And there you have it. Now your webservice can make a call, return a complex type, and use that type to put XAML on a webpage that has WPF/E.