Wednesday 12 August 2009

Silverlight 3 and Web Service

The most effective way for a Silverlight application to tap into server-side code is through web services. The basic idea is simple—you include a web service with your ASP.NET website or add reference to a standalone web service, and your Silverlight application calls the methods in that web service. Silverlight applications can call traditional ASP.NET web services (.asmx services) as well as the WCF services, which are the newer standard.

Creating a Web Service


There are 2 ways how to create a Web Service in Visual Studio:
  1. Add New WCF Service Application Project (File - New Project - WCF Service Application)
  2. Add New Item - Silverlight-Enabled WCF Service (In the Solution Explorer on your web application project right click and select from the content menu Add - New Item - Silverlight-Enabled WCF Service
As I mentioned in my previous post, if you're about to create a standalone WCF Service Application you'll also needed to create and copy to your web root directory clientAccessPolicy.xml and crossDomain.xml files. These two files take care about accessibility of the Web Service.

When the project "included" WCF Service is created, you don't need to think about accessibility files. Visual Studio automatically configures the Web Service to be accessible for the Silverlight application.

When you create a web service, Visual Studio, creates an Interface and Implementation files, namely as defaults: IService1.vb and Service1.svc.

All functions/subs definitions you create in the Interface file (IService1.vb) - make sure you Imports System.ServiceModel - have to start with <OperationContract()>
WCF Service doesn't support all object types to return results from functions, expect of some defaults such as Integer, String. Non supported object have to be implemented via class. Each class definition has to start with <DataContract()> and all property within a class has to start with <DataMember()>

Example of IService1.vb
Imports System.ServiceModel
<ServiceContract()> _
Public Interface IService1
<OperationContract()> _
Function GetUsers() As List(Of user)
End Interface
<DataContract()> _
Public Class user
Private _uId As Integer
Private _firstName As String
Private _lastName As String
<DataMember()> _
Public Property uId() As Integer
Get
Return _uId
End Get
Set(ByVal value As Integer)
_uId = value
End Set
End Property
<DataMember()> _
Public Property firstName() As String
Get
Return _firstName
End Get
Set(ByVal value As String)
_firstName = value
End Set
End Property
<DataMember()> _
Public Property lastName() As String
Get
Return _lastName
End Get
Set(ByVal value As String)
_lastName = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal usrId As Integer, _
ByVal
uFirstName As String, ByVal uLastName As String)
Me.uId = usrId
Me.firstName = uFirstName
Me.lastName = uLastName
End Sub

Very important

If you'd like to use the web service with your Silverlight application, you must modify the web service's Web.Config file and change service endpoint binding to "basicHttpBinding". That's because the Silverlight works only with this one.

To edit this value, open the web service's Web.Config file and scroll almost at the very end of the file. You have to search for these tags:

<system.serviceModel>
<services>
<service name=...

under the <service> tag you'll find <endpoint address=... tag. Edit this tag's binding value to "basicHttpBinding"

Example - before update:

<system.serviceModel>
<services>
<service name="WcfService.Service1"
behaviorConfiguration="WcfService.Service1Behavior">
<endpoint address="" binding="wsHttpBinding"
contract="WcfService.IService1">

after update:

<system.serviceModel>
<services>
<service name="WcfService.Service1"
behaviorConfiguration="WcfService.Service1Behavior">
<endpoint address="" binding="basicHttpBinding"
contract="WcfService.IService1">

Note: Before you publish your Silverlight application to the IIS, make sure you modified the ServiceReferences.ClientConfig file located in your Silverlight application folder. Only one but most important change is required - modify WCF service's endpoint address to the one on which the WCF service to be accessible on the Internet. By default, Visual Studio set up this address for running the web application in development environment, such as <endpoint address="http://localhost:49249/SilverlightApplication.Web/Service.svc".
Suppose, your WCF service is published on Default Web Site\WCFService folder, so the "real" service address will be http://localhost/WCFService/Service.svc therefore service's endpoint address in ServiceReferences.ClientConfig file should looks like:
<endpoint address="http://localhost/WCFService/Service.svc"
Please refer to Publish Silverlight 3 on IIS 7.0/7.5 for more information.

And finally
Whatever service creation way you choose you'll need to add a web service reference to your Silverlight application.

