Wednesday 28 October 2009

Javascript in Flash

Imaging an situation when you need to track redirect activity from the flash animation. Execute some tracking script right before user left the flash animation.

Basically there are two ways have you can call javascript from ActionScript:

1) getURL()
2) ExternalInterface.call

Problem is that browsers does not behaves same way.
getURL is properly executed on FF but not in IE7.
ExternalInterface.call is working on FF, IE in same way. Browser executes javascript before redirect.

Simple validation in Model View Presenter

Last time I wrote about how to implement simple page by Model View Presenter pattern. Quickly we created form with 2 textboxes, label and button for firing presenter method SaveData.

This time let's focus a bit to validation in presenter and co-working your page with validation result.

Let's imagine we have form for validating user contact information.

I will assume that you will have presenter in separate assembly. The assembly it self will contains interface of view (used for communication between presenter and page) and presenter logic (presenter logic and interface of presenter).

1) create new assembly, call it for example WebAppPresenter. Create new interface for View

namespace WebAppPresenter
{
public interface IDataView
{
string FirstName { get; }
string LastName { get; }
string EmailAddress { get; }
string Status { set; }
}
}

Another one for interface of presenter


namespace WebAppPresenter
{
public interface IDataPresenter
{
void SaveData();
bool AreDataValid();
}
}

2) the Presenter will implement IDataPresenter inferface created above.


using System;
using System.Text.RegularExpressions;

namespace WebAppPresenter
{
public class DataPresenter : IDataPresenter
{
private IDataView _view;
public DataPresenter(IDataView view)
{
_view = view;
}

public void SaveData()
{
if (AreDataValid()) { _view.Status = "Data could be saved"; }
}

public bool AreDataValid()
{
if (String.IsNullOrEmpty(_view.FirstName))
{ _view.Status = "First Name cannot be blank"; return false;}
if (String.IsNullOrEmpty(_view.LastName))
{ _view.Status = "Last Name cannot be blank"; return false; }
if (!String.IsNullOrEmpty(_view.EmailAddress))
{
if (!Regex.IsMatch(_view.EmailAddress,@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"))
{_view.Status="Email address is not valid!"; return false;}
}
else { _view.Status = "Email address is not valid!"; return false; }

return true;
}

}
}

Let's stop for a while to describe what is happening here. Constructor populating user entries from webpage through IDataView interface. Method SaveData executes validations and set Status based on validation results. The AreDataValid method is the one which is responsible for validation within presenter. It goes through user inputs present in property _view, validating each value and if one of the field is invalid set Status with proper error message.

Because AreDataValid method is present in interface and it's public can be called directly from webpage for example to prevent execution of other code (such SaveData() ) once the input data are not in good shape. It's up to you whether you make validation method visible outside presenter.

3) Now let's make user interaction with presenter. So firstly create some form for getting information from visitors: (extract from the page)


    <form id="frmContact" runat="server">
First name:<asp:TextBox ID="tbFirstName" runat="server" /><br />
Last name:<asp:TextBox ID="tbLastName" runat="server" /><br />
Email Address:<asp:TextBox ID="tbEmailAddress" runat="server" /><br /><br />
Result:<asp:Label ID="lblStatus" runat="server" /><br />
<
asp:Button ID="btnSave" runat="server" Text="Submit" />
</
form>

4) Code behind of the page. Firstly do not forget to inherit from the View (in my scenario it should be IDataView) to be able make connection between Presenter and View layer.


public partial class MyPage : System.Web.UI.Page,IDataView
{
#region IDataView Members
public string FirstName { get { return this.tbFirstName.Text; } }
public string LastName { get { return this.tbLastName.Text; } }
public string EmailAddress { get { return this.tbEmailAddress.Text; } }
public string Status { set { this.lblStatus.Text = value; } }
#endregion

protected void
Page_Load(object sender, EventArgs e)
{ }
}

I inherited from IDataView to be able implement it's members and connect them with the webform

5) In Page_Load() method is good to connect to the Presenter and initialize it.


protected void Page_Load(object sender, EventArgs e)
{
IDataPresenter _presenter = new DataPresenter(this);
}


Notice that Presenter constructor had one parameter in its definition and that parameter is the View. So when I initializing presenter I need to populate it with the View. In my scenario View will be webform it self because it inherits from IDataView, so I can use this object.

6) Now just need to wire up button btnSave click event to launch SaveData method from Presenter.


protected void Page_Load(object sender, EventArgs e)
{
IDataPresenter _presenter = new DataPresenter(this);
btnSave.Click += delegate{ _presenter.SaveData(); };
}


As I mentioned above you can also call the _presenter.AreDataValid() first and then based on the result call _presenter.SaveData()

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.

Sunday 12 July 2009

ReSharper for Visual Studio 2010 (Preview)

Finally JetBrains launched ReSharper v.5.0 for VS2010 after a month delay against previous announce.

Installation it self should be a bit tricky. First of all download a nightly build of ReSharper from

