Sunday, November 2, 2008

Another one on repacking XAPs to reduce filesize

I've seen quite some articles on repacking your XAP file to reduce the filesize. You can do this manually by using archiving utilities like 7-ZIP, WINRar or the Windows built in zip support.

Delay did a great article on this which inspired me to do the following:

I created a very small application called ReXapper.exe which can do exactly the same as Delay describes but there is no external archiving utility necessary. It uses the SharpZipLib to re-zip the file. So there's just a tiny (124 kb) command-line executable.

The executable requires .NET framework 2.0. It has only 1 parameter: -xap for the xap file.

To automatically re-xap your project after building follow these steps:
1. Copy the executable to a folder. I used d:\tools\rexapper\
2. Open you project in Visual Studio. Go to the properties of the project (using the Project menu) and locate the "Post-build event command line" box.
3. Paste the following in the box:
D:\tools\ReXapper\ReXapper.exe -xap "$(TargetDir)$(TargetName).xap"




Now build your project. In the Output window you will see the ReXapper.exe is called and it will output the following three things:
- filesize before ReXapping
- filesize after ReXapping
- time spent on ReXapping



As you will see it is pretty fast so don't worry about it slowing you down.

If you want to use ReXapper.exe, you can download it here:

ReXapper

If you want the source to make some changes or additions, drop me a message.

Hope this helps.

Monday, October 13, 2008

Clipboard support in Silverlight 2

* UPDATE *
The demo now works with Silverlight 2 RTW and the ClipboardHelper has been added to the source of the SilverlightContrib project. It'll be in the next release.

By default, there is no clipboard support in Silverlight 2. Page Brooks, who is the project coordinator of the open source project SilverlightContrib I'm working on with a bunch of other developers (I feel really honered to work with them...), wanted to add this to the SilverlightContrib project as well so I decided to start off with a basic implementation.

You can create (limited) clipboard support in Silverlight by using Javascript and HTML DOM to read from, add to and clear the clipboard. Note that this will only work in Internet Explorer.

I created a class in which the following methods do the most important work:


public void SetData(string Value)
{
_window.Eval(string.Format("window.clipboardData.setData('text','{0}')", Value));
}

public string GetData()
{
this._currentValue = (string)_window.Eval("window.clipboardData.getData('text')");
return _currentValue;
}

public void ClearData()
{
_window.Eval("window.clipboardData.clearData()");
}


In the constructor of the class I create the possibility to add multiple UIElements as a parameter:


public ClipboardHelper(params UIElement[] UIElementsToCatchEventsOf)
{
if (UIElementsToCatchEventsOf != null)
{
foreach (UIElement element in UIElementsToCatchEventsOf)
{
element.KeyDown += new KeyEventHandler(UIElement_KeyDown);
element.KeyUp += new KeyEventHandler(UIElement_KeyUp);
}
}
}


By subscribing to the KeyUp and KeyDown event of the specific UIElement you can also add paste support to the elements. For example the Button:

private void UIElement_KeyUp(object sender, KeyEventArgs e)
{
if(e.Key == Key.Ctrl)
_ctrlPressed = false;
if (e.Key == Key.V && _ctrlPressed)
{
this.GetData();
if (sender.GetType() == typeof(Button))
{
Button buttonSender = (Button)sender;
buttonSender.Content = this.GetData();
}
}
}


You could also create your own implementation for ListBox and other elements. Be aware though that this will only work with items that can gain focus.


Watch the demo here

Download the project here

Saturday, September 20, 2008

Sneak preview

Don't tell Jeff, but here's a preview of another one of the projects in Foundation Silverlight 2 Animation.

I made a little modification to this compared to the one that you'll create from the instructions in the book, assuming you're going to get it of course :).
To make the dragon move more "natural" I used a DispatcherTimer with a random duration to make it change direction at a random time.



Click here to view the WMV file


Visit designwithsilverlight.com for more previews of the projects in the book.

Thursday, August 21, 2008

Authentication in Silverlight using ASP.NET FormsAuthentication

On the Silverlight forum I've seen a lot of questions regarding authentication and security.
I figured I'd write something down from my perspective as an ASP.NET developer. This isn't the only way to do it and probably not the best, but it does what I need it to do and it might get you started in the right direction.

IMPORTANT NOTE: This article is built with the assumption that all files requested are handled by ASP.NET and not by IIS (so let .net dll handle the wildcard extension *).

Click here to download the source

1. How and why

ASP.NET provides excellent methods for securing web applications using WindowsAuthentication or FormsAuthentication. And creating your own MembershipProvider allows you to authenticate using any type of database or other data storage.
For this example I chose to use FormsAuthentication because this gives you lots of freedom and flexibility to expand or adjust in a later stage of the build of your project.

2. Securing the ASP.NET application

In this example we will secure the application and will test it using 2 types of request:
- a file which is in a secured folder on the server
- a WCF webservice secured method

I used a standard Silverlight Application Project template in Visual Studio with a Web Application project, not a Website. This will probably also work with the Website, but to prevent problems better use the WebApplication type.

2.1 Changes to the Web.Config

First of all we're going to configure the ASP.NET Web Application to use FormsAuthentication to secure a folder.
Create a folder named "Secure" in the root of the ASP.NET Web Application (website from here on).
Open the Web.Config and locate the following section:

Change it to this:








Second thing to do in the Web.Config is to add the following section just after the tag:








That's it. We're done securing the folder Secure in our website. To verify this, create an XML file in the Secure folder with the following content:



You can only see this if you're logged in.



Now run your website pressing F5 and try to open the XML file. If everything went as planned you should see the application redirected you to a login.aspx which doesn't exist. We won't create one in this tutorial because we'll be using Silverlight to do the logging in and not ASP.NET.

Next thing we're going to do is to create the WCF webservice that we'll use for logging in the website.

2.2 Create a WCF Authentication Service


We'll be using a standard Silverlight WCF Service for this. I always like to keep my application tidy so I created a folder in the root of my website named "WebServices".
In that folder, create a "Silverlight-enabled WCF Service" which you can find in the dialog that appears when you click on Add => New Item and when you select the Silverlight Category. Name the service "AuthenticationService".

In the created WCF service there is a method already created for you:
[OperationContract]
public void DoWork()
{
// Add your operation implementation here
return;
}

Replace this method with the following code:
[OperationContract]
public bool Authenticate(string Username, string Password)
{
if (FormsAuthentication.Authenticate(Username, Password))
{
FormsAuthentication.SetAuthCookie(Username, false);
return true;
}
return false;
}


