Silverlight & Expression Blend 3

11 07 2009

Yesterday, there was the long awaited release of Silverlight 3 and Expression Blend 3. So now we can finally bring all the new and exciting things onto live environments, and share it with the world: See the Light!

For those of you who haven’t had the time to follow the Beta, Silverlight 3 brings a wealth of new features features:

Blend 3 also has gone trough a major update, some of the major improvements are:

Over the next few weeks I will write about each of these features, and show how they can impact your development.

All the downloads can be found here.

Have fun playing!

-Robertjan Tuit



No Debugging after converting solution to VS 2010 and Silverlight 3

29 06 2009

Just a quick post to hopefully save a lot of other people the trouble I just went through.

I had a Silverlight 2 and Web solution in VS2008 and converted it to VS2010 and Silverlight 3.

After this conversion a setting is removed from the properties of the Web project: Silverlight Debugging.

image image

This of course disables all debugging with the projects, and sent me on a wild goose chase to all kinds of interesting articles trying to fix debugging with Silverlight.

And of course, all I had to do was enable Silverlight debugging again on the Web project :) Hopefully this will save you the day I lost trying to solve this problem.

-Robertjan Tuit



Silverlight 2 dynamic assembly loading - Part 2

6 03 2009

In this article:
- Unpacking and loading the assemblies.
- Direct & Deferred Loading
- Download Progress
In part 1 (click to open) :
- Rebuilding the AppManifest.xaml
- (Re)compressing .xap files with 7zip

A little but later then promised, mostly because I kept building more and more functionality. But here is second part of the Dynamic Assembly Loading. Click here for part 1.

In part 1 we rexapped out applications to meet our requirements, now let’s start loading the application, the custom xaml and the referenced assemblies. We start out with the solutions created in part 1, click here to download it.

ReferencedAssemblies.xaml

To reference and handle the data while loading we create a ReferencedAssembly class looking like this:

public enum ReferencedAssemblyStates { None, Loading, Error, Done };

public class ReferencedAssembly

{

    public ReferencedAssemblyStates State;

    public long ByteSize;

    public long ByteSizeLoaded;

    public double PercentLoaded;

    public string Name;

    public Uri Uri;

    public EventHandler Loaded;

    public Exception Error;

    public AssemblyPart AssemblyPart;

    public LoadingTypes loadingType = LoadingTypes.Direct;

}

Then we load our custom created ReferencedAssemblies.xaml from the Application Resource Stream, in the project this is done when you access the getter of the ReferencedAssemblies property:

var xmlReader = XmlReader.Create(

    Application.GetResourceStream(

        new Uri("ReferencedAssemblies.xaml", UriKind.Relative)

        ).Stream

    );

And use the XamlReader to put the data in our class:

while (xmlReader.ReadToFollowing("ReferencedAssembly"))

{

    // Read Name & Source and Size

    xmlReader.MoveToAttribute("Name");

    var name = xmlReader.Value;

    xmlReader.MoveToAttribute("Source");

    var uri = new Uri(Application.Current.AbsolutePath() + xmlReader.Value, UriKind.Absolute);

    xmlReader.MoveToAttribute("ByteSize");

    var byteSize = long.Parse(xmlReader.Value);

 

    // Create a new ReferencedAssembly class and set values

    var referencedAssembly = new ReferencedAssembly()

        {

            Name = name,

            Uri = uri,

            ByteSize = byteSize

        };

 

    // Add it to the Dictionary

    Current._ReferencedAssemblies.Add(name, referencedAssembly);

}

 

So now we have a list of assemblies, their xap file and their size. (This is a new addition to the ReXapper not done in Part 1). With this list we can load up all our referenced assemblies.

Direct & Deferred Loading

As you might have notice by looking at the code above, I added 2 kind of loading methods: Direct and Deferred.

I added these 2 types to make it possible to load assemblies on demand. This can be very useful when you are building bigger applications.

App.Xaml.cs

Now lets look at what we have to do in our applications. All other code we did so far is a one of, meaning we can just reference it next time. This piece of code needs to be added to every application we wish to use this type of assembly loading.

By default your app.xaml.cs contains the following to initiate your Page:

private void Application_Startup(object sender, StartupEventArgs e)

{

    this.RootVisual = new Page();

}

This should be changed to:

private void Application_Startup(object sender, StartupEventArgs e)

{

    AppAssemblyLoader.Current.LoadReferencedAssemblies(

        new string[] { "System.Xml.Linq" },

        () => this.RootVisual = new Page()

    );

}

What we are doing here is loading the referenced assembly System.Xml.Linq and when it is done call the Page creation method.

Because we also have a MyLibrary.dll in our application this will also be loaded, but this happens in the background. I have added a couple of Events to attach to to monitor what is happening:

public static LoadingDoneEventHandler LoadingDone;

public static LoadingDoneEventHandler LoadingStarted;

