Sunday, September 21, 2008

Scale, Move and rotate controls in your GUI - WPF

In this blog post I will describe and share a custom control in WPF (Windows Presentation Foundation) that you can use to encapsulate other WPF-controls. This control will add functionality so that you can move, rotate and scale your WPF-controls with your mouse. It also adds a control that will rasie an event (Click-event) when clicked. You can download a sample project with the control at the bottom of the page.

image

In the image above the red square is a control that will raise a click-event. The outer gray squares are for rotation and the inner ones are for scaling. In the middle the control can be moved (translated).

Background
In a previous post I did this control in Silverlight. This is the WPF version and I have made some adjustments and used some other solutions in this version. These will be described below.

Problem
Many times when you design an application you do not only want to present information to a user, you also want the user to interact with your program and your controls. Sometimes this interaction involves moving, scaling and rotating an object. I missed a simple control to wrap other controls in that would add this.

How does it work?
Feel free to compare this solution with the Silverligh version. To start with, this is a control that can be used as a container for other controls. This means that you do not have to modify any other code in your existing controls. This control just adds the ability to interact (move, scale and rotate) with your control.
To do this we need a bock of Transforms in our custom control:

<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Scale}" ScaleY="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Scale}" />
<RotateTransform Angle="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Angle}"/>
<TranslateTransform X="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=X}" Y="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Y}"/>
</TransformGroup>
</Grid.RenderTransform>

This is the first different to the Silverlight version. In Silverlight we used the PART_-convension and set the ScaleX, Angle etc. in code behind through these variables. We still have dependency properties in code behind but we don't need to have the reference to the Transforms. Instead we use a binding to our properties. The RelativeSource property is something that doesn't exist in Silverlight and therefore we need the PART_-solution in that example. In WPF we can solve the problem by using a binding and in that way we doesn't restrict us as much in the template. It also results in less code in code behind (a lot of code/logic is removed) and that will make this control simpler and easier to maintain.

The templates are much the same in WPF/Silverlight. One other difference than the binding is that the VisualStateManager doen't exist in WPF (but I think it will be implmeneted in future versions). I love the VisualStateManager and its separation between control and template. In WPF we instead put all the interaction controls in a Panel and then set the Visibility of that panel in code behind.

To get all the interaction working we hook up on mouse events in both our interaction controls (panels) and also in our base the custom control (inherited from ContentControl). When I first translated from Silverlight to WPF the whole control froze after the first interaction. The listeners for mouse event looks like this:
private void OnScaleControlsMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_previousPosition
= e.GetPosition(this);
this.CaptureMouse();
_iteractionMode
= InteractionMode.Scaling;
}

We call CaptureMouse() and in Silverlight this will be automatically released then the mouse button is released. In WPF we need to do this manually by setting:
private void OnControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_iteractionMode
= InteractionMode.None;
Mouse.Capture(
null);
}

With Mouse.Capture(null) the mouse will be released.

WPF also have more Cursors that we can use. We use more common cursors for moving and sizing which makes the interaction more intuitive.

image 
Code and sample project can be found here.

Saturday, September 20, 2008

Scale, Move and rotate controls in your GUI - Silverlight

In this blog post I will describe and share a custom control in Silverlight 2 that you can use to encapsulate other Silverlight-controls. This control will add functionality so that you can move, rotate and scale your Silverlight-controls with your mouse. It also adds a control that will rasie an event (Click-event) when clicked. You can download a sample project with the control at the bottom of the page.
image
In the image above the red square is the are that will raise a click-event when pressed. The outer squares at the other corners is controls for rotation and the inner squares are controls for scaling.

Problem
Many times when you design an application you do not only want to present information to a user, you also want the user to interact with your program and your controls. Sometimes this interaction involves moving, scaling and rotating an object. I missed a simple control to wrap other controls in that would add this.

How does it work?
A custom control in Silverlight and WPF is a control that contains some functionality and it is look-less. This means that you can change and switch the presentation of a control by changing its template. The base class chosen for this control is ContentControl and this base class is extremely powerful for encapsulating other controls (the content).
The functionality we want in this control is logic for clicking, moving, rotating and scaling. This includes listening to mouse-events of different kinds and the acting on those. What we need is some way of catching these events for the different type of transforms that we want to do.