As you can see is that all we did was create a method with a username and password parameter which uses the two default FormsAuthentication methods, Authenticate and SetAuthCookie, to enable security. The reason we need the SetAuthCookie is because it fills the
HttpContext.Current.User
object which we can read out later on to verify if a user is logged in and which user it is.

That covers the authentication part for now. Next, we'll create the Silverlight Application with the login form.

2.3 Creating the login form in Silverlight


Open the Page.xaml in Expression Blend. In the LayoutRoot, add three Grids:
- LoginForm
- ResultForm
- TestForm

Make sure the ResultForm is positioned at about the same position as the LoginForm and the the visibility of the ResultForm is set to Collapsed.

On the LoginForm, add the textblocks (labels), textboxes and a button needed to create login form so that it looks like this:



On the ResultForm, add a textblox with a default text of:
Login succeeded. You can continue with the application

On the TestForm, add 2 buttons and a large Textblock:
- ButtonGetMyBalance
- ButtonGetXmlFile
- TextBlockResult

When you're done you should have something similar to this:


Now we're done with preparing the interface and we'll proceed to the coding of the application. Open the Page.xaml.cs with Visual Studio 2008.

2.4 Calling the AuthenticationService


Before calling the service, we need to add the reference to the service to the Silverlight Application.
Right-click on the Service Reference folder in the Silverlight Application project, and select "Add Service Reference".
Next, click on the "Discover" button that is in the pop-up window that will appear.
After the discovery is completed, you'll see the AuthenticationService appear in the box "Services".
Select it and change the "Namespace" to: "AuthenticationService". Now press OK and the service will be added to you project.
Now, let's continue in the Page.xaml.cs.

In the Page() constructor, add the following lines of code after the InitializeComponent() method:
ButtonLogin.Click += new RoutedEventHandler(ButtonLogin_Click);
ButtonGetXmlFile.Click += new RoutedEventHandler(ButtonGetXmlFile_Click);


Next, add the code for getting the Xml file using a WebClient:
void ButtonGetXmlFile_Click(object sender, RoutedEventArgs e)
{
WebClient webclient = new WebClient();
webclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webclient_DownloadStringCompleted);
webclient.DownloadStringAsync(new Uri("../Secure/TestFile.xml", UriKind.Relative));
}

void webclient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null && !string.IsNullOrEmpty(e.Result))
{
TextBlockResult.Text = e.Result;
}
else
{
if (e.Error != null)
TextBlockResult.Text = e.Error.Message;
else
TextBlockResult.Text = "Please login first";
}
}


Now create the event handler for the ButtonLogin.Click event:
private void ButtonLogin_Click(object sender, RoutedEventArgs e)
{
AuthenticationService.AuthenticationServiceClient authService = new AuthenticationService.AuthenticationServiceClient();
authService.AuthenticateCompleted += new EventHandler(authService_AuthenticateCompleted);
authService.AuthenticateAsync(TextBoxUsername.Text, TextBoxPassword.Text);
}


As you can see in the previous code, we need to create an eventhandler for the AuthenticateCompleted event.

void authService_AuthenticateCompleted(object sender, SilverlightAuthentication.AuthenticationService.AuthenticateCompletedEventArgs e)
{
if (e.Result)
{
LoginForm.Visibility = Visibility.Collapsed;
ResultForm.Visibility = Visibility.Visible;
TextBlockResult.Text = "";
}
else
{
TextBlockResult.Text = "Unable to log you in. Invalid username or password.";
}
}


In this piece of code we hide the LoginForm once the authentication is succeeded. If authentication fails, an errormessage is displayed in the TextBlockResult control.

When you press F5 to run the application, you should be able to login using the credentials we added in the Web.Config:
username: myUser
password: secret

If you use an incorrect username, the errormessage should appear in the TextBlockResult.

You can also try to use the "Get Xml File" button which should only work if you're logged in.


2.5 Create a secure WCF Webservice


For the retrieval of your balance (don't worry, it's not your real balance) we are going to create another Silverlight-enabled WCF service. Let's call it "BalanceService". Next, we'll create the method "GetMyBalance" in there which will return a decimal and a boolean value.
Again, replace the default generated method with the following code:
[OperationContract]
public bool GetMyBalance(out decimal Balance)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Balance = 1000.00M;

return true;
}
else
{
Balance = decimal.MinValue;
return false;
}
}


As you can see, we use the User.Identity object, which was filled using the SetAuthCookie method we called earlier on in this tutorial, to check if the user calling the method is authenticated. If the user is authenticated, the webservice will return true and will fill the Balance output paramater with the correct value, else it will return false and the decimal.MinValue in the Balance parameter.

Next well add the functionality to call the WCF Service in the Silverlight application.

2.6 Add functionality in Silverlight for the BalanceService


First we need to add the Service Reference to the project again. Since we've done this before, I'll just sum it up:
- Right-click the Service References folder
- Click on Add Service Reference
- Click on Discover
- Select the service "BalanceService"
- Change the namespace to "BalanceService"
- Click OK

Once we're done with that it's time to do some coding.

Add the following line to the Page() constructor, just before the closing curly brace:
ButtonGetMyBalance.Click += new RoutedEventHandler(ButtonGetMyBalance_Click);


Next, create the event handler for the Click event:
void ButtonGetMyBalance_Click(object sender, RoutedEventArgs e)
{
BalanceService.BalanceServiceClient balanceService = new SilverlightAuthentication.BalanceService.BalanceServiceClient();
balanceService.GetMyBalanceCompleted += new EventHandler(balanceService_GetMyBalanceCompleted);
balanceService.GetMyBalanceAsync();
}


What we see here is that it is not necessary to add the out parameter of our method to the webservice call. In the next step we'll see what happens with that.

All we need to do now is to create the handler for the GetMyBalanceCompleted event:
private void balanceService_GetMyBalanceCompleted(object sender, SilverlightAuthentication.BalanceService.GetMyBalanceCompletedEventArgs e)
{
if (e.Result == true)
{
TextBlockResult.Text = string.Format("Your balance: {0}", e.Balance.ToString());
}
else
{
TextBlockResult.Text = "Please login first";
}
}


What we see now is that our out parameter is included in the GetMyBalanceCompletedEventArgs. This way we can use multiple parameters without creating a class for this.