public static DownloadProgressChangedEventHandler DownloadProgressChanged;

public static AssemblyDownloadProgressEventHandler AssemblyDownloadProgressChanged;

public static AssemblyStateChangedEventHandler AssemblyStateChanged;

 

If you want to application to start only after every assembly is loaded you just call it like this:

AppAssemblyLoader.Current.LoadReferencedAssemblies(

    () => this.RootVisual = new Page()

);

Loading the Assembly

Loading an assembly is pretty straightforward. First we use a WebClient to get a stream to .xap file.

var wc = new WebClient();

wc.OpenReadCompleted += (sender, e) =>

    {

        LoadAssemblyFromWebResponse(referencedAssembly, e);

    };

 

wc.OpenReadAsync(referencedAssembly.Uri);

Then we take the Result stream and use it load create an AssemblyPart:

private static void LoadAssemblyFromWebResponse(ReferencedAssembly referencedAssembly, OpenReadCompletedEventArgs e)

{

    // Load .xap as StreamResource

    var xap = new StreamResourceInfo(e.Result, null);

    // Get the assembly (.dll) from the .xap StreamResource

    var assemblySri = Application.GetResourceStream(xap, new Uri(referencedAssembly.Name + ".dll", UriKind.Relative));

 

    // Load the stream into a new AssemblyPart

    var assemblyPart = referencedAssembly.AssemblyPart = new AssemblyPart();

    assemblyPart.Load(assemblySri.Stream);

}

 

Luckily Silverlight takes care of everything else. The assembly is added to the Assembly Pool, and when another part of you application requests the assembly it is automatically referenced.

Managed Download Progress

You have the possibility to get the download progress for the total and for every separate assembly. If you would load everything deferred, this would enable you to make a Managed Code Loader.

WrapUp

The end project contains a lot more  code which I won’t talk about here, but if you download the source code you can check it out yourself.

It ended up being a lot more that just the Loader I was planning it to become. But the things that were added, make it very useful for a few other scenario’s as well:

  • Easily separate a Silverlight application into different xap files, making it a good base for bigger applications in SL.
  • Create a managed loader, which is currently impossible with Silverlight.

I hope to have time in the next weeks, to build some demo applications to show off the possibilities.

You can download the finished source code here:
Robertjan Tuit - DynamicAssemblyLoading - Part 2 - Source Code.zip

Of course if you have any questions, ideas or remarks please let me know!

Stay in the Light!

Robertjan Tuit

Technorati:



Silverlight 2 Image Tiling

28 01 2009

Silverlight’s big brother WPF has a lot of functionality that you would like to have in Silverlight 2. One of these features is a Tiling Image Brush.

As many of you have probably already found out, it is impossible to create your own brush in Silverlight, because of internal and sealed base classes.

A couple of months a go I was working on a project which required images being tiled across the screen, so I decided to create a tiling control, which takes an image and puts as many as fit the screen into a wrap panel (from the silverlight control toolkit).

Since then I have seen a lot of people asking for this functionality, I though I would share this very simple (user) control. Below is the code, let me know if you have any questions or remarks.

There are still a few improvements you could make to this code. For example determining the width and height of the image automatically. But to keep it simple I left that code out.

The image used in this example is a random one I took from Google image search:

When finished it should look like something like this:

image

In the application: (local namespace should be added to the top of the xaml)

<local:ImageRepeater

   ImageSource="http://ecx.images-amazon.com/images/I/51sghWRypwL._SL75_SS50_.jpg"

   ImageWidth="50"

   ImageHeight="50"/>

UserControl: ImageRepeater.xaml

<UserControl x:Class="ImageRepeater"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"

    >

    <Grid x:Name="LayoutRoot" Background="White">

        <controls:WrapPanel x:Name="xRepeaterPanel"/>

    </Grid>

</UserControl>

UserControl: ImageRepeater.xaml.cs

public partial class ImageRepeater : UserControl

{

    public ImageRepeater()

    {

        InitializeComponent();

    }

 

    // The brush to paint on the rectangle

    private ImageBrush _BrushToTile = new ImageBrush();

    public ImageSource ImageSource

    {

        get { return _BrushToTile.ImageSource; }

        set {

            _BrushToTile.ImageSource = value;

        }

    }

 

    private double _ImageWidth;

    public double ImageWidth

    {

        get { return _ImageWidth; }

        set { _ImageWidth = value; }

    }

 

 

    private double _ImageHeight;

 

    public double ImageHeight

    {

        get { return _ImageHeight; }

        set { _ImageHeight = value; }

    }

 

    protected override Size ArrangeOverride(Size finalSize)