ReSharper EAP - ReSharper for Visual Studio 2010 (Preview). The download links are located at the very end of the page.

1) Before you click on downloaded .vsix file I highly recommend to click on "Load extensions from my local application data folder" in Tools/Options, Extension Manager menu.

2) Close all your VS2010 instances

3) After that click on downloaded .vsix file, following installation dialog.

4) Start VS2010 and open Tools | Extensions Manager menu and confirm that ReSharper is there

image

5) Maybe you will meet with registration of evaluation period during VS2010 start up. Basically evaluation period should start automatically, if not use following credentials:  (this will provide you a temporary license till 07/23/2009). I heard that keys from ReSharper 4.5 are working as well.

User Name: VS2010Beta1
License Key: 0-dY16Cav/tdLePfqRzD2kp37wt2HXosbp

 

Check JetBrains Resharper EAP VS2010 regularly for updates.

Monday 25 May 2009

Model View Presenter (MVP) Pattern

Model-View-Presenter is a user interface design pattern engineered to facilitate automated unit testing and improve the separation of concerns in presentation logic.

The Model is an interface defining the data to be displayed or otherwise acted upon in the user interface.

The View is an interface that displays data (the Model) and routes user commands to the Presenter to act upon that data.

The Presenter acts upon the Model and the View. It retrieves data from repositories, persists it, manipulates it, and determines how it will be displayed in the View.

The degree of logic permitted in the View varies among different implementations.

At one extreme, the View is entirely passive, forwarding all interaction operations to the Presenter. In this formulation, when a user triggers an event method of the View, it does nothing but invoke a method of the Presenter which has no parameters and no return value. The Presenter then retrieves data from the View through methods defined by the View interface. Finally, the Presenter then operates on the Model and updates the View with the results of the operation.

Other versions of Model-View-Presenter allow some latitude with respect to which class handles a particular interaction, event, or command. This is often more suitable for web-based architectures, where the View, which executes on a client's browser, may be the best place to handle a particular interaction or command.

From a layering point of view, the Presenter class might be considered as belonging to the application layer in a multilayered architectured object-oriented system with common layers but it can also be seen as a Presenter layer of its own between the Application layer and the User Interface layer.

 

Sample of MVP implementation in few steps:

1) create an interface (later in the topic will be note like view-interface ) with properties  which will contain values from webpage/form.

public interface IDataView {
string FirstName { get; }
string LastName { get; }
string Status { set; get; } }

2) make webpage/form to implement this interface

public partial class MyPage : System.Web.UI.Page,IDataView


3) implement interface members and map them to webpage/form entries (such labels, textboxes, etc.)

public string FirstName { get { return this.tbFirstName.Text; } }
public string LastName { get { return this.tbLastName.Text; } }
public string Status { set { this.lblStatus.Text = value; }
get { return this.lblStatus.Text; } }


4) create new class for presenter

public class DataPresenter { }


5) declare a variable of view-interface type (created at point 1), variable value will be initialized through constructor

public class DataPresenter
{
private IDataView _view;
}


6) create/extend constructor with input parameter of view-interface type

public class DataPresenter
{
private IDataView _view;
public DataPresenter(IDataView view) { _view = view; }
}


7) create public method(s) for manipulate with view

public class DataPresenter
{
private IDataView _view;
public DataPresenter(IDataView view) { _view = view; }
public void SaveData() {
_view.Status = string.Format("FirstName={0}; LastName={1}",
_view.FirstName, _view.LastName);
}
}


8) in webpage/form create an instance of the presenter, constructor requires input value of view-interface type. Because the webpage/form already implements an view-interface, input value for constructor will be this.

private DataPresenter _presenter;

protected void Page_Load(object sender, EventArgs e)
{
_presenter = new DataPresenter(this);
}


9) now you can calling presenter’s method(s) from the events etc. If the presenter method change view’s data you’ll get back updated data automatically based on mapping from point 3.

protected void Page_Load(object sender, EventArgs e)
{
_presenter = new DataPresenter(this);
btnSave.Click += delegate { _presenter.SaveData(); };
}


In my sample I called the SaveData method on  _presenter presenter.

Wednesday 20 May 2009

Visual Studio 2010 Beta 1

VS2010 is now available for wide audience. Download your copy from following locations:

  • Visual Studio 2010 Team Suite Beta 1

http://download.microsoft.com/download/5/1/8/518B204C-1DF9-4666-B8D4-FAC0375FADB2/VS2010Beta1ENU_VSTS.iso

  • Visual Studio 2010 Team Foundation Server Beta 1

http://download.microsoft.com/download/6/1/7/61788179-96DF-4E60-B65A-A060F28D1F22/VS2010Beta1ENU_TFS.iso

More info at http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx

Some tips to increase performance of your web site.






You're most likely familiar with the paradox - whether you're using brand-new technologies and have top hardware it looks your web is slow. Please find below some tips that can help you to improve web's performance.

1) Set debug="false" under compilation as follows:

<compilation language="c#" debug="false"></compilation>

2) Use Server.Transfer instead of Response.Redirect