If you press F5 now, and try the button without logging in, you'll see the "Please login first" text in the resultbox.
After you're logged in and press the button the Balance will appear in the resultbox.



That's it for now. I hope this helps to get you started using this type of authentication.

Click here to download the source

Monday, August 18, 2008

Announcing Foundation Silverlight 2 Animation

Past weeks it's been a bit slow on my blog and I haven't been participating on the Silverlight.net forums as much as I would like to.
The reason for this is that I've been pretty busy on a big project.

Jeff Paries, author of multiple books on animation and now the author of "Foundation Silverlight 2 Animation", approached me to do the technical editing of the book and the (150) projects that are used in it. I am very grateful with the opportunity Jeff has offered me and I hope he's not dissapointed with his choice.

Being a technical editor involves reading the book very thorough and testing the code, helping to solve problems, refactor code, etc. With the 150 projects in the book you can imagine this is quite time consuming, but it's also very much fun.
Jeff has selected very interesting, appealing projects and writes in a way that captures your attention and doesn't let it go. So this makes my job a lot easier :)

Visit the website of Jeff Paries for the announcement

Preorder the book on Amazon

Tuesday, August 5, 2008

Silverlight SharpZipLib on CodePlex

Today I added the code for the ported version of the SharpZipLib for Silverlight to CodePlex.

If you want to download the DLL or the source go visit http://www.codeplex.com/slsharpziplib

Tuesday, July 22, 2008

PanoCube in Webdesigner Magazine


For the panocube project, created by Martijn Croezen and me, we teamed up again and wrote a tutorial for Webdesigner Magazine.

It's in store starting today, so get it while it's hot!

Monday, June 23, 2008

Creating a sketch application in Silverlight - Part 2, Saving the sketch on the server

In the second part of this tutorial we're going to pick up where we left off. So we can open the SketchApplicationPart1 solution from the first part of the tutorial (or you can download the source of the second part of this one at the end of this tutorial).
Now we're going to create a way to save the drawing to the server using SharpZipLib in Silverlight, to zip the XML file before sending it to a webservice.

1. Add Click eventhandler for the Save button


and a Helper class for creating the XML.
First we need an eventhandler for the Save button. This is added in the Page_Loaded event of the Page.xaml.cs:
SaveButton.Click += new RoutedEventHandler(SaveButton_Click);


In the Lib Folder, which I created, I add a class "XMLHelpers.cs".
This class only contains a single method to create the XML file. I used LINQ (both to objects and XML) to iterate through the StrokeCollection and create the XML. The class returns an XElement object.
Add the following method "CreateXElementsFromStrokeCollection" to the class:

public static XElement CreateXElementsFromStrokeCollection(StrokeCollection DrawingStrokeCollection)
{
var xmlElements =
new XElement("Strokes", DrawingStrokeCollection
.Select(stroke =>
new XElement("Stroke",
new XElement("Color",
new XAttribute("A", stroke.DrawingAttributes.Color.A),
new XAttribute("R", stroke.DrawingAttributes.Color.R),
new XAttribute("G", stroke.DrawingAttributes.Color.G),
new XAttribute("B", stroke.DrawingAttributes.Color.B)
),
new XElement("Points", stroke.StylusPoints
.Select(point =>
new XElement("Point",
new XAttribute("X", point.X),
new XAttribute("Y", point.Y),
new XAttribute("PressureFactor", point.PressureFactor)
)
)
),
new XElement("Width", stroke.DrawingAttributes.Width),
new XElement("Height", stroke.DrawingAttributes.Height)
)
)
);

return xmlElements;
}


This is an example of the xml the function returns:






13
13







13
13



Looks nice with a minimal amount of effort thanks to LINQ.
By the way, don't forget to add a reference to System.Xml.Linq and add both the using's for System.Linq and System.Xml.Linq.

2. Create a webservice in your WebAppication Project

Name it SaveDrawingService.asmx.
In this tutorial I used an ASMX webservice, but it's almost just as easy to implement a WCF service.
This webservice should contain 2 methods, one for saving a new drawing and one for saving a already saved drawing.
These methods are uncomplicated because all we have to do here is to save the byte[] that is sent from the Silverlight application to the server:

[WebMethod]
public Guid SaveNewDrawing(byte[] zippedFileBytes)
{
Guid drawingID = Guid.NewGuid();
DirectoryInfo drawingFolder = new DirectoryInfo(Server.MapPath("~/App_Data/Drawings/"));
if (!drawingFolder.Exists)
drawingFolder.Create();
FileInfo fi = new FileInfo(Server.MapPath("~/App_Data/Drawings/" + drawingID.ToString() + ".zip"));
if (fi.Exists)
fi.Delete();

FileStream fileStream = new FileStream(Server.MapPath("~/App_Data/Drawings/" + drawingID.ToString() + ".zip"), FileMode.OpenOrCreate);

fileStream.Write(zippedFileBytes, 0, zippedFileBytes.Length);
fileStream.Flush();
fileStream.Close();
return drawingID;
}

[WebMethod]
public bool SaveEditedDrawing(Guid DrawingID, byte[] zippedFileBytes)
{
FileInfo fi = new FileInfo(Server.MapPath("~/App_Data/Drawings/" + DrawingID.ToString() + ".zip"));
if (fi.Exists)
fi.Delete();

FileStream fileStream = new FileStream(Server.MapPath("~/App_Data/Drawings/" + DrawingID.ToString() + ".zip"), FileMode.OpenOrCreate);

fileStream.Write(zippedFileBytes, 0, zippedFileBytes.Length);
fileStream.Flush();
fileStream.Close();
return true;
}


3. Add a Service Reference to the ASMX webservice


Before doing this, always try if the ASMX webservice opens in the browser by right-clicking the asmx file of the webservice in VS2008 and clicking "View in browser".
If it opens correctly, it can be added to the Silverlight project using the namespace SaveDrawingService.

4. Add the SharpZipLib library to your Silverlight project


The version I rebuilt is added in the sourcecode, so you can add the project, or compile a DLL and reference that.

5. Implement the save functionality in Silverlight (zipping and sending)