Adding a web service reference is easy. Just, in the Solution Explorer right click on your Silverlight application and from the content menu select "Add Service Reference". In the opened pop-up window (Add Service Reference) fill-up the "Address" text box with the service URL (if you're about to add reference to a standalone service already published on the Web) or click "Discover" button and Visual Studio automatically finds service(s) from current project for you. You can change the default service namespace in the "Namespace" text box to be more comfortable for you. Click "OK" and it's done.

Please note: if change and/or rebuilt your web service you have to update the web service reference in your Silverlight application. To update a web service reference you have to right click on its name in the Solution Explorer under Service References and select "Update Service Reference" from the offered content menu.

When you add or update a web service reference, Visual Studio, creates some files in Service References folder and ServiceReferences.ClientConfig file in the root of your Silverlight application.

Start consuming the web service

Security

All data sent from web service are sent as clear text. So, if you're sending sensitive data consider using appropriate encryption for your data.

Adding Service Reference

If you'd like to use your WCF Service in your Silverlight application you need to add a Service Reference.
In Visual Studio, in Solution Explorer right click on your Silverlight application folder and select "Add Service Reference" from the context menu. The pop-up appears.
To the Address field enter the WCF service's URL address. If you're adding an "project included" WCF service (your service is in your current solution) you can click to "Discover" button - it will list all found services in the current solution. Choose one you'd like to add.
Change the Namespace field if you'd like to.
Click "OK". If everything went smoothly your WCF service reference is successfully added and you can find the ServiceReferences.ClientConfig file in the application root.

Consuming web service

After you've created the Service Reference you're ready to use/consume the web service in your Silverlight application.
It's useful to import the Service Namespace to your application (Performance tip: every "dot" in your command requires a call to the object and it's dramatically impact on performance.)
And finally you can write "consumer" script.

Example: Suppose, you've created the Web Service. Next step is to add a Service Reference, setup Service Namespace as wcfService. Your Silverlight project's name is SilverlightApplication. First of all, open or create a xaml file which is about to consume your Web Service, import Service Namespace and write "consumer" script (in this example I'll call the Service after page is loaded). Because Service is asynchronious you need to add a Handler to sub what will consume the result from the Web Service. The MainForm.xaml contains <TextBlock x:Name="txtUsers"/>

Imports SilverlightApplication.wcfService

Partial Public Class MainPage
Inherits UserControl

Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As _
System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim client As New ServiceClient
With client
AddHandler .getUsersCompleted, AddressOf usersComplete
.getUsersAsync()
.CloseAsync()
End With
client = Nothing
End Sub

Private Sub usersComplete(ByVal sender As Object, ByVal e As _
getUsersCompletedEventArgs)
Dim users As New System.Text.StringBuilder
For Each u As usr In e.Result
With users
.Append("uID = " & u.uId.ToString & vbCrLf)
.Append("firstName = " & u.firstName & vbCrLf)
.Appned("lastName = " & u.lastName & vbCrLf)
End With
Next
txtUsers.Text = users.ToString
End Sub

That's all the basic information you need to know about Silverlight ∧ Web Service. For more details see next postings.

Happy coding!

Tuesday 11 August 2009

Tracking with dcsMultiTrack invalid fields on client side

dcsMultiTrack - is 3rd compoment. Instead of dcsMultiTrack you can use your own tracking tool or for example google analytics.

Sometimes you can find yourself in situation when you want to track if some of the fields on your form is not valid. Track some value when it does not goes through clientside validation. In this situation you need to somehow hook up on or before validation it self will be executed. In my sample I use custom javascript function which executes validation on form fields and based on their validity reporting status of the form.

1) set custom attribute on the field you want to track. For example on textbox with username you can set up attribute DSCError with value "UN".
<asp:textbox id="username" runat="server" DSCError="UN" />

2) place validation control by own choice (regular, required, custom ...) and ensure that clientside validation is turned on.

3) The button used for submit need to have onclick clientside event with own function used for validation. Custom validation function should contain one parameter ValidationGroup (useful for avoid validation of other validationgroups).
For example onclick="PreValidation('MyValidationGroup');"

4) Custom validation function. Function should consume ValidationGroup name but it is not required. If you does not provide it  (PreValidation('') or PreValidation() ) function will validate all fields with validators.