    {

        // To make sure that we fill the screen horizontally and vertically, we create negative margins

        Margin = new Thickness(0, 0, -ImageHeight, -ImageWidth);

 

        // Determine how many rectangles we need to add to fill the available screen width

        var itemCount = (int)Math.Ceiling(finalSize.Width / ImageWidth);

        itemCount = itemCount * (int)Math.Ceiling(finalSize.Height / ImageHeight);

 

        // It is done incrementally, so when the control is resized, we add more to it so fill the area again.

        var diff = itemCount - xRepeaterPanel.Children.Count();

        for (int i = 0; i < diff; i++)

        {

            // Create and add every rectangle

            var rect = new Rectangle();

            rect.Width = ImageWidth;

            rect.Height = ImageHeight;

            rect.Fill = _BrushToTile;

            xRepeaterPanel.Children.Add(rect);

        }

        return base.ArrangeOverride(finalSize);

    }

}

Stay in the light!

Robertjan Tuit



Storyboard Animation for the ScrollViewer: AnimationHelperControl

13 09 2008

The Scrollviewer is a great panel and very easy to use. But it has a drawback. It uses a method to set the scrollbars and content position. This means you cannot use a storyboard animation to animate this.

I had 2 projects that required this functionality, and really needed a better solutions then using a DispatcherTimer to create the animation manually. And after a few good nights of sleep (I always have the best ideas after a good nights rest) I came up with what I think is a nice solution.

The idea is that you need a DependencyProperty to animate a Scrollviewer. But since the ScrollViewer is a Sealed control you can’t inherit from it, I needed to wrap a new control around it to make it reusable.

So I took a different approach, I created a AnimationHelperControl, which does not have any visuals but only has a Dependency property and an event that is fired when this property has been changed. In this way you can use it to control anything you want.

Below the source code for the control:

public class AnimationHelperControl : Control

{

    public AnimationHelperControl()

    {

        Visibility = Visibility.Collapsed;

    }

 

    public double DoubleValue

 

    {

        get { return (double)GetValue(DoubleValueProperty); }

        set

        {

            SetValue(DoubleValueProperty, value);

            DoubleValueChanged(value);

        }

    }

 

    public static readonly DependencyProperty DoubleValueProperty =

        DependencyProperty.Register("DoubleValue", typeof(double), typeof(AnimationHelperControl), new PropertyMetadata((sender, e) =>

            {

                if (e.OldValue != e.NewValue)

                {

                    var ah = (AnimationHelperControl)sender;

                    ah.DoubleValue = (double)e.NewValue;

                }

            }));

 

    public event EventHandler<DoubleEventArgs> DoubleValueChange;

    private void DoubleValueChanged(double newValue)

    {

        if (DoubleValueChange != null)

            DoubleValueChange(this, new DoubleEventArgs() { Value = newValue });

    }

}

 

public class DoubleEventArgs : EventArgs

{

 

    private double _Value;

    public double Value

    {

        get { return _Value; }

        set { _Value = value; }

    }

 

}

 

Then you add the control, the scrollviewer and 2 buttons to the page.xaml:

<Grid x:Name="LayoutRoot" Background="White">

    <Grid.RowDefinitions>

        <RowDefinition Height="0.853*"/>

        <RowDefinition Height="0.147*"/>

    </Grid.RowDefinitions>

    <local:AnimationHelperControl x:Name="xScrollViewerHorizontalOffset" Grid.RowSpan="2"/>

    <Grid Grid.RowSpan="1" Margin="0,0,0,0">

        <ScrollViewer x:Name="xScrollViewer" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden">

            <ScrollViewer.Content>

                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">

                    <Rectangle Fill="Red" Width="300" Height="200" Margin="20,0,20,0"/>

                    <Rectangle Fill="Blue" Width="300" Height="200" Margin="0,0,20,0"/>

                    <Rectangle Fill="Green" Width="300" Height="200" Margin="0,0,20,0"/>

                    <Rectangle Fill="Purple" Width="300" Height="200" Margin="0,0,20,0"/>

                    <Rectangle Fill="Black" Width="300" Height="200" Margin="0,0,20,0"/>

                    <Rectangle Fill="Yellow" Width="300" Height="200" Margin="0,0,20,0"/>

                    <Rectangle Fill="Cyan" Width="300" Height="200" Margin="0,0,20,0"/>

                </StackPanel>

            </ScrollViewer.Content>

        </ScrollViewer>

    </Grid>

    <Button HorizontalAlignment="Left" Margin="8,8,0,8" VerticalAlignment="Stretch" Width="65" Content="Left" Grid.Row="1" x:Name="xButtonLeft"/>

    <Button HorizontalAlignment="Right" Margin="0,8,8,8" VerticalAlignment="Stretch" Width="66" Content="Right" Grid.Row="1" x:Name="xButtonRight"/>

</Grid>

 

Remember to the "local" namespace to the xaml.

Then we add the event handler to the page.xaml.cs:

xScrollViewerHorizontalOffset.DoubleValueChange += (sender, e) =>

{

    this.xScrollViewer.ScrollToHorizontalOffset(e.Value);

};