First, add the System.IO and System.Text namespaces in the using section of the Page.xaml.cs. Next, create a private field "_currentDrawing" of type Guid to save the guid of the drawing (this is returned by the webservice).
Now, the following code needs to be used for the click event:
void SaveButton_Click(object sender, RoutedEventArgs e)
{
SaveDrawingService.SaveDrawingServiceSoapClient wsclient = new SaveDrawingService.SaveDrawingServiceSoapClient();

// A memory stream to write to
using (MemoryStream zippedMemoryStream = new MemoryStream())
{
// A ZIP stream
using (ZipOutputStream zipOutputStream = new ZipOutputStream(zippedMemoryStream))
{
// Highest compression rating
zipOutputStream.SetLevel(9);

byte[] buffer;

// The string to use as file content
using (MemoryStream file = new MemoryStream(Encoding.UTF8.GetBytes(Lib.XMLHelpers.CreateXElementsFromStrokeCollection(inkpresenter.Strokes).ToString())))
{
buffer = new byte[file.Length];
file.Read(buffer, 0, buffer.Length);
}

// Write the data to the ZIP file
ZipEntry entry = new ZipEntry("drawing.xml");
zipOutputStream.PutNextEntry(entry);
zipOutputStream.Write(buffer, 0, buffer.Length);

// Finish the ZIP file
zipOutputStream.Finish();
}
if (_currentDrawing == Guid.Empty)
{
wsclient.SaveNewDrawingCompleted += new EventHandler(wsclient_SaveNewDrawingCompleted);
wsclient.SaveNewDrawingAsync(zippedMemoryStream.ToArray());
}
else
{
wsclient.SaveEditedDrawingCompleted += new EventHandler(wsclient_SaveEditedDrawingCompleted);
wsclient.SaveEditedDrawingAsync(this._currentDrawing, zippedMemoryStream.ToArray());
}
}
}

void wsclient_SaveEditedDrawingCompleted(object sender, SaveDrawingService.SaveEditedDrawingCompletedEventArgs e)
{
//Add functionality to show user drawing is saved
}

void wsclient_SaveNewDrawingCompleted(object sender, SaveDrawingService.SaveNewDrawingCompletedEventArgs e)
{
this._currentDrawing = e.Result;
//Add functionality to show user drawing is saved
}


An explanation of what happens here: First, an instance of the WebserviceSoapClient is created. Next, a new memorystream is created for writing and a ZipOutputStream is created for zipping the "XML string" returned by the XElement which is returned by the CreateXElementsFromStrokeCollection method.
A zipentry "drawing.xml" is created for the actual drawing in the zipfile. The byte array created by the zippedMemoryStream.ToArray() is then sent asynchronously to the server.

I'll call it a day for now. If I get around to it, I'll create a third part to add some more functionality to the sketching application.

Click here for the source

Creating a sketch application in Silverlight - Part 1, Sketching on the InkPresenter

As some of you know, I try to help people out on the Silverlight Forum. It's nice to help people and I have learned a lot by doing that.


One of the members of the forum (RamsZone) had a question about saving strokes that are drawn on the InkPresenter. Since I was already working on a sketch application and had already solved that puzzle, I promised him/her to post an article on my weblog explaining how to do this.
So I started a few hours ago to create a simple sketch application with save functionality. This article is the first part of describing how to create a sketch application. This part is focussed on the sketching itself. The next part will address the save issue. I hope to post the second part today as well.


1. Getting started


First, we need to create the XAML necessary for our sketch application. All we need right now is the InkPresenter and a button which the user can press to save the drawing:



2. Add eventhandlers


We need eventhandlers for the MouseLeftButtonDown, MouseLeftButtonUp and MouseMove events to make sure our sketch application responds to input the way we would expect it to:

LayoutRoot.MouseLeftButtonDown += new MouseButtonEventHandler(LayoutRoot_MouseLeftButtonDown);
LayoutRoot.MouseLeftButtonUp += new MouseButtonEventHandler(LayoutRoot_MouseLeftButtonUp);
LayoutRoot.MouseMove += new MouseEventHandler(LayoutRoot_MouseMove);


3. MouseLeftButtonDown and MouseLeftButtonUp handlers


We need very little code to produce the functionality we need in these handlers.
All we need to do here is set the value of private field to make sure that drawing is starting and stopped. This way we can determine if the visitor is keeping the mousebutton down so he/she is drawing:
void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
this._drawing = false;
this._drawingContinuousLine = false;
}

void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this._drawing = true;
}


4. MouseMove event


This is were the drawing is really done. Normally you would assume the code to be quite simple, something like this:
StylusPointCollection originalPoints = e.StylusDevice.GetStylusPoints(inkpresenter);
Stroke stroke = new Stroke();
stroke.DrawingAttributes.Color = Colors.Black;
stroke.DrawingAttributes.Width = 13.0;
stroke.DrawingAttributes.Height = 13.0;
stroke.StylusPoints = originalPoints;
inkpresenter.Strokes.Add(stroke);