function PreValidation(valGroupName) {
var errorsToReport = "";

for (i = 0; i < Page_Validators.length; i++) {
var val = Page_Validators[i];
// in case there is no value for valGroupName validate all validators
if (typeof (valGroupName) == 'undefined' || valGroupName == '' ||
val.validationGroup == valGroupName)
{
var cntrToValidate = $get(val.controltovalidate);
if (cntrToValidate != null) {
var DSCError = cntrToValidate.attributes["DCSError"];
if (DSCError != null) {
ValidatorValidate(val, val.validationGroup, null);
if (val.isvalid == false) {
errorsToReport += (errorsToReport.length > 1 ? "," : "");
errorsToReport += DSCError.value;
}
}
}
}
}
if (errorsToReport.length > 0) {
dcsMultiTrack("DCS.dcsuri", "/your_page_name", "DCSext.ErrorFields", errorsToReport);
}
}


How does it works? Well firstly we need to go through all validators on the page, they are present in Page_Validators collection. Then check out whether actuall validator could be evaluated or not. If so store field you tracking into variable cntrToValidate. Check present of custom attribute DCSError on the field. Finally execute the validation with ValidatorValidate(...). Based on the results populate errorsToReport with list failed fields DSCError entries. When validation is done, try to track failed fields by calling dcsMultiTrack.

Silverlight 3 and Sessions

Silverlight does NOT support Session object as you may know in ASP/ASP.NET. The reason is because the Silverlight runs on the Client.

In most cases you will not need Session object at all. If you browse over the Internet you'll probably find some solutions how to implement Session object to your Silverlight application via WCF Service. The main idea of this approach is that your Silverlight application call function from the WCF service to set "something" to the Session object and another call to the WCF service is required to get value from the Session object.

Happily, Silverlight has something like Session object - Isolated storage. It's used for storing application settings. It can contain various types of information, such as text, XML, and serialized objects. The default space limit is a mere 1MB (although you can request that the user grant you more). Isolated storage is persistent—unlike the browser cache, it never expires and it’s not removed if the user chooses to explicitly delete temporary Internet files. The user can manually delete isolated storage area (see folder location below) or you can delete it in your code by calling the Remove or Clear method.

The base location of the isolated storage area on each operating system supported in Silverlight 2+:

Mac OS XAppData\Local
Windows XPC:\Documents and Settings\[UserName]\Application Data\Microsoft\Silverlight\is
Windows Vista/Windows 7C:\Users\[UserName]\AppData\LocalLow\Microsoft\Silverlight\is

To use the Isolated storage you need to import the namespace to your Silverlight application:
Imports System.IO.IsolatedStorage.IsolatedStorageSettings

When you call Save method (e.g. ApplicationSetting.Save()) data stored in ApplicationSettings are saved to above location on Client's HDD. Beware: Saved data are not encrypted - if you need to store sensitive data you have to encrypt them - see example of encryption utility here.

This namespace contains two settings option:
  1. ApplicationSetting - ApplicationSettings are stored as per-application, per-computer, and per-user settings. Their scope is determined by the full path of the application .xap file.
  2. SiteSetting - SiteSettings are stored as per-domain, per-computer, and per-user settings. Their scope is determined by the sub-domain that hosts the application .xap file.
Example:
Suppose, you'd like to store a username from a login form. There's a text block named txtUserName and an OK button named btnOK on the login form. To display stored value you have another text block named txtStoredName and a button named btnShow that you'll use to
display stored value. You want to store the username on the application level.

Imports System.IO.IsolatedStorage.IsolatedStorageSettings
Private Sub btnOK_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs)
ApplicationSetting.Add("userName", txtUserName.Text)
ApplicationSetting.Save() 'saves stored data to disk
End Sub

Private Sub btnShow_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs)
If ApplicationSetting.Contains("userName") Then
txtStoredName.Text = ApplicationSetting("userName").ToString
Else
txtStoredName.Text = "Sorry, nothing stored"
End If
End Sub

You can for example store in the Isolated Storage file, just add this code:

Imports System.IO
Imports System.IO.IsolatedStorage
Private Sub CreateAndWriteToFile()
Using store As IsolatedStorageFile = _
IsolatedStorageFile.GetUserStoreForApplication
Using file As IsolatedStorageFileStream = _
store.CreateFile("sample.txt")
file.Close()
End Using
Using sw As New StreamWriter(store.OpenFile("sample.txt", _
FileMode.Open, FileAccess.Write))
sw.WriteLine("Test Text")
End Using
End Using
End Sub