Now you could create storyboard animation on the DoubleValue property of the xScrollViewerHorizontalOffset control, but to keep it short and simple I’ll use the AnimateTo extension method of one of my previous posts, which accomplishes the same, only it does it with code. Let’s use the 2 buttons to animate scrolling left and right.

xButtonRight.Click += (sender, e) =>

{

    xScrollViewerHorizontalOffset.AnimateDoubleTo(200, "(StoryboardAnimationHelper.DoubleValue)", xScrollViewer.HorizontalOffset + 320);

};

xButtonLeft.Click += (sender, e) =>

{

    xScrollViewerHorizontalOffset.AnimateDoubleTo(200, "(StoryboardAnimationHelper.DoubleValue)", xScrollViewer.HorizontalOffset - 320);

};

 

That should do the trick I’ll try to upload a Demo Project later this weekend.

Stay in the light!

Robertjan Tuit



Silverlight 2 UserControl base class in Blend

24 08 2008

I have been doing a lot of Silverlight2 development the last few months. And one of the things you will start to do pretty quickly is make base classes for your pages. You can do this by creating a base class which inherits a UserControl, inherit from it in your Page.xaml.cs and then changing the root of your XAML from <UserControl to <local:MyPageBase. Like this:

Page.xaml.cs:

public partial class Page : PageBase

{

    public Page()

    {

        InitializeComponent();

    }

}

PageBase.cs: (In another "common" project)

public class PageBase : UserControl

{

 

}

Page.xaml:

<common:PageBase x:Class="Silverlight2UserControlWithBaseClassBlendFix.Client.Page"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:common="clr-namespace:Silverlight2UserControlWithBaseClassBlendFix.Client.Common;assembly=Silverlight2UserControlWithBaseClassBlendFix.Client.Common">

    <Grid x:Name="LayoutRoot"/>

</common:PageBase>

 

The blend catch

However there is one catch to this whole exercise: you lose editing capabilities in blend!

This is of course something that will be fixed in the final version, but for now it is a breaking bug. Which "almost" makes you run away from base classes.

I have seen a lot of people complain about the bug, but I have not found anyone giving at least a temporary solution until it is fixed.

Not so much a solution

First I thought about a very simple solution. To use the page only as a container and put all the content in another user control. But this has the disadvantage that you loose all the functionality from the base class again, and that was was we were aiming for. So it solves one problem and we get back the one we had.

At least it works

First couple of weeks, I just stuck with it, no base classes. I used extension methods and other "not so much solutions" to work around the problem. But today I finally had enough and started on a bandage that should hold at least until the final versions are released.

The idea is to use a text SearchAndReplace tool in the pre build event, to replace <UserControl to <local:BasePage and back again in the post build.

I had already written a simple command line SeardAndReplace tool before so this was ideal for the job. It does gives you random build errors, but this is simply resolved by building your project again.

Pre-Build: (replace <UserControl to <common:PageBase )

$(SolutionDir)SearchAndReplace $(ProjectDir)Page.xaml /search="<UserControl " /replace="<common:PageBase "

$(SolutionDir)SearchAndReplace $(ProjectDir)Page.xaml /search="</UserControl>" /replace="</common:PageBase>"

 

Post-Build: (replace back to <UserControl)

$(SolutionDir)SearchAndReplace $(ProjectDir)Page.xaml /search="<common:PageBase " /replace="<UserControl "

$(SolutionDir)SearchAndReplace $(ProjectDir)Page.xaml /search="</common:PageBase>" /replace="</UserControl>"

 

Click here to download the whole "proof of concept project".

Stay in the light!

Robertjan Tuit



AnimateTo extension methods in Silverlight 2

13 08 2008

So, back from a fair long time of blog silence. Had some pretty busy weeks, with some very interesting projects. Among them I had the honor of working on the Olympics website (http://os2008.nos.nl) for the NOS (Dutch Broadcasting Company). I just wanted to mention it because I think the team over there did an excellent job, you can check it out yourself.

Since I have done lots of work with Silverlight 2 the past months, I wish to share with you some of the findings, and handy pieces of code I have gathered. I’ll start with the AnimateTo Extension methods I wrote for Silverlight UIElements and Transforms.

The reason I started writing these extensions was the frequency with which I needed to do simple animations, especially with transforms the result is fantastic.

The extensions methods can be used as follows:

// ————————————

// These examples create the elements only for

// demo purposes, normally you would use elements from XAML.

// ————————————

 

// This will fade out the rectangle, scale it to very small,

// and at the end set visibility to collapsed

Rectangle rectangle = new Rectangle();

rectangle.RenderTransform = new ScaleTransform();

rectangle.RenderTransformOrigin = new Point(0.5, 0.5);

rectangle.AnimateDoubleTo(300, "(UIElement.Opacity)", 0, null);

((ScaleTransform)rectangle.RenderTransform).AnimateTo(300, 0.1, 0.1, (sender, e) =>{

    rectangle.Visibility = Visibility.Collapsed;

});

 

// This example will rotate an element for 30 degrees

// in 200 milliseconds and then rotate -30 degrees the

// other way

var rotateTransform = new RotateTransform();

rotateTransform.AnimateTo(200, 30, true, (sender,e) =>{

    rotateTransform.AnimateTo(200, -30, true, null);

    });

}

 