<Grid x:Name="PART_TranslateControls">
<Rectangle Fill="Transparent" Cursor="Hand" />
</Grid>
<Grid x:Name="PART_ClickControls">
<Rectangle Fill="Red" Stroke="Black" Cursor="Arrow"/>
</Grid>
<Grid x:Name="PART_RotateControls">
<Rectangle Fill="DimGray" Stroke="Black"/>
<Rectangle Fill="DimGray" Stroke="Black"/>
<Rectangle Fill="DimGray" Stroke="Black"/>
</Grid>
<Grid x:Name="PART_ScaleControls">
<Rectangle Fill="LightGray" Stroke="DimGray"/>
<Rectangle Fill="LightGray" Stroke="DimGray"/>
<Rectangle Fill="LightGray" Stroke="DimGray"/>
</Grid>



The rectangles in the snippet above is only the appearance of the control and since we are listening to events from the panels we can set any look and feel of the clickable areas. Also not that we use the PART_ convention for creating custom controls.

In code behind, we add listeners for mousedown events for the different panels and calculates new position, rotation or size when the mouse is moved. The ContentControl contains a TransformGroup with a ScaleTransform, RotateTransform and a TranslateTransform which is the values that we are adjusting.

<TransformGroup>
<ScaleTransform x:Name="PART_Scale"/>
<RotateTransform x:Name="PART_Rotation" />
<TranslateTransform x:Name="PART_Translation" />
</TransformGroup>

We also have dependency properties that we can bind to in this control. The most beautiful solution for this would be to skip using PART_ for the transform and bind to the properties instead. The problem is that you can not bind to ScaleX, Angle, X etc. and therefore we need to get these parts in code-behind and then set the properties manually.

In the sample project below you can see all the mechanisms for this control.

image
Update 1: The sample code is now updated to Silverlight 2 RTW and I also added an sample of a transformable TextBox and functionality for ZIndex.
Update 2: The sample code has been updated. Hal9000Lives pointed out that the calculations for the rotation used the upper left corner as its center point. This has now been changed to use the middple point as center.
Code and sample project can be found here.

WPF-version of this control can be found here.

Thursday, September 4, 2008

How to use Sessions in WCF and Silverlight (basicHttpBinding)

I was writing a WCF Service for a Silverlight application and I needed to use Sessions in my WCF service to store some info. As you know Silverlight 2 (beta 2) only supports basicHttpBinding and therefore we can not use sessions as in wsHttpBinding. I wanted to write something like:

HttpContext.Current.Application.Add("MyKey", someInfo);

But HttpContext.Current returned null. After reading some threads in forums I found this workaround, make your WCF Service ASP.NET compatible:

1. Put this attribute on your WCF Sevice class: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService : IMyService
{
...
}

2. In your Web.config for your WCF Service project set:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>

This will make HttpContext.Current return something and you can now store your sessions. The difference I found in my solutions from some others is that I had a separate project for my WCF service than my ASP.NET web-project and that made the HttpContext fail.

Read more here: https://silverlight.net/forums/p/14175/57586.aspx

Thursday, June 19, 2008

WCF - The type name ServiceReference1 does not exist in the type Namespace.ClassName

This is a short blog post about a strange error when adding a WCF service reference. A WCF service was created and I wanted to add a service reference to a client project. I added the reference through Visual Studio and everything went fine until I compiled. Then I got this error:
Error    3    The type name 'ServiceReference1' does not exist in the type 'MyWorld.MyWorld' ...\Service References\ServiceReference1\Reference.cs

I found that this is caused by me having the same namespace name as class name (MyWorld.MyWorld = Namespace.ClassName). Change your namespace to a name that is not the same name as your class and this will compile.

Friday, June 13, 2008

Pixel Shader Wave Effect - Hit-testing in WPF Effect

In my previous post I wrote about a grayscale effect. That effect only changed the color of the GUI. In this post I will describe an effect that will apply a sine-wave effect on a GUI that will displace the controls. The cool thing is that the controls will still work with user interaction. This is quite simple and it is done by implementing an EffectMapping (GeneralTransform) in the ShaderEffect.
Download sample project here.
Download only WaveEffect.dll here.
You must have .NET Framework 3.5 sp1 or later installed for this to work. You also need DirectX SDK installed to be able to compile the pixel shader effect.