So, this works.... There are strokes drawn on the inkpresenter. However... (bet you didn't see that one coming ;) ), the strokes are visible as dots. When moving the mouse very slow, it seems to be no problem. When moving very fast, there are gaps between the dots which makes it look like a dotted line instead of a smooth line.
We can fix this by using keeping track of the last detected point and if a continuous line is drawn. We do this by declaring 2 private field, so our private fields look like this:
public partial class Page : UserControl
{
private bool _drawing = false;
private bool _drawingContinuousLine = false;
private StylusPoint _lastDetectedPoint;


The MouseMove will look like this:
void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
{
if (this._drawing)
{
StylusPointCollection originalPoints = e.StylusDevice.GetStylusPoints(inkpresenter);

StylusPointCollection newPoints = new StylusPointCollection();
StylusPointCollection moreNewPoints = new StylusPointCollection();

if (this._drawingContinuousLine)
{
StylusPoint point = new StylusPoint(this._lastDetectedPoint.X + 3, this._lastDetectedPoint.Y + 3);
point.PressureFactor = this._lastDetectedPoint.PressureFactor;
newPoints.Add(point);

StylusPoint morepoint = new StylusPoint(this._lastDetectedPoint.X + 6, this._lastDetectedPoint.Y + 6);
morepoint.PressureFactor = this._lastDetectedPoint.PressureFactor;
moreNewPoints.Add(morepoint);
}

foreach (StylusPoint thisPoint in originalPoints)
{
StylusPoint morepoint = new StylusPoint(thisPoint.X + 6, thisPoint.Y + 6);
morepoint.PressureFactor = thisPoint.PressureFactor;
moreNewPoints.Add(morepoint);

StylusPoint newpoint = new StylusPoint(thisPoint.X + 3, thisPoint.Y + 3);
newpoint.PressureFactor = thisPoint.PressureFactor;
newPoints.Add(newpoint);
}

Stroke stroke = new Stroke();
stroke.DrawingAttributes.Color = Colors.Black;
stroke.DrawingAttributes.Width = 13.0;
stroke.DrawingAttributes.Height = 13.0;
stroke.StylusPoints = newPoints;
inkpresenter.Strokes.Add(stroke);

Stroke morestroke = new Stroke();
morestroke.StylusPoints = moreNewPoints;

this._drawingContinuousLine = true;

this._lastDetectedPoint = originalPoints[originalPoints.Count - 1];
}
}


One thing that isn't handled in this application is resizing of the browser. If you remove the width and height in the UserControl in XAML and of the LayoutRoot Grid, you will be able to draw in the full available space of the browser. However, the application must be (re)loaded when the browserwindow is maximized.

That's it for now. Now you can start practising your sketching skills. In the next part I'll try to explain how to save the drawings on the server.


Click here for the demo

Click here for the source

Friday, June 20, 2008

Enabling browser back button in Silverlight 2 beta 2

Who hasn't encountered the problem of not being able to use the browser buttons when navigating a Flash, Silverlight or AJAX enabled website?
This articles describes how you can prevent this in your Silverlight application.

In this article I will explain how to use a class created by Mark Rideout for his TechEd presentation during this year's TechEd in the US.

1. Implement the NavigationManager


Include the NavigationManager.cs in your Silverlight application.
You can find it in the sourcecode of the project (see below).

Now implement the INavigationState interface on you main Silverlight UserControl(usually the Page UserControl).
public partial class Page : UserControl, INavigationState

#region INavigationState Members

public NavigationPropertyDictionary GetState()
{
throw new NotImplementedException();
}

public void LoadState(NavigationPropertyDictionary properties)
{
throw new NotImplementedException();
}

public string Id
{
get { throw new NotImplementedException(); }
}

#endregion

2. Instantiate the NavigationManager in the constructor


NavigationManager.Instance.Register(this);            
NavigationManager.Instance.LinkingMode = NavigationManager.LinkMode.None;

3. Declare a page indexer field and set it


This field is saved in the state of the NavigationManager so you know which page to show.
private int _pageIndex=-1;

On a state change, set the value of the page indexer field and save the current state:
_pageIndex = 1;
HtmlPage.Document.SetProperty("title", "Page 1");
NavigationManager.Instance.Save();


Now do this for all the state changes you want to have in your browser history:

_pageIndex = 2;
HtmlPage.Document.SetProperty("title", "Page 2");
NavigationManager.Instance.Save();

4. Create code for GetState()


When implementing the interface a number of methods were created.
The GetState() method sets the index of the NavigationPropertyIndex to the pageindex used in this example:

public NavigationPropertyDictionary GetState()
{
NavigationPropertyDictionary props = new NavigationPropertyDictionary(this);
props["index"] = _pageIndex.ToString();
return props;
}

5. Create code for LoadState


The retrieval of the pages from the state need to be implemented in this method.
By using the NavigationPropertyDictionary's index property we are able to retrieve the current index:

public void LoadState(NavigationPropertyDictionary properties)
{
ContentPlaceholder.Children.Clear();

if (properties.Count > 0)
{
UserControl pageToLoad = new UserControl();
switch (int.Parse(properties["index"]))
{
case 1:
HtmlPage.Document.SetProperty("title", "Page 1");
pageToLoad = new Subpage1();
break;
case 2:
HtmlPage.Document.SetProperty("title", "Page 2");
pageToLoad = new Subpage2();
break;
case 0:
HtmlPage.Document.SetProperty("title", "Home");
break;
default: break;
}
ContentPlaceholder.Children.Add(pageToLoad);
}
}

6. Create code for the ID property


This property is used for naming the ParentStateID in the NavigationManager. You'll also see this name in the querystring (depending on the settings).

public string Id
{
get { return "home"; }
}

7. Optional setting; LinkingMode


The property available on the NavigationManager which enables the address bar to be updated as well. So the querystring is set to the value necessary for deeplinking.
To set the LinkingMode property use the following code:
NavigationManager.Instance.LinkingMode = NavigationManager.LinkMode.None;


Click here for the demo

Click here for the source

Sunday, June 8, 2008

How to detect NO mouse movement

In some occasions you might want to show or hide stuff in your Silverlight application when the visitor stopped moving his/her mouse.
For instance, most media players use this method to hide the play/pause/forward/mute controlbar.

You can achieve this by doing following this:

1. Create a DispatcherTimer

private DispatcherTimer _nomousemoveTimer = new DispatcherTimer();


2. Use the constructor of your class to create eventhandlers for events listed below and set the interval of the DispatcherTimer (in this case I set it to 3 seconds):
this.MouseMove += new MouseEventHandler(Page_MouseMove);
this.Loaded += new RoutedEventHandler(Page_Loaded);
_nomousemoveTimer.Tick += new EventHandler(_nomousemoveTimer_Tick);
_nomousemoveTimer.Interval = new TimeSpan(0, 0, 3);


3. In the Page Loaded, start the timer:
_nomousemoveTimer.Start();


4. Now, in the Page_MouseMove event, stop the timer and start it immediately after stopping it:
_nomousemoveTimer.Stop();
_nomousemoveTimer.Start();


5. Finally, in the Tick event of the timer, execute your code:
private void _nomousemoveTimer_Tick(object sender, EventArgs e)
{
//do your stuff
}


Click here to view the demo

Click here to download the source

Tuesday, May 20, 2008

Manually controlling storyboard animation

For an animation I did for a customer I wanted it to start the storyboard when the mouse enters and to play backwards, until the state it started in, when the mouse leaves the object.

At this time, in Silverlight 2 beta 1, there is no method I found to play a storyboard backwards from a custom position in the timeline. There is an option to AutoReverse the storyboard but this only occurs when the Storyboard is finished.

So I needed to create a custom method to play a storyboard backwards. The only way we found to do this (thanks again Martijn ;) ) is to manually play the stoyboard. Forward as well as Reverse. By using this method you'll gain total control over the animation.
You can start wherever you want, reverse playback, play faster or slower, etc.

First of all I created a Gameloop (neverending loop) which comes down to a Storyboard which, in the Completed event, restarts the Storyboard animation so you get a neverending loop. There are a lot of posts from fellow Silverlight developers who describe this very well so I won't go any deeper into this. In the sourcecode download is the gameloop.cs file which I always use.

Setting variables and eventhandlers

Second, we need a couple of variables for remembering the state of the animation (is it in Reverse mode or in Forward mode, what's the progress, etc):

 private bool isMouseOver;
private enum PlayDirectionEnum { Forward, Reverse }
private PlayDirectionEnum playDirection = PlayDirectionEnum.Forward;
private TimeSpan progressTime = new TimeSpan(0);
private TimeSpan totalPlayTime;
private int frameRate = 60;

Next step is to create all of the eventhandlers in the constructor of your application for the mouseenter, mouseleave, page_loaded:


        public Page()
{
InitializeComponent();

this.MouseEnter += new MouseEventHandler(Page_MouseEnter);
this.MouseLeave += new MouseEventHandler(Page_MouseLeave);
this.Loaded += new RoutedEventHandler(Page_Loaded);

gameloop.GameloopDuration = new Duration(new TimeSpan(0, 0, 0, 0, 1000 / frameRate));
gameloop.Update += new Amercom.Silverlight.Game.GameLoop.UpdateDelegate(gameloop_Update);
gameloop.Attach(this);
}

MouseEnter and MouseLeave


Now the real coding begins ;)

In the MouseEnter event we need to do a couple of things to start the animation:


            
if (gameloop.IsAttached && progressTime == new TimeSpan(0))
{
gameloop.Start();
MoveBall.Begin();
MoveBall.Pause();
}
playDirection = PlayDirectionEnum.Forward;
isMouseOver = true;

Because we chose to use a gameloop and maintain the state of the animation using variables, the actions in the MouseLeave are minimal:


            isMouseOver = false;
playDirection = PlayDirectionEnum.Reverse;

Animating


Finally, we need to create the gameloop_Update method to play the animation:


        
void gameloop_Update(TimeSpan ElapsedTime)
{
if (isMouseOver || progressTime > new TimeSpan(0))
{
switch (playDirection)
{
case PlayDirectionEnum.Forward:
progressTime = progressTime.Add(new TimeSpan(0, 0, 0, 0, 1000 / frameRate));
break;
case PlayDirectionEnum.Reverse:
progressTime = progressTime.Subtract(new TimeSpan(0, 0, 0, 0, 1000 / frameRate));
if (progressTime == new TimeSpan(0))
resetAnimation();
break;
}
}

if (progressTime >= totalPlayTime)
{
progressTime = totalPlayTime;
}
MoveBall.Seek(progressTime);
}


As you can see is that the main 2 things that are done in this method are:


  1. 1. Set the progressTime (in the switch)

  2. 2. Tell the storyboard to jump to the position (progressTime) using the Seek method


Some of the other things that happen in this method is that we need to reset the animation back to default state (back to the beginning, Forward play instead of Reverse, etc.) when it's reached the beginning of the Storyboard (when playing backwards).

Furthermore I "pause" the animation when it reaches the end. You could also change this behaviour so it autoreverses when it reaches the end of the Storyboard.


Hope this is usefull!


Click here to view the demo
Click here to download the source

Thursday, April 24, 2008

Controlling video with dragging

To control video with dragging might sound a bit strange and useless but keep on reading and you'll see the the possibilities this creates.

For a project I'm working on, I wanted to create a rotating 3D image which the user van control with his mouse using dragging.
This kind of displaying is often used for displaying objects like cars:
http://www.autotropolis.com/car.review/2008/Ford/Mustang/exterior.htm
or purses?
http://www.cheathamlane.net/index.php?section=projects
or other products:
http://www.pioneer.fr/nl/black/products/kuro2007/360_PDP_508XD.html

Getting started


First I created a UserControl and added 2 properties:

  1. "AutoPlay" which plays the video once when loaded

  2. "MovieSource" which contains the Uri of the videofile

  3. "StretchMovie" which contains the Uri of the videofile



To be on the safe side I created a public method "Release" to stop the dragmode. If by some reason the mouse stays in dragmode it can be stopped by calling the Release method:




Custom MouseCursor


For a better drag experience a "custom" mousecursor is added when hovering over the control and when dragging.


To achieve this, add an image ("_cursor" in the code snippet below) to the page which you can position when the mousemove event is triggered. With this.Cursor = Cursors.None you can make sure the default cursor is no longer visible.





Controlling the video


In the eventhandler of the mousemove event, first check if the user is dragging:



If so, set the movieposition to the correct value:



The call to the UpdateVideoPlayer method makes sure the movie gets rendered on the right frame:


Click here to view the demo

Click here to download the source

One issue I experience (could be my laptop) is that it's a shocky. It's not rotating fluently. In Mediaplayer Classic it works fine, so I think it's a rendering issue with SL2. If someone has any ideas about this, please let me know!

That's it for now. I'm leaving to Spain tomorrow for a two week holiday, so no more for at least two weeks! :)