Private Sub ReadFromFile()
Dim result As String
Using store As IsolatedStorageFile = _
IsolatedStorageFile.GetUserStoreForApplication
Using sr As New StreamReader(store.OpenFile("sample.txt", _
FileMode.Open))
result = sr.ReadToEnd
End Using
End Using
End Sub

How to increase Isolated Storage size

When you right click anywhere inside your Silverlight application displayed on the browser, a pop-up named "Microsoft Silverlight Configuration" will appear. Click "Application Storage" tab to get information about storage size. To increase the size you need to write a code to request a user to grant it.
So, it can be done as follows. Suppose, you need 6MB of storage size:

Imports System.IO.IsolatedStorage
Using store As IsolatedStorageFile = _
IsolatedStorageFile.GetUserStoreForApplication
Dim spaceNeeded As Int64 = 1048576 * 6
Dim spaceAvailable As Int64 = store.AvailableFreeSpace
If spaceNeeded > spaceAvailable Then
If store.IncreaseQuotaTo(spaceNeeded) Then
' user granted increase, the store is now 6MB
' put your code here
End If
End If
End Using

Simple but Powerfull Encryption/Decryption utility

You probably find yourself thinking about how to secure your data. It's especially useful if you stored some information in file(s) and/or sending information over the Internet, or just want to secure something. According what's your goal is, you can find hundred-of-thousands encryption utilities on the Internet.
I personally think that .NET has strong enough ability for encryption/decryption. Just have a look to System.Security.Cryptography namespace. I find myself comfortable with Advanced Encryption Standard (AES). The following code shows how easy is to used it and create a powerful encryption:

Imports System.Security.Cryptography
Imports System.Text.UTF8Encoding

Public Class crypt
Private _salt As String = "I am Salt-to mix output phase" 'some "salt" string to spice-up phase
Private _pass As String = "A1most_p0w3rful_passw0rd" 'password used to en/decrypt

Public Sub New()

End Sub

Public Function Encrypt(ByVal input As String) As String
Dim result As String
Dim aes As New AesManaged
Dim utfData As Byte() = UTF8.GetBytes(input)
Dim saltBytes As Byte() = UTF8.GetBytes(_salt)
Dim rfc As New Rfc2898DeriveBytes(_pass, saltBytes)
With aes
.BlockSize = .LegalBlockSizes(0).MaxSize
.KeySize = .LegalKeySizes(0).MaxSize
.Key = rfc.GetBytes(.KeySize / 8)
.IV = rfc.GetBytes(.BlockSize / 8)
Using encryptTransf As ICryptoTransform = .CreateEncryptor
Using encryptStream As New System.IO.MemoryStream
Using encryptor As New CryptoStream(encryptStream, _
encryptTransf, CryptoStreamMode.Write)
encryptor.Write(utfData, 0, utfData.Length)
encryptor.Flush()
encryptor.Close()
End Using
result = System.Convert.ToBase64String(encryptStream.ToArray)
End Using
End Using
End With
rfc = Nothing
aes = Nothing
Return result
End Function

Public Function Decrypt(ByVal input As String) As String
Dim result As String
Dim encryptedBytes As Byte() = System.Convert.FromBase64String(input)
Dim saltBytes As Byte() = UTF8.GetBytes(_salt)
Dim aes As New AesManaged
Dim rfc As New Rfc2898DeriveBytes(_pass, saltBytes)
With aes
.BlockSize = .LegalBlockSizes(0).MaxSize
.KeySize = .LegalKeySizes(0).MaxSize
.Key = rfc.GetBytes(.KeySize / 8)
.IV = rfc.GetBytes(.BlockSize / 8)
Using decryptTrans As ICryptoTransform = .CreateDecryptor
Using decryptStream As New System.IO.MemoryStream
Using decryptor As New CryptoStream(decryptStream, _
decryptTrans, CryptoStreamMode.Write)
decryptor.Write(encryptedBytes, 0, encryptedBytes.Length)
decryptor.Flush()
decryptor.Close()
End Using
Dim decryptBytes As Byte() = decryptStream.ToArray
result = UTF8.GetString(decryptBytes, 0, decryptBytes.Length)
End Using
End Using
End With
rfc = Nothing
aes = Nothing
Return result
End Function

Public Function toHex(ByVal input As String) As String
Dim result As New System.Text.StringBuilder
For Each c As Char In input
result.Append(AscW(c).ToString("X"))
Next
Return result.ToString
End Function