image

If you are interested in how pixel shaders work in WPF I refer to my previous post about the grayscale effect. In this post I will not write much about how pixel shaders work in general, but more about how to interact with a GUI after an effect has been applied. As I mentioned before there are different types of effects. An effect can modify the appearance of a GUI (e.g. grayscale, modify colors or contrast...) or it can apply an effect that will displace the controls from its original positions. The wave effect used in this example is an displacement effect that will apply a simple sine-wave to the GUI. Download the sample project above for source code.

1. The Sine-Wave Algorithm
The algorithm is quite simple and we will use it both in out shader file (HLSL-file) and also in our transform class. This is the math part:
Output.Y = Input.Y + (Math.Sin(Input.X * Frequency) * Amplitude);
Output.X = Input.X;
What is this? Ok, we will have some input, a x-value and a y-value, and then provide some output, a x-value and y-value. Think of them as coordinates in an image. In this example we only modify the y value and therefore the output x will be the same as the input x. We then calculate the y-value by taking the input y and add another value to it. The added value comes from a Sine-calculation meaning that it will be a +/- value. This also means that for a position y we will instead output a position above or below the input value, this will create the wave effect. In the Sine-calculation we have two properties that we can change to modify the appearance of the wave (amplitude and frequency).

2. WaveEffect : ShaderEffect
This is the C#-effect that we use in WPF and XAML. It it the interface toward the pixel shader and the GPU. The difference between this effect and the grayscale effect is that we have overridden a property called EffectMapping:

private WaveTransform _waveTransform = new WaveTransform();
protected override GeneralTransform EffectMapping
{
get
{
return _waveTransform;
}
}




We provide a customized transform class that inherits from GeneralTransform.



3. WaveTransform : GeneralTransform

This is out transform class that is associated with our pixel shader (WaveEffect.fx). When we apply the shader, we will displace the controls from their original positions. If we then try to mouse click the control where we see it on screen, we might actually miss is because in the logic of our program it is placed at a different location. The EffectMapping is here to help us. If we implement this mapping of positions, the framework can ask the transform class how the displayed position actually maps to the logical position. It is actually very simple, if the user clicks at screen position x,y, the framework asks the transform-class: -Hey, where is this position in my logical world?


Note that we can not create a pixel shader that is created from total random numbers (noise) and at the same time apply interaction logic. In other words, there are a very strong relation between the pixel shader and the transform. First look at our pixel shader:



sampler2D implicitInput : register(s0);
float frequency : register(c0);
float amplitude : register(c1);

float4 main(float2 uv : TEXCOORD) : COLOR
{
uv.y
= uv.y + (sin(uv.x * frequency) * amplitude);
float4 color
= tex2D(implicitInput, uv);

return color;
}




And this algorithm corresponds to the algorithm in the following WaveTransform (see TryTransform, TransformBounds):