3) Always check Page.IsValid when using Validator Controls

4) Use For Each loop instead of For loop for String Iteration.

5) Use Client-Side Validation. (but not all the time you have to validate even on the server side)

6) Check Page.IsPostBack to avoid repetition code execution.

7) Set trace enabled="false" unless until required. (by default it's off, use on the pages where it's required)

<trace enabled="false" requestlimit="10" pageoutput="false" tracemode="SortByTime" localonly="true"></trace>

8) Precompiled pages and disable AutoEventWireup; setting AutoEventWireup="false" in the Machine.config file.

9) If not required, set SessionState mode="Off".

<SessionState timeout="20" cookieless="false" mode="Off" StateConnectionString="tcpip=127.0.0.1:42424" SqlConnectionString="data source=127.0.0.1;Trusted_Connection=no"></SessionState>

10) Select the Release mode before making the final Build for your application. (by default, the mode is Debug)

11) Disable ViewState by setting EnableViewState="false" when not required.

12) Use Using as much as possible. This not very documented feature defines a scope, outside of which an object or objects will be disposed. VB.NET/C#, via the .NET Framework common language runtine, automatically releases the memory used to store objects that are no longer required. The release of memory is non-deterministic; memory is released whenever the CLR decides to perform garbage collection. However, it's usually best to release limited resources such as file handles and network connections as quickly as possible. The Using statement allows the programmer to specify when objects that use resources should release them. The object provided to the Using statement must implement the IDisposable interface. This interface provides the Dispose method, which should release the object's resources. A Using statement can be exited either when the end of the Using statement is reached or if an exception is thrown and control leaves the statement block before the end of the statement.

Dim sContent As String
Using sr As New IO.StreamReader("C:\test.txt")
sContent = sr.ReadToEnd
sr.Close()
End Using

13) Use Caching to improve the performance of your application.

i. Page output caching:
<%@ OutputCache duration="3600" varybyparam="none" %>

ii. Page fragment caching:
Write a Page output caching code into each User Control.

iii. Data caching:
Imports System.Data.SqlClient
Imports System.Data


Partial Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load


Dim dv As DataView = Cache.Get("ProductDataView")

If dv Is Nothing Then
Using conn As New SqlConnection("Data Source=LAPTOP;Initial Catalog=Customers;Integrated Security=True")


Using da As New SqlDataAdapter("Select ProductId, ProductName From dbo.Product (noLock)", conn)

Using ds As New DataSet
da.Fill(ds, "Product")
dv = ds.Tables("Product").DefaultView
Cache.Insert("ProductDataView", dv)
End Using
End Using
End Using
Else

Response.Write("<h2>Loaded Data From Cache</h2>")
GridView1.DataSource = dv
GridView1.DataBind()
End If
End Sub

End Class

14) Use Finally method to kill resources. (but not in the case of Using)

15) The String and StringBuilder magic.

It's nice to use StringBuilder instead of String when string are Amended. Strings occupy different memory location in every time of amended where StringBuilder use single memory location.

Dim vars As New StringBuilder
For Each sv As String In Request.ServerVariables
vars.Append(sv & "<br />")
Next
Response.Write(vars.ToString.ToLower)

16) Use strString = String.Empty instead of strString = ""

17) Never use object value directly; first get object value in local variable and then use. It takes more time than variable reading.

18) Don't make the member variables Public or Protected, try to keep Private and use public/protected as properties.

19) Avoid Exceptions: Use If condition (if it is check proper condition).

20) Code Optimalization: Avoid using code like x = x + 1; it's better to use x += 1.

21) Data Access Techniques: DataReaders provide a fast and efficient method of data retrieval. DataReader is much faster than DataSets as far as performance is concerned.

22) Before doing a bulky ASP code processing, you can check to make sure Response.IsClientConnected.

23) Avoid Session variables because each ASP page runs in a different thread and session calls will be serialized one by one. So, this will slow down the application. Instead of Session variables you can use the QueryString collection or hidden variables in the form which holds the values.

24) To improve performance, set Response.Buffer = True.

<% Response.Buffer = True %>

Then use:

<% Response.Flush = True %>

25) Avoid frequent round trips to the Database.

26) Use Repeater control instead of DataGrid, DataList because it's efficient, customizable, and programmable.

27) Data listing is more time consume when large data are retrieve from Database.

Paging will display only particular data but take load of all data.

Fetch only data that is needed for current page.

28) Reduce Cookie size.

29) Avoid Inline JavaScript and CSS.

30) Use single CSS file instead of multiple CSS files.

Lot of .CSS files will cause a large amount of requests, regardless of the file sizes.

.CSS files are normally cached by browsers, so a single and heavy .CSS file doesn't cause a long wait on each page request.

Inline .CSS classes could make HTML heavy.

31) Compress CSS, JavaScript, Images and try use server side compression.

CSS compression tool

JavaScript compression tool

Image compression: GIF and PNG are similar, but PNG typically produces a lower file size (Note: some browser not supporting PNG format)

Server side compression tool