Thursday, April 3, 2008

Panoramic view using texture mapping in Silverlight 2

My colleague Martijn Croezen created an awesome panoramic view with Silverlight 2 beta 1. Because he's a Flash developer with little .NET experience, I tried to help him out a bit with C# syntax and Silverlight objects, etc.

He used texture mapping to create the effect of a panoramic view using the inside of a cube to project the photo's on.
After trying to create this in Silverlight 1.1 alpha he found out there was a bug in the implementation of the matrix, so we were stuck.
With the release of Silverlight 2 beta 1 we decided to give it another go and the problem with the matrix seemed to be resolved.

One issue we still have is that Silverlight uses anti-aliasing which cannot be disabled. Therefore the edges of the images are "blurred" and the triangles used for the texture mapping are still visible (white lines).

Visit the weblog of Martijn Croezen for an in-depth description

View the demo here

Download the (non-optimized/version 1) sourcecode here

Tuesday, April 1, 2008

Motion Blur in Silverlight 2

Gilbok Lee posted an example of motion blur in Silverlight 2 on the Silverlight Forum (http://silverlight.net/forums/t/13167.aspx) which turned out to be an April Fools joke.

Very funny ;)... But thanks to one of the flash developers I work with (thanks Martijn Croezen) I was able to create a motion blur effect.
You can use the sliders for adjusting the amount of blur and the opacity.

Click here for the demo

Click here for the source

Wednesday, March 26, 2008

Using and embedding fonts in Silverlight 2

There are multiple options to embed your fonts in Silverlight 2:

1. Using font as "Content"