public class WaveTransform : GeneralTransform, ICloneable
{
public WaveTransform()
{ }

public static readonly DependencyProperty FrequencyProperty = DependencyProperty.Register("Frequency", typeof(double), typeof(WaveTransform), new PropertyMetadata(1.0), new ValidateValueCallback(IsValidFrequency));
public double Frequency
{
get { return (double)GetValue(FrequencyProperty); }
set { SetValue(FrequencyProperty, value); }
}

public static bool IsValidFrequency(object value)
{
double checkValue = (double)value;

if (checkValue < 0.0)
{
return false;
}

return true;
}

public static readonly DependencyProperty AmplitudeProperty = DependencyProperty.Register("Amplitude", typeof(double), typeof(WaveTransform), new PropertyMetadata(0.2), new ValidateValueCallback(IsValidAmplitude));
public double Amplitude
{
get { return (double)GetValue(AmplitudeProperty); }
set { SetValue(AmplitudeProperty, value); }
}

public static bool IsValidAmplitude(object value)
{
double checkValue = (double)value;

if (checkValue < 0.0)
{
return false;
}

return true;
}

private bool _isInverse = false;
private bool IsInverse
{
get { return _isInverse; }
set { _isInverse = value; }
}

public override GeneralTransform Inverse
{
get
{
WaveTransform newWaveTransform
= (WaveTransform)this.Clone();
newWaveTransform.IsInverse
= !IsInverse;
return newWaveTransform;
}
}

/// <summary>
/// Transform a bounding box to/from Sin-wave.
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
public override Rect TransformBounds(Rect rect)
{
Rect boundingBox
= rect;

if (IsInverse)
{
boundingBox.Y
= rect.Y - Amplitude;
boundingBox.Height
= rect.Height + (2 * Amplitude);
}
else
{
boundingBox.Y
= rect.Y + Amplitude;
boundingBox.Height
= rect.Height - (2 * Amplitude);
}
return boundingBox;
}

/// <summary>
/// Transform a point to/from the Sin-wave. Use Inverse to get the inverse transformation
/// of the Sine-wave.
/// </summary>
/// <param name="inPoint"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryTransform(Point inPoint, out Point result)
{
result
= new Point();

if (IsInverse)
{
result.Y
= inPoint.Y + (Math.Sin(inPoint.X * Frequency) * Amplitude);
result.X
= inPoint.X;
}
else
{
result.Y
= inPoint.Y - (Math.Sin(inPoint.X * Frequency) * Amplitude);
result.X
= inPoint.X;
}
return true;
}

protected override Freezable CreateInstanceCore()
{
return new WaveTransform();
}

protected override void CloneCore(Freezable sourceFreezable)
{
WaveTransform newWaveTransform
= (WaveTransform)sourceFreezable;
base.CloneCore(newWaveTransform);
this.CopyCommon(newWaveTransform);
}

#region ICloneable Members

public new object Clone()
{
WaveTransform newWaveTransform
= new WaveTransform();
newWaveTransform.CopyCommon(
this);

return newWaveTransform;
}

#endregion ICloneable Members

private void CopyCommon(WaveTransform transform)
{
this.IsInverse = transform.IsInverse;
this.Frequency = transform.Frequency;
this.Amplitude = transform.Amplitude;
}
}




This is pretty much it. Now we have an effect that displaces the controls (WaveEffect.fx and WaveEffect.cs), but we also have a transform mapping (WaveTransform.cs) that makes it possible to interact with the displaced controls. How are we going to use this functionality? Well, I don't know yet, I haven't found a very good area for this, however... It is really cool!!! :)
I think we can use this for a maginfying glass/zoom effect and maybe for some focus-context stuff. In the final release of .NET 3.5 sp1, there will be some added functionality to this. One question that I have is if the pixel shader only works for Pixel Shader version 2.0 (ps_2_0). I tried to implement a mandelbrot effect but it failed due to the shader version. Does anyone know about this? I would like it to work with ps_3_0!

Thursday, June 12, 2008

Grayscale Effect - A Pixel Shader Effect in WPF

This post will describe how Effects (pixel shaders) are used in WPF. I will give a simple step-by-step example of a grayscale effect. You must have .NET Framework 3.5 sp1 or later installed for this to work. You also need DirectX SDK installed to be able to compile the pixel shader effect.

Download Sample Project and source code here.
Download effect here (add it to your project and use a grayscale effect on your WPF components)

image  image   image

1. New Project
Start Visual Studio and create a new WPF Application. (Targeting the .NET 3.5 Framework)

image

2. Add new project. (Targeting the .NET 3.5 Framework)
In your solution, add a new project, a Class Library-project. Name your project "GrayscaleEffect". Now you will have the following view in your Solution Explorer:

image

3. Prepare the Grayscale Effect.
First add some references to the GrayscaleEffect-project:
PresentationCore
PresentationFramework
WindowsBase
Delete the autogenerated Class1.cs-file and instead create a new C# class and name it "GrayscaleEffect.cs". This will be an empty class. Now lets add the C#-part of the effect:

using System;
using System.Windows.Media.Effects;
using System.Windows;
using System.Windows.Media;