Below the AnimateTo Extention method for UIElements:

public static class UIElementExtentions

{

    public static void AnimateDoubleTo(this UIElement element, int miliseconds, string propertyPath, double value, EventHandler completed)

    {

        // Create a new storyboard

        var sb = new Storyboard();

 

        // Create a double animation

        var da = new DoubleAnimation();

        da.Duration = new TimeSpan(0, 0, 0, 0, miliseconds);

        // Set target property and target

        Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));

        Storyboard.SetTarget(da, element);

        da.To = value;

        // Add the doubleanimation to the storyboard

        sb.Children.Add(da);

 

        // Add the storyboard to the Rootvisual of the application

        ((FrameworkElement)Application.Current.RootVisual).Resources.Add(Guid.NewGuid().ToString(), sb);

 

        // Begin the animation

        sb.Begin();

 

        if (completed != null)

            sb.Completed += completed;

    }

}

 

And here is the rest, without comments, because they are basicly the samen as the previous code:

public static class ScaleTransformExtentions

{

    public static void AnimateTo(this ScaleTransform scaleTransform, int milliseconds, double scaleX, double scaleY, EventHandler completed)

    {

        var sb = new Storyboard();

 

        var daX = new DoubleAnimation();

        daX.Duration = new TimeSpan(0, 0, 0, 0, milliseconds);

        Storyboard.SetTargetProperty(daX, new PropertyPath("(ScaleTransform.ScaleX)"));

        Storyboard.SetTarget(daX, scaleTransform);

        daX.To = scaleX;

 

        var daY = new DoubleAnimation();

        daY.Duration = new TimeSpan(0, 0, 0, 0, milliseconds);

        Storyboard.SetTargetProperty(daY, new PropertyPath("(ScaleTransform.ScaleY)"));

        Storyboard.SetTarget(daY, scaleTransform);

        daY.To = scaleY;

 

        sb.Children.Add(daX);

        sb.Children.Add(daY);

 

        ((FrameworkElement)Application.Current.RootVisual).Resources.Add(Guid.NewGuid().ToString(), sb);

 

        sb.Begin();

 

        if (completed != null)

            sb.Completed += completed;

    }

}

 

public static class TranslateTransformExtentions

{

    public static void AnimateTo(this TranslateTransform translateTransform, int milliseconds, double x, double y, EventHandler completed)

    {

        var sb = new Storyboard();

 

        var daX = new DoubleAnimation();

        daX.Duration = new TimeSpan(0, 0, 0, 0, milliseconds);

        Storyboard.SetTargetProperty(daX, new PropertyPath("(TranslateTransform.X)"));

        Storyboard.SetTarget(daX, translateTransform);

        daX.To = x;

 

        var daY = new DoubleAnimation();

        daY.Duration = new TimeSpan(0, 0, 0, 0, milliseconds);

        Storyboard.SetTargetProperty(daY, new PropertyPath("(TranslateTransform.Y)"));

        Storyboard.SetTarget(daY, translateTransform);

        daY.To = y;

 

        sb.Children.Add(daX);

        sb.Children.Add(daY);

 

        ((FrameworkElement)Application.Current.RootVisual).Resources.Add(Guid.NewGuid().ToString(), sb);

 

        sb.Begin();

 

        if (completed != null)

            sb.Completed += completed;

    }

}

 

public static class RotateTransformExtentions

{

    public static void AnimateTo(this RotateTransform rotateTransform, int milliseconds, double angle, bool relative, EventHandler completed)

    {

        var sb = new Storyboard();

 

        if (relative)

            angle = rotateTransform.Angle + angle;

 

        var da = new DoubleAnimation();

        da.Duration = new TimeSpan(0, 0, 0, 0, milliseconds);

 

        Storyboard.SetTargetProperty(da, new PropertyPath("(RotateTransform.Angle)"));

        Storyboard.SetTarget(da, rotateTransform);

        da.To = angle;

 

        sb.Children.Add(da);

 

        ((FrameworkElement)Application.Current.RootVisual).Resources.Add(Guid.NewGuid().ToString(), sb);

 

        sb.Begin();

 

        if (completed != null)

            sb.Completed += completed;

    }

}

 

Have fun with it!

Robertjan Tuit



Silverlight 2 shape tween

20 06 2008

One of the things you miss immediately when you start to work with SL2 is the shape tween, it only lets you animate properties.

One of the projects I’m doing really needed something like a shape tween, and after some research, I found a way to reach almost the same result.