First, embed the font as "Content" (see Properties, Build action). With this option, you can use the following method to set the font:

codesnippet1

The "problem" with this can be that the fontfile is embedded in the .XAP file. Therefore is available to anyone who wants to use the fontfile:

screenshot1

 

2. Using font as "Embedded Resource"

The advantage of using this method is that the fontfile is compiled with the DLL. So this makes it hard (maybe even impossible?) to retrieve the fontfile.
If it is still possible (maybe with a decompiler), at least it makes it less easy to get it.

When you want to use this method, first set the Build action to "Embedded Resource".

Next, use the following code to display the correct font (click to view larger size image):
 codesnippet2

 

3. Using font as a downloadable object

This has no real advantages to the above mentioned methods. Maybe if you use a font across multiple sites or something...

First, copy the font file to the ClientBin folder and use a WebClient to download the font:

codesnippet3

When the async request is completed, set the font:

codesnippet4 

That's it for now!

 

Download the source here

Sunday, March 23, 2008

Unpacking ZIP files in Silverlight 2

One of the nice features in Silverlight 1.1 alpha was, in my opinion, the use of zipfiles and being able to use files from a zipped file by using the Downloader object.

In Silverlight 2 the Dowloader is replaced with the WebClient. Which is not a bad thing, but it seems to be missing the ability to use zipfiles. Or I've missed it somehow...

Anyway, I stil wanted the thumbnails on my site to be downloaded using a ZIP file simply because it makes it smaller and easier to handle.

In the past I've been using ICSharpZipLib so I figured I'd give that a try.
After adding ICSharpZipLib project to my Silverlight project I had to change a few things, mostly changing ArrayLists to generic Lists and a few minor other thing. My SharpZipLib project is available for download (bottom of this article) but be sure to test it thoroughly, I've only used a small part of the library.

How does it work

It was quite easy to implement this:

ICSharpZipLib contains a ZipInputStream which converts an existing Stream to a ZipInputStream.

The e.Result property (Stream) of the OpenReadCompletedEventArgs can be used in the constructor of the ZipInputStream class.

After creating the ZipInputStream, a call of the GetNextEntry method will return a ZipEntry (zippedFile). So we need to create a loop to get through all of the files in the ZipInputStream:

codesnippet1

codesnippet2

In between these 2 snippets is the code for handling the files.
A MemoryStream is created for reading the buffer of the outputStream of the zippedFile.

Handling the MemoryStream for each filetype

The extension of the file in the zip is used by the switch to determine which method is called for handling the MemoryStream:

codesnippet3

Inside these functions the stream is used as a parameter for a SetSource of a BitmapImage in case of an image filetype:

codesnippet4

or as a parameter for a StreamReader in case of a text file:
codesnippet5

or as the stream parameter for the SetSource of a MediaElement:

codesnippet6 

The last one, however, crashes IE. Not sure why this happens. When debugging it seems to work fine, but it crashes when playing with a System.CrossAppDomainMarshaledException (crossdomain error) even when using a clientaccesspolicy.xml or a crossdomain.xml.

 

Click here to download SharpZibLib for Silverlight

Click here to download the entire solution

Thursday, March 6, 2008

First Silverlight 2 impressions

After the first few days I wanted to share some of my first Silverlight 2 experiences.

Nice controls!

To be honest, I was very impressed with the set of controls that are supplied. They look great and in the demo app I've downloaded they seem to work well too.

On the downside, after the first day I've had a few dissapointments as well:

No inheritance in UserControls

According to someone on the Silverlight.net forum, it's a "limitation", but I think it's a bug:

It it not possible to use inheritance in a UserControl even if the Base class inherits the UserControl class. When building you get the error:
"Partial declarations of 'CLASSNAME' must not specify different base classes" in the file CLASSNAME.g.cs (g = generated).

When you open the .g.cs file and remove the base class from it, the project compiles fine and also seems to work fine. Looks like a bug in the implementation of Silverlight in Visual Studio.

**************************** UPDATE ****************************
I've posted my problem on the SL.net forum (like some more people) and got some nice responses. Some even seem to work almost right :):

Check out this forum thread: http://silverlight.net/forums/p/10970/34988.aspx

So it is possible but has some peculiarities. For instance, when opening a file in Blend it gives an error. When you have no need for that, it's no problem, but I would like my designers to still have the ability to change things in the design after I used inheritance on the control.
Not quite sure what to think of this...

 

XAP project shortcoming?

This could be my lack of experience in SL2, but again, it's my first impression. If I find a way this does work, I'll post it as well :)

When having multiple Silverlight pages (or applications as they are called now) in a single Silverlight Project it seems impossible to load any other than the default Page.xaml:

- When using the "html" <object> method: it expects a parameter with a xap file, xaml files are not allowed.
- asp:Silverlight control also needs a xap file. asp:xaml doesn't work.

I also posted this at the silverlight.net forum so if I get any solution I'll let you know.

**************************** UPDATE ****************************
There is an option by setting an initParam with the name of the startup control you want to use. I'm not sure if I like this option, but it's a workaround.

WebClient instead of Downloader, improvement?

Okay, probably a better control I assume. Hmmm, dunno about that after implementing it. For a simple thing as loading an image from a zip file (thanks Marnix)....

Old school (1.1 alpha) code:

codesnippet1

New school (2 beta) code:

codesnippet2

Seems like more lines of code. Doesn't necessarily mean that it's a worse solution than the SL1.1 one, but it definitely makes it easier to make an error.

I think that they (MS), maybe in a later version, want to support bitmap effects like Flash does (blurring, etc). Maybe that's the reason they chose for the addition of a bitmap object.
Time will tell.

Uri inconsistency

As Imran Shaik posted on his blog, Silverlight 2 beta 1 is inconsistent when it comes to relative Uri.
The relative path should be the path from the root of the website, as it is in SL1.0 and as it was in SL1.1.

In SL2 beta 1, the path in design is relative to the root of the site, but when running the app, it is relative to the location of the XAP file. So when downloading "thumbnails.zip" using the WebClient it looks for thumbnails.zip in the ClientBin folder instead of in the root of your website.

Setting the WebClient.BaseAddress to the root of the site doesn't seem to work either.

That's what I've encountered up until now. So far, I'm not very positive about this release. I was much happier with SL1.1 alpha, but it's only the first few days.

I'll keep you informed....

Monday, March 3, 2008

Getting Live Messenger Status and display it in Silverlight 2.0 alpha