Public Function hexToString(ByVal hexInput As String) As String
Dim result As New System.Text.StringBuilder
For i = 1 To Len(hexInput) Step 2
result.Append(ChrW(Integer.Parse(Mid(hexInput, i, 2), _
System.Globalization.NumberStyles.HexNumber)))
Next
Return result.ToString
End Function

Sample of usage: The following code shows how to encrypt the string stored in txtString. I used extra function toHex to convert encryption result to Hex numbers. The encr contains encrypted string and decr contains decrypted string (the original string)

Dim txtString As String = "String To Be Encrypted!"
Dim crypto As New crypt
Dim encr As String = crypto.toHex(crypto.Encrypt(txtString))
Dim decr As String = crypto.Decrypt(crypto.hexToString(encr))
crypto = Nothing

Happy coding ;)

Monday 10 August 2009

Publish Silverlight 3 on IIS 7.0 / 7.5

Please note: Windows Vista/Server 2008 comes with IIS 7.0 and Windows 7 comes with IIS 7.5, however there are no major changes in publishing your web application and web service. The following steps guide you via process of publishing.

Building/Publishing Web Application in Visual Studio

In the Visual Studio, on your website project, or from the Build menu, select Publish Web Site. In the opened window setup "Target Location" where you'd like to publish the application. Set appropriate check boxes and click OK.

Example:
In the Visual Studio, you have the Silverlight.Web web application and the Silverlight project. Right click on "Silverlight.Web" and select "Publish Web Site" from the content menu. In the opened window, browse for "Target Location" such as "C:\Inetpub\wwwroot\TestApp" and click OK.

Building/Publishing WCF Service in Visual Studio

First of all, please make sure that you've edited the ServiceReferences.ClientConfig file located in your Silverlight project. You need to change client's end point address to the real one. It's essential because your WCF Service won't work.

Example:
Suppose, you're about to publish your WCF Service named TestService.svc to the IIS root directory to the WebService folder, so after successful publishing it can be accessible as http://localhost/WebService/TestService.svc
Your web service worked fine in the development environment because the service references' config file (ServiceReferences.ClientConfig) looked like this:

<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="CustomBinding_Service">
<binaryMessageEncoding />
<httpTransport maxReceivedMessageSize="2147483647"
maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://localhost:49741/SilverlightApplication.Web/TestService.svc"
binding="customBinding" bindingConfiguration="CustomBinding_Service"
contract="wcfService.Service" name="CustomBinding_Service" />
</client>
</system.serviceModel>
</configuration>

So, you need to change <endpoint address from one provided by Visual Studio to the address that represents the web service's web location. In this case, just make this change:

<endpoint address="http://localhost/WebService/TestService.svc"

rest of the line leave as it. By changing this address you setup your web service.

After that, you can continue to publish your web service. In Visual Studio, in Solution Explorer, right click on project's name and select "Publish". In the opened pop-up window browse for target directory. I prefer setup the publish process as following, check these options:
  1. "Delete all existing files prior to publish"
  2. "Copy only files needed to run this application"
  3. "Include files from the App_Data folder" - if you use it

Preparing IIS Environment


After you built your Silverlight application you're probably about to publish it on IIS.