It works by animating a line in a path. Normally when you create a path it is created like this:

<Path StrokeThickness="1.000000" Fill="#ffffffff"

    Stroke="#ff221e1f" StrokeMiterLimit="1.000000"

    Data="F1 M 98.704102,0.049805 L 98.704102,141.497559 L 0.019531,141.497559 C 0.019531,141.497559 70.579102,138.700684 85.875000,128.010254 L 98.704102,0.049805 98.704102,0.049805 98.704102,0.049805 Z"/>

 

This will produce:

image

Now it is very hard to animate any of the lines and points, because you need a name to animate. But the very exact same Path can also be written like:

<Path StrokeThickness="1" Stroke="#ff666666"

    StrokeMiterLimit="1.000000" Fill="#ffffffff">

    <Path.Data>

        <PathGeometry>

            <PathGeometry.Figures>

                <PathFigureCollection>

                    <PathFigure IsClosed="True" StartPoint="98.704102,0.049805">

                        <LineSegment Point="98.704102,141.497559" />

                        <LineSegment Point="0.019531,141.497559" />

                        <BezierSegment x:Name="CurvedLine" Point1="0.019531,141.497559" Point2="70.579102,138.700684" Point3="85.875000,128.010254"/>

                    </PathFigure>

                </PathFigureCollection>

            </PathGeometry.Figures>

        </PathGeometry>

    </Path.Data>

</Path>

 

As you see in the code one of the lines, the BezierSegment, has a name: CurvedLine.

So now when we create a storyboard, we can animate this BezierSegment like this:

<UserControl.Resources>

    <Storyboard x:Name="Animation">

        <PointAnimation Storyboard.TargetName="CurvedLine" BeginTime="00:00:00" Duration="00:00:00.200" 

                       Storyboard.TargetProperty="Point1"

                       To="0,141.497559"/>

        <PointAnimation Storyboard.TargetName="CurvedLine" BeginTime="00:00:00" Duration="00:00:00.200"

                       Storyboard.TargetProperty="Point2"

                       To="0,141.497559"/>

        <PointAnimation Storyboard.TargetName="CurvedLine" BeginTime="00:00:00" Duration="00:00:00.200"

                       Storyboard.TargetProperty="Point3"

                       To="98.704102,141.497559"/>

    </StoryBoard>

</UserControl.Resources>

 

Which will make it animate like this:

image image image image

I know, this example is not very exciting, but it does show how it works. You can make some pretty complex and exiting animations with it, if you just animate some more of the line segments.

If anyone does some more complex animation with it, let me know and I’ll put it up here.

Stay in the light!

Robertjan Tuit



Updating the UI Thread in SL2

18 04 2008

Just as in WPF, Silverlight only allows you to update the UI from a UI Thread. This is done trough the Dispatcher object. There are a lot of samples floating around but none did exactly what I was looking for.

Let’s say we have the following code:

// Create a new thread and Start it