namespace GrayscaleEffect
{
public class GrayscaleEffect : ShaderEffect
{
private static PixelShader _pixelShader = new PixelShader() { UriSource = new Uri(@"pack://application:,,,/GrayscaleEffect;component/GrayscaleEffect.ps") };

public GrayscaleEffect()
{
PixelShader
= _pixelShader;

UpdateShaderValue(InputProperty);
UpdateShaderValue(DesaturationFactorProperty);
}

public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0);
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}

public static readonly DependencyProperty DesaturationFactorProperty = DependencyProperty.Register("DesaturationFactor", typeof(double), typeof(GrayscaleEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0), CoerceDesaturationFactor));
public double DesaturationFactor
{
get { return (double)GetValue(DesaturationFactorProperty); }
set { SetValue(DesaturationFactorProperty, value); }
}

private static object CoerceDesaturationFactor(DependencyObject d, object value)
{
GrayscaleEffect effect
= (GrayscaleEffect)d;
double newFactor = (double)value;

if (newFactor < 0.0 || newFactor > 1.0)
{
return effect.DesaturationFactor;
}

return newFactor;
}
}
}


This is the C# side of the effect and will be the interface to the shader effect. This class and its properties (dependency properties) can be used directly from WPF and XAML. Note that the properties can be animated in the same way as other dependency properties. The difference is that we use a special type for the Input property and for our custom property we just refer to a register on the GPU, but we really do not need to know that ;). I really love the simplicity!



4. Add the effect files.

In the GrayscaleEffect-project, add two new files (Add -> New Item). GrayscaleEffect.fx and GrayscaleEffect.ps as Text-file. Make sure that GrayscaleEffect.ps is set as a Resource (in Build Action) in Properties. NOTE: The GrayscaleEffect.fx must be in ANSI format. Convert it to ANSI by opening the file in Notepad and save it, select ANSI format in the save dialog.



5. Now open the GrayscaleEffect.fx and add the following code (this is HLSL - High Level Shader Language):



sampler2D implicitInput : register(s0);
float factor : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color
= tex2D(implicitInput, uv);
float gray = color.r * 0.3 + color.g * 0.59 + color.b *0.11;

float4 result;
result.r
= (color.r - gray) * factor + gray;
result.g
= (color.g - gray) * factor + gray;
result.b
= (color.b - gray) * factor + gray;
result.a
= color.a;

return result;
}



This is our pixel shader. As we can see at the top implicitInput refers to one registry and a float named factor refers to another register. This is the register that we set in our C#-file (the last parameter in RegisterPixelShaderSamplerProperty(,,0) and the parameter in PixelShaderConstantCallback(0)). Below is our entry point the the shader file. We take a position as input and the first thing we do is to get the value in the "texture" at this position.


The magic below this is out grayscale alogrithm. We first create a grayscale value from the red, green and blue channel. Then we set this value in the output value depending on the factor parameter. The factor parameter is the DesaturationFactor in the C#-file and we use it to create a desaturation effect and a gradient between color and grayscale mode.



6. Compile the pixel shader.

We have created the pixel shader effect but we need to compile it to use it. Note that we can do this outside of Visual Studio, but we don't want that, so do the following. Open the project-properties for the GrayscaleEffect-project and select "Build Events". Under Pre-build event command line, add the following:


"$(DXSDK_DIR)Utilities\Bin\x86\fxc.exe" /T ps_2_0 /E main /Fo"$(SolutionDir)GrayscaleEffect/GrayscaleEffect.ps" "$(SolutionDir)GrayscaleEffect/GrayscaleEffect.fx"


If you now compile the project your effect should compile just fine. But we are not using it so lets use it:



7. Now in WpfApplication1.

First add a reference to the GrayscaleEffect-project from the WpfApplication1-project.


We also want to use some images that we can apply out effect on. NOTE that the effect is not limited to images, we can use any control or panel in WPF, and that's cool!!! However, we use images because they are nice ;). Add a folder the the WpfApplication1 called "images". Add a few (~4) images to this folder (Add -> Existing item), name them like img1.jpg, img2.jpg... .


Your Solution Explorer should look like this:


image 
Now add the following XAML to Window1.xaml:



<Window x:Class="WpfApplication1.Window1"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:effect
="clr-namespace:GrayscaleEffect;assembly=GrayscaleEffect"
Title
="Grayscale Effect - Dotway (www.dotway.se)" Height="400" Width="480">