First of all, check the IIS in the Windows Features (e.g. on Windows Vista/7, navigate to Control Panel - Programs and open Turn Windows Features On Or Off). Make sure you have checked the following options:
  • Internet Information Services (recommend all features except FTP if you don't use it)
  • Microsoft .NET Framework 3.5.1 (recommend all features)
If you already have installed the IIS, you can start it (IIS Manager), it can be found in Control Panel - Administrative Tools in Vista or Control Panel - System And Security - Administrative Tools in Windows 7 as Internet Information Services (IIS) Manager.

Adding your Silverlight application

In IIS Manager, right click on your Default Web Page, located under Sites, and Add New Virtual Directory.
In the opened window (Add Virtual Directory) input Alias name and browse for Physical path. You can also specify a user whos credentials will be used to open a web site by clicking on Connect as.... If you're happy with your settings click OK. You have successfully created the virtual directory where your applications resists.

Now, you have to convert this virtual directory to application. To do this, just right click on it and select "Convert To Application" option from the content menu. The application pool should be "Classic .NET AppPool". To set it up click the "Select" button and choose "Classic .NET AppPool" from the drop-down. After then, click "OK" and your website is published on the IIS.

Please follow this simplified example to setup web application in IIS for a project that's been build in C:\Inetpub\wwwroot\TestApp
1. Open IIS
2. Expand Sites
3. Right click on Default Web Page and select "Add New Virtual Directory" option
4. In the opened pop-up window input "Alias" as Test and populate "Physical path" with C:\Inetpub\wwwroot\TestApp and click "OK"
5. Right click on the created virtual directory (Test) and select "Convert To Application" option from the content menu.
6. In the opened pop-up window make sure you have selected by clicking the "Select..." button Classic .NET AppPool in "Application pool". Click "OK".

That's all. Right now, you can check your website. Right click on the created application (Test) select "Manage Application" - "Browse" from the content menu. This will open your website on your default browser.
Right now, your website is also accessible by typing the http://localhost/Test on the browser's URL.

Adding your WCF Service

To add your WCF Service please follow steps described above (Adding your Silverlight application).
Suppose, you've added Service1.svc from C:\Inetpub\wwwroot\Service folder. You can test the accessibility of your service by browsing this URL: http://localhost/webService/Service1.svc
If you successfully installed the service you'll see the service test page, follow the very first link at this page (something like: http://localhost/webService/Service1.svc?wsdl) to test your service - you'll get an XML output.

If everything went smoothly, you've got WSDL output, you'll need to accomplish one simple step to make your service accessible from your web application. Because it's a separate service (built outside the Silverlight application solution), you have to add clientAccessPolicy.xml and crossDomain.xml files to your web root directory. In these files you can specify, who and from which domain can access (consume) your web service. If you avoid these files or wrongly setup them your service can no longer be accessible.

Example: Suppose, your web root directory is C:\Inetpub\wwwroot and you'd like that your web service to be accessible by everyone from any domain. All you need is copy the clientAccessPolicy.xml and crossDomain.xml to your C:\Inetpub\wwwroot folder.

Example of clientAccessPolicy.xml that allow accessing the service for everyone:

<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource include-subpaths="true" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>

Example of crossDomain.xml to allow access web service from any domain:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

If you've created your WCF Service within your Silverlight application by adding new item (Silverlight-enabled WCF Service) you don't need to create and copy clientAccessPolicy.xml and crossDomain.xml files to your web root directory. Visual Studio automatically creates security and accessible roles for your Silverlight application. In this case this WCF Service is accessible only from your Silverlight application.

Silverlight 3 and ASP.NET 3.5

Before you start to thinking about your new website in Silverlight - it's good to remember that Silverlight application runs on the Client. It means your compiled ".xap" file is copied to the client prior to its execution. The ".xap" file contains all your Silverlight application. It's the big advantage because this approach dramatically reduces loading and/or rendering and there are minimal or none server calls. If you'll need to communicate with the server from your Silverlight application you have to use a webservice - Silverlight enabled WCF Service.

Silverlight 3 and Visual Studio 2008

First of all, you'll need to download and install Silverlight runtime as well as Silverlight SDK. The first mentioned is required to be able to see your output (your Silverlight application) on the browser and the second one is neccessary for developing.

You can get the latest stuffs from here:
http://silverlight.net/GetStarted/

I recommend to install SDK2 and SDK3 too, the reason is that SDK3 does not contain required library "System.Web.Silverlight.dll".
You should Add Reference to this library to be able use the Silverlight namespace (<asp:silverlight>)

To start build your Silverlight application, create a new "Silverlight Application" project. According to selected option the VS creates several files for you:

in Silverlight folder:
  • App.xaml - it's the starting main application file. It contains data and logic for your entire application, application-scope resources. This file is executed as first when your project starts. You can direct all other *.xaml files from here.
  • MainPage.xaml - main page, e.g. one of your control. The default behaviour is that this page is loaded from App.xaml.

In Silverlight usually each control resists in one xaml file or you can create separate xaml file for each site of your web. Please remember there's no Post-Back, so your website can be just one file, controls are refreshed via UpdateManager.

in your Silverlight.Web folder:
  • Default.aspx - your aspx startup page
  • SilverlightTestPage.aspx, SilverlightTestPage.html - these are test pages for your Silverlight application. You can open these files on your browser to test your Silverlight project.

That's enough for now and you can start coding. Let's say, your web application will start with the Default.aspx site, so setup this page in VS as "Set as Start Page".

Modify your Default.aspx to accept <asp:silverlight> namespace, add this line right below the Page directive:
<%@ Register Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls" TagPrefix="asp" %>

Right now, you can use <asp:silverlight> namespace in the code in <form> section - don't forget to add <asp:scriptmanager>, e.g.:

<asp:scriptmanager id="ScriptManager1" runat="server">
<asp:silverlight id="Silverlight1" runat="server" source="~/ClientBin/Silverlight.xap">

The Source parameter contains location to compiled xap application. (This xap file is about to be created when you build your Silverlight application).

Please note you can use CssClass or style parameters in the <asp:silverlight> namespace to position your silverlight control. It means you can create mixed content page, e.g. page can be aspx and your ads can be Silverlight, however the Silverlight has power to build the entire web. In case you'll use whole browser window to display your Silverlight application you can add the following style: style="width:100%, height:100%".
Of course, you can position your Silverlight controls in xaml file(s).

Suppose, you're building an CRM application. When you start your web application a login form should appear.

Example of the login.xaml:

<UserControl xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input" x:Class="Silverlight.login"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas>
<Grid x:Name="loginGrid" Width="238" Height="156" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Border BorderBrush="Black" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid Background="LightGray">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="120" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBlock Text="L O G I N" Grid.ColumnSpan="2" HorizontalAlignment="Center"/>
<TextBlock Text="User Name" Width="100" HorizontalAlignment="Right" Height="20" Grid.Row="1" Grid.Column="0"/>
<TextBox x:Name="UserName" MaxLength="50" Width="100" Height="25" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="Password" Width="100" HorizontalAlignment="Right" Height="20" Grid.Row="2" Grid.Column="0"/>
<PasswordBox x:Name="Password" MaxLength="20" Width="100" Height="25" Grid.Row="2" Grid.Column="1"/>
<Button x:Name="bntLogin" Width="80" Height="25" Content="Login" Click="bntLogin_Click" Grid.Row="3" Grid.Column="1"/>
</Grid>
</Border>
</Grid>
</Canvas>
</UserControl>

This form should be positioned at the centre of the user's browser area. This task can be accomplished very easily:
  1. you need to get browser's display area dimensions
  2. you need to write a code that will centre the login form
First of all, Silverlight support functions to access Browser and HTML page properties via System.Windows.Browser namespace.

To get browser's dimensions:
Dim clientWidth As Double = Application.Current.Host.Content.ActualWidth
Dim clientHeight As Double = Application.Current.Host.Content.ActualHeight

Retrieve control's dimensions:
Dim cntWidth As Double = loginGrid.Width
Dim cntHeight As Double = loginGrid.Height

Calculate control's new margin position:
loginGrid,Margin = New Thickness((clientWidth - cntWidth) / 2, (clientHeight - cntHeight) / 2, 0, 0)

So, the code for login.xaml.vb looks like:
Imports System.Windows.Browser

Public Sub New()
InitializeComponent()
'Add event handler to form resize
AddHandler Application.Current.Host.Content.Resized, AddressOf centerForm
End Sub

Private Sub login_Loaded(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Call centerForm()
End Sub

Private Sub centerForm()
Dim clientWidth As Double = Application.Current.Host.Content.ActualWidth
Dim clientHeight As Double = Application.Current.Host.Content.ActualHeight
Dim cntWidth As Double = loginGrid.Width
Dim cntHeight As Double = loginGrid.Height
loginGrid.Margin = New Thickness((clientWidth - cntWidth) / 2, _
(clientHeight - cntHeight) / 2, 0, 0)
End Sub

There are some other important things to think about before you start to build your Silverlight application - see next posts.

Wednesday 5 August 2009

Request.UrlReferrer Is Null In IE 6.0 when location.href(Javascript) is used to redirect

Microsoft announced fix will be from IE9. Until you can avoid problems with following code:

function navigateWithReferrer(url)
{
var fakeLink = document.createElement ("a");
if (typeof(fakeLink.click) == 'undefined')
location.href = url; // sends referrer in FF, not in IE
else
{
fakeLink.href = url;
document.body.appendChild(fakeLink);
fakeLink.click(); // click() method defined in IE only
}
}


The trick is that, although location.href navigation (and window.navigate() too) doesn't send a referrer, the IE-only click() method on a hyperlink will send a referrer. So if you create a
hyperlink, set its href, and then click() it with script, you'll get the referrer you want.

Unfortunately the click() method isn't supported on hyperlinks in some versions of Firefox, so you need to check for its presence and use location.href if it's not there. Since location.href sends a referrer on firefox, it works fine.