new Thread(new ThreadStart(() =>

{

    // Do some NON UI Asynchronous work here       

    PopulateSomeDataFromOtherData();

 

    // THIS WILL NOT WORK

    UpdateUI();

}

The code starts a new thread, then does some work on the thread. And when it’s finishes it tries to update the UI, this will fail!

To make this work, we need to tell the UI thread to execute this code trough the Dispatcher object:

First you define a custom (empty) delegate:

public delegate void UpdateUIDelegate();

And then you change the erroneous previous code to:

// Create a new thread and Start it

new Thread(new ThreadStart(() =>

{

    // Do some NON UI Asynchronous work here       

    PopulateSomeDataFromOtherData();

 

    // Define an action to execute in the UI Thread

    UpdateUIDelegate action = new UpdateUIDelegate(() =>

    {

        //Update the UI

        UpdateUI();

    });

 

    // Tell the UI Thread (Dispatcher) to execute the action

    Application.Current.RootVisual.Dispatcher.BeginInvoke(action);

})

).Start();

The changes use the Dispatcher from the RootVisual to execute the action delegate. This will nicely update the UI from a non UI-Thread.

NOTE: The Dispatcher object used in this sample is from the RootVisual. But every Control has it’s own Dispatcher object. Use it when it’s available, it will save you a lot of debugging.

For some more info on Silverlight and Threading check:

Stay in the light!

Robertjan Tuit

 



Silverlight 2 - MSN Video ListBox

9 04 2008

NOTE TO RSS READERS: Click this link to see the images and source code correctly.

This is just one of those days: I have been redoing my Silverlight project for 5 times now and tried about 10 ways to do the same thing. All resulting in nothing. Not the result I wanted and not an error, If I had any hair I would have pulled it out. But I guess the choice to go work with a beta product was my own.

Onto the real reason of this blog post: to prevent you from having the same terrible day. I was able to reproduce the error in a test project which will also demonstrate some nice features of SL that do work as expected :).

The end result

Usually I don’t begin with the end, but I always like to visualize what I’m going to make:

image

and we will be using a the Random MSN Video service as our data source:
http://catalog.video.msn.com/randomVideo.aspx?mk=us&vs=0&df=99&c=10

For the lazy (most of us developers/designers), here is the source code we are going to create:

New Project

We start out by creating a new Silverlight Project in this demo I assumed it to be named SilverlightApplication2:

image

And in the next screen select "Add a new Web to the solution for hosting the control" and select "Web Application Project".

image 

After the 2 projects are created, set SilverlightApplication2TestPage.aspx as your start page.

XML to Objects for Databinding

Silverlight does not yet support the direct XML databinding available in WPF. So we will have to do our binding by hand. First let’s create the data model we will be using: Add a new class "VideoFeedItem.cs" to the SilverlightApplication2 project with the following code:

public class VideoFeedItem

{

    public string Title { get; set; }

    public string Source { get; set; }

    public string PublishDate { get; set; }

    public string Description { get; set; }

    public string ImageUrl { get; set; }

    public string VideoUrl { get; set; }

    public string ViewCount { get; set; }

}

Then add a new class "VideoDS.cs" to the SilverlightApplication2 project, I have added code comments to explain what it does:

/// <summary>

/// This class will load from a webservice and

/// provide a datasource which we can bind to from XAML.

/// </summary>

public class VideoDS : INotifyPropertyChanged

{

    // Webservice URI & Namespace

    private const string _randomVideo = "http://catalog.video.msn.com/randomVideo.aspx?mk=us&vs=0&df=99&c=10";

    readonly XNamespace _NS = "urn:schemas-microsoft-com:msnvideo:catalog";

 

    // Bindable Collection

    public ObservableCollection<VideoFeedItem> RandomVideos { get; set; }

 

    public VideoDS()

    {

        // Only load the video feed when we are in the browser (Not in blend/VS)

        if (HtmlPage.IsEnabled)

            LoadVideoFeed();

    }

 

    /// <summary>

    /// Load the video feed and put it into our collection

    /// </summary>

    private void LoadVideoFeed()

    {

        // Use a normal webclient to fetch the feed

        var wc = new WebClient();

        // New lambda style delegate declaration

        wc.DownloadStringCompleted += (sender, e) =>

        {

            var doc = XDocument.Parse(e.Result);

            // Linq Select from the XML Tree and create a new VideoFeedItem for each selected.

            var videos =

              from video in doc.Descendants(_NS + "video")

              select new VideoFeedItem()

              {

                Title = (string)video.Element(_NS+"title"),

                Source = (string)(video.Element(_NS + "source").Attribute("friendlyName")),

                PublishDate = DateTime.Parse((string)video.Element(_NS + "startDate")).ToShortDateString(),

                Description = (string)video.Element(_NS + "description"),

                ImageUrl = GetUriAsset(video.Element(_NS + "files"), "file", "2007"),

                VideoUrl = GetUriAsset(video.Element(_NS + "videoFiles"), "videoFile", "1002"),

                ViewCount = (string)video.Element(_NS + "usage").Element(_NS + "usageItem").Attribute("totalCount"),

              };

 

            // Add the results to the Collection

            RandomVideos = new ObservableCollection<VideoFeedItem>();

            foreach (var video in videos)

                RandomVideos.Add(video);

 

            // Notify all listeners that the data has changed

            PropertyChanged(this, new PropertyChangedEventArgs("RandomVideos"));

        };

        wc.DownloadStringAsync(new Uri(_randomVideo));

    }

 

    #region LINQ Uri Helper

    /// <summary>

    /// Helps with the finding of some data in the complicated XML Structure

    /// </summary>

    private string GetUriAsset(XContainer element, string nodeName, string formatCode)

    {

        var uris = from file in element.Descendants(_NS + nodeName)

                                   where (string)file.Attribute("formatCode") == formatCode

                                   select (string)file.Element(_NS + "uri");

        return ((uris.Count<string>() > 0) ? uris.First<string>() : string.Empty);

    }

    #endregion

 

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

}

Binding

Now that we have setup the classes necessary for the data loading and binding, let’s add the ListBox to our page.xaml. Page.xaml should look like this:

<UserControl x:Class="SilverlightApplication2.Page"

   xmlns="http://schemas.microsoft.com/client/2007"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"             

   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"             

   xmlns:SilverlightApplication2="clr-namespace:SilverlightApplication2"

   mc:Ignorable="d"

   Width="600" Height="200">

    <UserControl.Resources>

        <SilverlightApplication2:VideoDS x:Key="VideoDS" d:IsDataSource="True"/>

    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">

        <ListBox ItemsSource="{Binding RandomVideos, 
                                Source
={StaticResource VideoDS}}"/>

    </Grid>

</UserControl>