In expectation of Silverlight 2.0 I decided to do some experimenting with some of the Live API's.

I build a small app which shows my Live Messenger status. And can also show other peoples status if they gave permission.

For now, all you see is a big Messenger icon I created in XAML. I'm not a designer, but you can imagine it's possible to create some nice visuals and animations instead of just displaying a still, like I did.

 

How does it work?

The API Microsoft supplies for this is a JSON API. So the examples they supply are in javascript so I had to find a way to get the data that is returned from the API in javascript into my Silverlight control.

 

Getting acces to Silverlight functions from Javascript

- Make your class Scriptable so that it's possible for Javascript to access methods and properties in your class.

- Also make the properties and/or methods you want to use from JS Scriptable.

It should look something like this:

codesnippet2

codesnippet1

Next, we need to create the call to the API from Javascript.

 

Calling the API

This can also be reviewed at the Microsoft site (http://msdn2.microsoft.com/en-us/library/bb936688.aspx).

The API can be called using an URL of this format:
http://messenger.services.live.com/users/[ID]/[resource]/[?queryparameters]

To call this API, you can use a javascript "include" in a webpage. By using a callback function you can read out the information returned by the API.

The script Microsoft provides looks like this:

codesnippet3

(be carefull, I added a return in the url otherwise it would be too long to fit on the screen)

Now, I wanted to set the id dynamically, so I added a literal to the page in which the script tag is generated from serverside code.
Place this Literal (or script) directly above the body closing tag.

The server side code I use to fill the Literal looks like this:
codesnippet6

Handle the callback

Next is the javascript with the functions to handle the callback and to set the properties in the silverlight object:

codesnippet4

This goes in a separate javascript file I called "MessengerAPI.js" and should be referenced in the head of the html.

 

Calling the API after Silverlight object is loaded

After first running into some problems with calling the setStatus method, it turned out to be a timing problem. setStatus cannot be called before the Silverlight control is completely loaded.

So the trick is to create a onloaded eventhandler in the createSilverlight function and set the variable "control" in the handler so you're 100% sure the control is loaded, like this:

codesnippet5

Finishing up

To be able to see the status of other people, Microsoft requires you to:

1. Have a privacy statement on your site, which they can refer to

2. Have a return URL where Microsoft can redirect to when the user has logged in and confirmed that your website can have access to their status information.

permission_live

Conformation on Live site

Next, I created a separate aspx page as a landingpage for catching the visitor who clickec "I accept" (ReturnUrl.aspx). In this page you can do lots of stuff, set cookies or save userdata in db (change your privacy statement accordingly).
At this time I just do a redirect to the default.aspx with the ID as a querystring parameter.

This brings us back to where we started.

screenshot

 

Click here to view a demo

Click here to download the source for this project

Saturday, March 1, 2008

Spinawards Showcase website in showcase on Silverlight.NET

The site my colleagues (http://www.amercom.nl) and I created as a showcase for the Spinawards entries has been placed on the Silverlight.NET site, in the showcase section!

http://silverlight.net/showcase/

Friday, February 29, 2008

Google Maps vs Microsoft Virtual Earth

For those people who don't want to read and just wanna play, here's the demosite: http://www.wijzijnhier.nl/Showcase/Maps_vs_LiveEarth/

After working with Google Maps and wanting to review Microsoft Virtual Earth I thought I'd have a look and see what the differences and similarities are. I also noticed that not everybody knows of the existence of Microsoft Virtual Earth (VE). Nice opportunity to change that as well :)

Virtual Earth is an application like Google Maps and has almost the same abilities. There are a few minor differences (for as far as I noticed creating the demo application).

One of the differences is the appearance of the controls. It's a matter of taste, but imho VE looks better than Maps.



image 1: Google Maps 


image 2: Microsoft Virtual Earth



Diving in a bit deeper, into the code, its the about the same story. Implementation is comparable. Only a few different implementations of events and stuff like that. No problems or difficulties with either one while creating the demo app.
One major difference I notices was the extra view option "Bird's eye". This is a sort of an aerial photo but from an angle which gives a better experience than the aerial one:
 



image 3: Bird's eye view

In my opinion both of these map tools are comparable and quite easy to use. My preference for a map tool is Virtual Earth because of the Bird's eye view and the nicer controls.

Microsoft is also working on MapCruncher for Microsoft Virtual Earth. This tool can be used to importing, layering and sharing maps using the Virtual Earth platform. Maybe I'll check that out later on.

If you want to tryout Google Maps and Live Earth for yourself, I made a webpage that holds both of the controls.
You can find it here:
http://www.wijzijnhier.nl/Showcase/Maps_vs_LiveEarth/

More info:
http://maps.google.com/
http://www.microsoft.com/virtualearth/default.aspx

Scott Guthrie releases more info on Silverlight 2.0

He now focusses on Expression Blend and how this works with Silverlight 2.0. Again a great set of tutorials.

Can't wait to get started!

Click here to check it out

Thursday, February 28, 2008

Tweening library for Silverlight

Check out MC's Sandpit for a great tween library for Silverlight based on the actionscript library Tweener.

TweenerDooby

The article contains by some nice samples and explanation. The project is shared on Codeplex, source code available for download.

Monday, February 25, 2008

Silverlight Pong

A while back I created a Silverlight 2.0 (1.1) alpha Pong game, for the excercise.

pong

You can view it here: http://www.wijzijnhier.nl/Showcase/Silverlight/Pong/Testpage.html

and download it here: http://www.wijzijnhier.nl/Showcase/Silverlight/Pong/pong.zip

Have fun!

Sunday, February 24, 2008

First look(s) at Silverlight 2.0

Scott Guthrie has published an article with a 8! tutorials of a Digg client application in SL2.0: http://weblogs.asp.net/scottgu/archive/2008/02/22/first-look-at-silverlight-2.aspx

Koen of First Floor Software wrote an article about the control hierarchy in SL 2.0. (take a look at the saved screenshot of the toolbox in VS): http://www.firstfloorsoftware.com/BlogPost.aspx?id=12127

Spinawards Flash and Silverlight site

Last week we sent in our entry for the Spinawards (Dutch web award).
We created a Flash site and a Silverlight site to show our entries to the jury and to our customers and to you :).

Check it our over here:
(Flash version) http://spinawards.amercom.nl/
(Silverlight version) http://spinawards.amercom.nl/silverlight

Soon I'll publish more silverlight content so stay tuned.