<Window.Resources>

<DataTemplate x:Key="itemTemplate">
<Grid Width="225" Margin="3">
<Border BorderBrush="White" BorderThickness="2">
<Image Source="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image.Effect>
<effect:GrayscaleEffect x:Name="grayscaleEffect"/>
</Image.Effect>
<Image.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1.0" Duration="0:0:0.3" AccelerationRatio="0.10" DecelerationRatio="0.25" Storyboard.TargetName="grayscaleEffect" Storyboard.TargetProperty="(effect:GrayscaleEffect.DesaturationFactor)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0.0" Duration="0:0:4" AccelerationRatio="0.10" DecelerationRatio="0.25" Storyboard.TargetName="grayscaleEffect" Storyboard.TargetProperty="(effect:GrayscaleEffect.DesaturationFactor)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Border>
</Grid>
</DataTemplate>

</Window.Resources>

<Grid Background="Black">

<ItemsControl x:Name="myItemsControl" ItemTemplate="{StaticResource itemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

</Grid>

</Window>


We have a ItemsControl that have a WrapPanel as layout panel. We also have a template for each item in the ItemsControl. In the template two triggers have been added with animations. These animations will animate the DesaturationFactor property which will affect the shader effect.

To use the images add the following to the code-behind file:




public Window1()
{
InitializeComponent();

List
<string> images = new List<string>();
for (int i = 0; i < 4; i++)
{
images.Add(
"images/img" + i + ".jpg");
}
myItemsControl.ItemsSource
= images;
}






Now just compile and run your new effect!

BUT, how is this really useful except that it gives us "pretty images". Well, I am a fan of focus and context/master-detail implementations. It is all about putting focus to some part of my GUI and leave the rest unfocused and in a context. With the color/grayscale we can put focus to an item by giving it color and all the rest (context) is grayscale. 
This is not the end of it. In a later post I hope I can show you how to interact with the GUI after applying a pixel shader effect. The only thing you have to do is to implement EffeceMapping in you C#-file, more on that later. 
I hope this will help someone, otherwise contact the consultants at Dotway (www.dotway.se)! ;)

Wednesday, June 11, 2008

<Image Source="images/myimage.jpg" /> in WPF

I had a very annoying problem when I did a simple WPF program. I just wanted to test something and I wanted my program to display an image. This is what I did:

1. Create a new WPF application in Visual Studio.

2. Create a folder in my project with the name "images". Add an image to this folder on disk.

3. Right click the images-folder and select add existing item. The image is added and it is set to "Resource".

4. In Window1.xaml set the following code:

<Window x:Class="TestProject.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject"
    Title="Test" Height="500" Width="650">

    <Grid Background="Black">
        <Image Source="images/1.jpg" />        
    </Grid>

</Window>

5. When running this code it works ok, but when I want to see it in the Visual Studio Designer I get an exception saying:

Error    1    The file /images/1.jpg is not part of the project or its 'Build Action' property is not set to 'Resource'.   

Solution: Rename the image to something like img1.jpg. I do not know why this happen but it works if you rename the image-file. You could rename it to 2.jpg and it will work. Anyone?

Use Live ID to login to your Silverlight application

In this post I will give you a step by step walkthrough of how to use Windows Live ID Web Authentication for logging in to a Silverlight app.

My first attempt was to create a login directly in Silverlight. I looked at Windows Live ID Client SDK which is the WinForm version of the Live ID SDK, but it seems that this SDK can not be used with Silverlight apps. The next approach was to use ASP.NET authentication and encapsulate the Silverlight authentication in ASP.NET sites. This is exactly what I will describe below. The example is pretty much independent of Silverlight version, but it uses Silverlight 2 (Silverlight 2 Beta 2).

 

1. Register your application with Windows Live ID

First of all we need to register our application with the Windows Live ID. To be able to use the authentication functionality of Live ID the application must be registered. Go to https://msm.live.com/app/default.aspx and select "Register an Application".

image

Note: If you want to use localhost in development and testing you must register this as an application. If you then have another server for publishing the application, you also need to register that one as another application. In this example I will use localhost.

image