You can now run the application, it should look like this:

image

Template

As you see here, the listbox has found our collection, but has no clue whatsoever what to do with it. To tell it how it should display our data, we add ListBox.ItemTemplate and we change the orientation using the ListBox.ItemsPanel property:

<ListBox ItemsSource="{Binding RandomVideos, Source={StaticResource VideoDS}}">

    <ListBox.ItemTemplate>

        <DataTemplate>

            <TextBlock Text="{Binding Title}"/> 

        </DataTemplate>

    </ListBox.ItemTemplate>

    <ListBox.ItemsPanel>

        <ItemsPanelTemplate>

            <StackPanel Orientation="Horizontal"/>

        </ItemsPanelTemplate>

    </ListBox.ItemsPanel>

</ListBox>

This should result in the following (with other data ofcourse since the video’s are random):

image

I’m always a big fan of the separation of design and behavior, so let’s move the properties to a style. First remove the properties and add a Style to the ListBox:

<ListBox Style="{StaticResource ListBoxStyle}" ItemsSource="{Binding RandomVideos, Source={StaticResource VideoDS}}"/>

Style

And then add a Style to the UserControl:

<UserControl.Resources>

    <SilverlightApplication2:VideoDS x:Key="VideoDS" d:IsDataSource="True"/>

    <Style x:Key="ListBoxStyle" TargetType="ListBox">

        <Setter Property="ItemsPanel">

            <Setter.Value>

                <ItemsPanelTemplate>

                    <StackPanel Orientation="Horizontal"/>

                </ItemsPanelTemplate>

            </Setter.Value>

        </Setter>

        <Setter Property="ItemTemplate">

            <Setter.Value>

                <DataTemplate>

                    <TextBlock Text="{Binding Title}" />

                </DataTemplate>

            </Setter.Value>

        </Setter>

    </Style>

</UserControl.Resources>

The above style does exactly the same thing as out previous properties does, so no changes in result here. As a design choice you could also add the Style to the Resources in the App.xaml.

Image Binding

Next we’ll add an Image to the mix. We’ll also add a StackPanel and some Margin to make it look a little bit better.

<Setter Property="ItemTemplate">

    <Setter.Value>

        <DataTemplate>

            <StackPanel Width="200">

                <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" Margin="5,5,5,0"/>

                <Image Height="120" Stretch="Uniform" Source="{Binding ImageUrl}" HorizontalAlignment="Center" Margin="10,10,10,10"/>

            </StackPanel>

        </DataTemplate>

    </Setter.Value>

</Setter>

Build and run the project, it should look something like this:

image

UserControl in a Style

To get a little bit more control over the video, we are going to move the content of the <DataTemplate> to a User Control. Add a new Silverlight UserControl to the project with the name : VideoFeedItemControl.

Move the XAML from the <DataTemplate> to the new usercontrol:

<UserControl x:Class="SilverlightApplication2.VideoFeedItemControl"

   xmlns="http://schemas.microsoft.com/client/2007"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid x:Name="LayoutRoot">

        <StackPanel Width="200">

            <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" Margin="5,5,5,0"/>

            <Image Height="120" Stretch="Uniform" Source="{Binding ImageUrl}" HorizontalAlignment="Center" Margin="10,10,10,10"/>

        </StackPanel>

    </Grid>

</UserControl>

And place the <VideoFeedItemControl> in the Page.xaml <DataTemplate>:

<Setter Property="ItemTemplate">

    <Setter.Value>

        <DataTemplate>

            <SilverlightApplication2:VideoFeedItemControl/>

        </DataTemplate>

    </Setter.Value>

</Setter>

Ready to go, build and run. Let’s see our end result:
image
Hey, wait a minute!? that’s not what we were aiming for. That’s empty!

We did not get any errors, everything seems to be working correctly, what happened here?

The error

This was the point where it got frustrating. It took me more than a day to figure out. Remember the namespace handling in the page.xaml? One of these was a reference to our own project. This was generated automaticaly.

I’m talking about this line:

xmlns:SilverlightApplication2="clr-namespace:SilverlightApplication2"

Nothing wrong with it, right?

If you know what the problem is, the fix is very simple. Add:
assembly=SilverlightApplication2.

It should now be:

clr-namespace:SilverlightApplication2;assembly=SilverlightApplication2

We build and we get:

image

That looks more like it!

So we only needed an assembly reference to the current assembly? Strange behavior.

I’m still looking for the reason, but at least it is fixed. The strangest thing is that if you would move the UserControl back to the normal page. The UserControl works normally without the need of the assemblyname= in the namespace declaration.

So it seems this only happens when a UserControl is used within a template or style declaration.

Would be nice to have

Ofcourse it is much more work to have running video’s in the listbox. And there are at least 20 more things I can think of that could nicely extend the sample.

Maybe next time :)

Stay in the light!

Robertjan Tuit

Technorati tags: ,