E.g.:
Application Name: MySilverlightLoginTestDev
Return URL: http://localhost:5501/LoginSampleWeb/authresponse.aspx
Domain Name: [Leave this empty for localhost registration]
Secret Key: Some7ingVery_1ong_AnD_S7ran9e
Application verifier required: 0

Remember the port that is registered as return URL, this will later be set in Visual Studio. When the information has been submitted you find information about it under "Manage My Application". You also find an Application ID here that you need later in Visual Studio.

Now you have registered an application with Live ID and it is time for code and Visual Studio.

 

2. Sample Project

<SourceCode>Download sample project here.</SourceCode>

First download, unzip and open the Visual Studio sample. You will see something like this in the Solution Explorer:

image

1. Open web.config, you find this block in the config file. Copy the information that you entered previously in the registration.

<appSettings>
   <add key="wll_appid" value="YourAppIdHere e.g. 00161AA1111111A"/>
   <add key="wll_secret" value="YourSecretKeyHere e.g. Some7ingVery_1ong_AnD_S7ran9e"/>
   <add key="wll_securityalgorithm" value="wsignin1.0"/>
   <add key="loginpage" value="Default.aspx"/>
   <add key="loggedinpage" value="LoginSampleTestPage.aspx"/>
   <add key="logincookie" value="webauthtoken"/>
</appSettings>

2. Now set the right port number. Select the web project in Solution Explorer (LoginSampleWeb) and then select properties.

image image

In the properties view, set Use dynamic ports to "False" and then set the port number to the same as you entered after localhost in the registration (localhost:5501).

3. Run the example.

image  image

3. How does it work - Overview

When you start the sample project Default.aspx will open. This is a very simple page that only displays some text and a "Sign in" link.

<body>
    <h1>Silverlight login using Windows Live&trade; ID Web Authentication SDK</h1>   

    <iframe
       id="WebAuthControl"
       name="WebAuthControl"
       src="http://login.live.com/controls/WebAuth.htm?appid=<%=_applicationId%>&style=font-size%3A+10pt%3B+font-family%3A+verdana%3B+background%3A+white%3B"
       width="80px"
       height="20px"
       marginwidth="0"
       marginheight="0"
       align="middle"
       frameborder="0"
       scrolling="no">
   </iframe>   
</body>

When the user clicks the link he/she will be redirected to a Live login page. If the login is successful the Live ID login page will return to the page that was specified in the registration, e.g. authresponse.aspx. In authresponse.aspx a cookie will be set with some settings to determine whether a user is logged in or not. This will also redirect the user to the right target page (LoginSampleTestPage.aspx). You can also get a specific token ("stoken") for the logged in user that you can use to identify your user and connect to user specific settings.

In LoginSampleTestPage.aspx we check that the user really is logged in. If he/she is not, we redirect them to the login page. If they are logged in we continue to load the page which in this case contains a Silverlight control.

    private static WindowsLiveLogin _windowsLiveLogin = new WindowsLiveLogin(true);
    protected static string _applicationId = _windowsLiveLogin.AppId;

    protected void Page_Load(object sender, EventArgs e)
    {
        HttpRequest request = HttpContext.Current.Request;
        HttpCookie loginCookie = request.Cookies[_windowsLiveLogin.LoginCookie];

        if (loginCookie == null)
        {
            RedirectToLoginPage();
        }

        string token = loginCookie.Value;
        if (string.IsNullOrEmpty(token))
        {
            RedirectToLoginPage();
        }

        WindowsLiveLogin.User user = _windowsLiveLogin.ProcessToken(token);
        if (user == null)
        {
            RedirectToLoginPage();
        }
    }

4. Summary

This is in fact only a encapsulation of a login to Silverlight. We use ASP.NET and server authentication for the login procedure and then give access to the Silverlight application. It is very easy to implement and we let the Live services handle the user registration and so on. Someone also mentioned that Silverlight will get some authentication controls in future versions but I do not know if that is true or not?! If such a control will come, then we can do client side authentication directly in Silverlight.

 

This example uses a modified version of WindowsLiveLogin.cs. The original file can be downloaded from the Windows Live ID Authentication SDK. You also find documentation and useful links at that location.