The use of vector graphics is really powerful because it connect the low weight for big images and free scalability to every size. Nevertheless sometimes you need to return to raster images because they grant a more contro to the rendering pixel by pixel and for photographic rendering are better. With Silverlight 2.0 the only way to use raster images is to download the content from the server, and embed it into an element <image> on the UserControl.

With Silverlight 3.0 a new way to manage raster images has been added. Using a WriteableBitmap it is possible to generate images on the fly. This tecnique is really powerful and someway unite the best of both vector graphic and raster images. Using this class you may render big images withoud downloading them from the source server.

Using WriteableBitmap: Back to Mandelbrot

To give an example of the great power of image generation let me explain how to create a simple silverlight application to generate Mandelbrot fractals on the fly. The core of the application is a short algorithm, that render a Mandelbrot set into a bitmap.

A is one of many set of fractals, probably the most famous, explored for the first by . The Fractal geometry is out of the scope of this article so let me skip a deep explanation of the fractals theory. If you need you may find many informations .

 

 

 

The only thing we need to know here is that a Mandelbrot Fractals may be zoomed and panned like a map and this generate a large set of wonderful image. You may see an example in the side image that show also the interface of the application I'm going to explain.

The first problem we have to address is the time needed to generate the image. This may be a time consuming activity but also may consume a lot of resources. In a silverlight application this may become to a freeze of the browser during the rendering. To avoid this problem we have to use a background thread that calculate every single pixel and write it to the raster image. Another little problem is that the WriteableImage is a UI element and it is not possible to write inside of it in a separated thread. So we have to calculate all the pixels in a System.Int32 array and at the end of the elaboration we switch to the UI thread and copy the array to the WritableBitmap. Here is the code to render the image:

   1: // Declare a buffer to generate the image
   2: int[] bmap = new int[this.Width * this.Height];
   3:  
   4: // Calculate here the Mandelbrot image into the int [].
   5: // ...
   6:  
   7: // Mashal to the UI Thread before render
   8: this.UIElement.Dispatcher.BeginInvoke(
   9:     new Action<int[]>(
  10:         data =>
  11:         {
  12:             // create the bitmap
  13:             WriteableBitmap output = new WriteableBitmap(this.Width, this.Height, PixelFormats.Pbgra32);
  14:  
  15:             try
  16:             {
  17:                 output.Lock();
  18:                 
  19:                 for (int i = 0; i < data.Length; i++) 
  20:                     output[i] = data[i];
  21:  
  22:                 // do something with generated image
  23:             }
  24:             finally
  25:             {
  26:                 output.Invalidate();
  27:                 output.Unlock();
  28:             }
  29:  
  30:         }), bmap);

From this short snippet we may learn a bunch or things: first of all writing a raster image has to be made pixel-by-pixel like we have to fill and array. The data is stored from the top line to the last line and from the left to the right. So at the position zero we have the top left pixel, then we continue with the first line and after the end we begin the second line and so on. This is a simple conversion from X and Y using Width of the image as modulus:

int pixelIndex = Y * Width + X;

In the constructor of the WriteableBitmap we may chose the PixelFormat for our pixels. There is only to values possible. Bgr32 specify a RGB color and Pbgra32 specify a ARGB where A stnd for Alpha, the usual color format for Silverlight.

The call to the methods Lock and Unlock are required to access to the pixels. This methods grant the exclusive access to the bitmap and lock the rendering from inside another thread.

The resulting image may be used directly in a ImageSource Property to show the generated image in the interface.

The sample application

The application is sligthly simple. We have an initial rectangle from -2.1 to 1.0 horizontally and from -1.3 to 1.3 vertically. These are the default "coordinates"  that show the full Mandelbrot set as you have probably already saw. We have a method Redraw() that accepts the UI coordinated of the part to render. The initial value is the full size of the canvas but wen we select a part of the image or click one of the panning and zooming buttons the square is recalculated and the part of the set become the full image. In thes way we can zoom to the most little particular of the set and see the fractas in all his beauty.

The generation of the image is done by a special class that incapsulate the algorithm and the thread marshaling. The only thing we get by this class called MandelbrotGenerator is the Completed event containing the generated image.

Here is a full size screenshot of the application:

Full size screenshot of fractal explorer

Some other words about WriteableBitmap

One thing I've missed speaking about this class in that the WritableBitmap can render a part of the VisualTree into an image. This may be useful to create effects like mirroring where all or part of an UIElement is rendered rotated on a bitmap and showed on the bottom of the original element to simulate a reflection effect. Unfortunately the Render() method does not work correctly in this beta. It is affected by the position of the elements in the Visual Tree so you have to surround the elements to render by a Border and sometimes this do not work (or at least I cannot get it working). So I will post a new article when this object will be fully working in the future releases.

Download Code: Elite.Silverlight3.Fractals.zip (867 KB)

Demo Video: WriteableBitmap.wmv (3.5 MB)


Sometimes working with silverlight you need to have a sort of service, always available for global tasks or  working in background notifing the main thread when something happen. I've found this scenario many times during my work, as an example when I needed to poll a service to get periodic updates, or in another case with a caching service. Every time I developed this services using a static class or an instance of a class exposed by the Application, but the main problem is to manage the lifecycle to run or stop the service freeing the allocated resources when needed.

With Silverlight 3.0 this kind of services has been covered by a new tool named Application Services. An application Service is simply a class that implements the IApplicationService interface. This interface require two methods to be implemented by the Application Service class:

   1: public interface IApplicationService
   2: {
   3:     void StartService(ApplicationServiceContext context);
   4:     void StopService();
   5: }

This methods will be called by the Silverlight runtime when the service need to start during Application initialization and a few moments before the runtime terminate when the browser is closing or the user is navigating away. In the StartService() method the Application Service may run some code to begin his work, initializing variables, running threads and so on. On the other side the same service may notify threads to exit when the StopService() method is called by the runtime.

An Application Service class has to be registered in a specific section of the app.xaml. The application markup file is an ideal place to register this kind of classes. It a global location accessible by all the pages and controls, and the service instances may be simply configured with attributes mapping the class properties. Here is a sample of code showing how to register a FeedPollingService available in the sample source code:

 

   1: <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   3:              xmlns:local="clr-namespace:Elite.Silverlight3.AppExtensibility"
   4:              x:Class="Elite.Silverlight3.AppExtensibility.App">
   5:     <Application.Services>
   6:         <local:FeedPollingService x:Name="polling" 
   7:                                 FeedType="RSS20"
   8:                                 FeedUrl="http://blog.boschin.it/rss.aspx" 
   9:                                 Timeout="10000" />
  10:     </Application.Services>
  11: </Application>

First of all the "local" namespace is declared in the Application tag. The application service is registered in the <Application.Services> element. This element needs you specify a unique name for the service instance. This name may be used to reach the instance from every class in the application. The configured FeedPolllingService exposes a "Completed" event we need to handle to get notified of feed download:

   1: public Page()
   2: {
   3:     FeedPollingService service = Application.Current.Services["polling"] as FeedPollingService;
   4:     service.Completed += new EventHandler<CompletedEventArgs>(service_Completed);
   5:     InitializeComponent();
   6: }

 

This happen in the Page constructor. In the service_Compleded event handler we simply handle the incoming feed binding it to the user interface to show the content. My service contains a thread that poll a rss feed every configured timeout so in the completed event I need to marshal the thread context using the Dispatcher object.

   1: #region IApplicationService Members
   2:  
   3: private ManualResetEvent exitEvent = new ManualResetEvent(false);
   4:  
   5: public void StartService(ApplicationServiceContext context)
   6: {
   7:     ThreadStart start = new ThreadStart(Service_Thread);
   8:     Thread thread = new Thread(start);
   9:     thread.Start();
  10: }
  11:  
  12: public void StopService()
  13: {
  14:     exitEvent.Set();
  15: }
  16:  
  17: #endregion

The FeedPollingService implements the IApplicationContext interface. In the StartService method the service start a thread that do the recurrent polling to the feed. In the StopService method instead a ManualResetEvent is triggered to notify the thread procedure to exit gracefully.

The other part of the application is simply the use of syndication to deserialize the downloaded feed and the binding of the feed items to the interface. The attached source code is available to test the Application Services. Hoping you may find it useful to learn about this new feature, I attach also a full size screenshot of the application running

 

Download Code: (2,07 MB)

Demo Video: (676 KB)

 


Many times, when using a Silverlight application, I thinked about how cool would be to detach the application from the browser and use it directly from my desktop. Some Silverlight application are really lightweighted and can easily used on many platforms without to have to install it in the computer; the only thing needed is the Silverlight runtime.

This scenario I've described in a few words may open the way to a new big set of applications. Think as an example at an application that may be used both online and offline checking for network connectivity and synchronizing the modification of the local storage to a global online repository. You may detach multiple instances of the application in many computers, the office workstation, the home PC, your mini notebook and have them synchronized each other when you apply some changes. This may be a real occasionally connected application.

Out of browser experience and Silverlight 3.0

The biggest feature of the new Silverlight 3.0, called Out of browser experience, OOB for the friends, let you start creating application that can easily detached and used from a simple host without the need of a browser instance. The action of detaching the application is really simple, very fast and do not need any kind of installer. You have simply to right click the running application and if it is configured correctly you will find an "Install ... onto this computer..." item. The only question you need to answer is where you want silverlight let create the icon, on the Desktop or in the Start menu (or both) and then your OOB will be started and ready to be used.

The use of this kind of experience need open some problem we will analize in the next paragraphs, as usual using a small project as a guide. I've build a very tiny ToDoList application in Silverlight to let manage some simple todo, with a title a description and an optional due date. The application is ready to detect network changes and to synchronize contents using a WCF service, but this part is not implemented because is out of the scope of this article.

Configuring the Out Of Browser Experience

The first problem we have to address after we have completed our development, is how to configure it to let the user detach the application to be able to run it online and offline without a browser. This is a quite simply task. In the silverlight project you have to find the AppManifest.xml file under the Properties folder and uncomment a special section. Obviously you may also customize some properties:

   1: <Deployment.ApplicationIdentity>
   2:   <ApplicationIdentity ShortName="ToDoList"
   3:                        Title="Silverlight ToDoList">
   4:     <ApplicationIdentity.Blurb>
   5:       Manage offline your tasks
   6:     </ApplicationIdentity.Blurb>
   7:   </ApplicationIdentity>
   8: </Deployment.ApplicationIdentity>

The properties you can customize are the ShortName that will appear on the menu item, the title and the description (blurb) that will be displayed in the installation window. You may also add an optional <ApplicationIdentity.Icons> section. This let you add some icons in png format to be displayed on the desktop or start menu.

The installation window let you decide how to install  the application you are detaching. You may choice to add some icons, one on the destop and one on the Start menu.

This is the only action you need to perform. If you click yes the files required to run the application will be copied into a folder in AppData and the icons wil be created as shortcut to the sllauncher.exe into the Silverlight 3.0 install location.

The folder where the application files will be copied is C:\User\...\AppData\LocalLow\Microsoft\Silverlight\Offline.

After installing the OOB application you may run it again double clicking on the icons. You may also uninstall it simply right clicking on the running instance and selecting "Remove this application..." from the context menu.

The installation process may be triggered programmatically using the Detach() method of the Application class. This the the developer to add a button that indicate to the user that the application may be detached. The Application.RunningOffline property let the developer know if the Application is already detached. Using this property we may show the installation instructions when the application is in the browser and the remote them when installed.

If you imagine to have a "detach" button you can write this code:

   1: this.detach.Visibility = 
   2:     Application.Current.RunningOffline ? Visibility.Collapsed : Visibility.Visible;
   3:  
   4: this.detach.Click += (sender, e) => Application.Current.Detach();

We may get information about the installation (detaching) or disinstallation of the application handling the event ExecutionStateChanged and reading the ExecutionState property in the Application.Current instance. This property let us know that the state of the application is:

  • Detached - The application has successful detached
  • DetachedUpdatesAvailable - see the paragraph (Updating the application)
  • DetachFailed - The detach is failed
  • Detaching - The detach is in progress
  • RunningOnline - The application is into the browser
Store local data: Isolated Storage

Running an Out of browser application imply to have a location where to store some data when the application is Offline. Unfortunately there is no way to access the local filesystem, also if the application is Detached by the user and also there is not any way to consume some kind of database (e.g. a Sql Server Mobile file).

The only place you can use to persist information is the local IsolatedStorage. The example application attached to this article use the IsolatedStorage to persiste a simple XML file containing the Tasks and the actions the user apply to them. This XML is loaded at the application startup and saved to the storage every time the user modify the data. To handle the synchronization of the modification to the online store the XML contains a status and version for each Task. This information may be used by the WCF Service to manage the updating of the online store.

Detecting network changes

After our OOB application is ready and works against the isolated storage we needs to detect when the user go online to trigger the syncronization with the online storage. The sncronization is made of two parts: we need to update the online store with the updates applied locally and the we needs to download locally the updates applied by other instances of the OOB application.

Detecting the network changes is sligthly simple. We may use two classes called NetworkChange and NetworkInterface. The first class expose and event that let us known when the network status has changed from online to offline and viceversa:

   1: NetworkChange.NetworkAddressChanged +=
   2:     new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);
   3:  
   4: // ... omissis ...
   5:  
   6: private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
   7: {    
   8:     if (NetworkInterface.GetIsNetworkAvailable() && this.NeedUpdate)
   9:     {
  10:  
  11:     }
  12: }

The events triggered by the NetworkChange class do not contains any informations about the connection is up or down. This event let us to know only that something has changed. Also we have to take note that the event will be triggered multiple times depending how many adapters are connected to the host computer so we have to handle this event accurately.

To detect if we are are running online or offline we may use the GetIsNetworkAvailable() method. This method returns true if this ia any connected network else it returns false. In the application we check also the NeedUpdate property to avoid that the update process will be triggered too many times if the network connect or disconnect many times due to network instability. In this application NeedUpdate returns true only if at least 1 hour has passed after the last successful update.

Updating the application

When an application is running online every time we connect to the host site we get the latest version of the application. The silverlight plugin handles the updates of the xap downloading the file and discarting the cached version. When an application has ben taken out of browser we needs to have this check to run sometimes because we want our applicatio remain up to date.

The Silverlight 3.0 OOB application run this check every time the application starts on the host computer. If the network is available and an update has been detected the new xap will be downloaded and then the running application will be notified of the available update. It is in charge of the developer to decide to show to the user that the application has been updated. However when the application is closed and restarted the update will be applied. Here is the code to get the notification about updates:

   1: Application.Current.ExecutionStateChanged += 
   2:     new EventHandler(Current_ExecutionStateChanged);
   3:  
   4: // ... omissis ...
   5:  
   6: void Current_ExecutionStateChanged(object sender, EventArgs e)
   7: {
   8:     if (Application.Current.ExecutionState == ExecutionStates.DetachedUpdatesAvailable)
   9:         MessageBox.Show("Updates have been downloaded. Please restart the application to apply this updates.");
  10: }

There is no way to break the update process. When the application detect an update it try to download the new application and updates itself. I something fail (e.g. the network is disconnected), the update process restarts the next time the application run.

Running the sample

Attached to this article you may found a Visual Studio 2008 project that shows a real application using the Out Of Browser Experience. To run this sample you need to have the Silverlight 3.0 installed and the Silverlight Tools for Visual Studio 2008 updated to the latest version. You have to know that after installing this updates the computer will be a full Silverlight 3.0 machine so if you have to develope something with Silverlight 2.0 it is better you not install anything.

I think the OOB experience will trigger many new applications. The simple ToDoList I've created is the evidence that unleashing your fantasy you may think about many applications of this tecnique. In the next month I hope many of you will stars publish your own sample.

Download code: (2.02 MB)

Demo Video: (3.5 MB)


More than often working with Silverlight you may want to communicate from a plugin instance to another instance in the same page. Coordinating scenes, across different plugin instances, may be a difficult issue with Silverligth 2.0, involving HTML Bridge and often limitated because this bridge needs to be enabled. With the recently released version 3.0 beta 1 working in this way it is simple like drinking a glass of water thanks to a new feature called Local Connection.

What is a Local Connection?

The new Silverlight 3.0 allow the plugin to expose a listener, with a specific name, that may be addresses by other plugin instances knowing this name. If you had ever used named-pipes for inter process communications you may found that Local Connections are very similar. There are two classes you may use to establish a connection; LocalMessageReceiver and LocalMessageSender. The first one is used to expose an endpoint with this few lines of code:

   1: LocalMessageReceiver receiver = new LocalMessageReceiver(name);
   2: receiver.MessageReceived += new EventHandler<MessageReceivedEventArgs>(Receiver_MessageReceived);
   3: receiver.Listen();

The "name" used when you create the instance of the receiver is the name you have to use when you create the LocalMessageSender. Here is how to create a sender in a remote plugin. The sender needs simply to be instantiated and is ready to send messages. The most beautiful thing is that a couple of Received and Sender may communicate across plugins not always in the same page but also across plugins instantiated in different browsers.

Here is how to instantiate the LocalMessageSender and send a message to a listening receiver:

   1: LocalMessageSender sender = new LocalMessageSender(name);
   2: sender.SendAsync("this is a message");

Obviously the receiver must exist and listening. The messages you may exchange over this channel are plain strings so if you have to send complex informations you have to create a private protocol wrapping this data. As an example you may use JSON or XML Serializer to send class instances to another plugin.

A live example

We have now to create a working example of this tecnique, so let' try to coordinate the movement of one element across four instances, disposed to compose a square. We call this instances with a name, that will be the name of the listening channel and identify the position the plugin assume in the square. the names are "nw", "ne", "sw" and "se" like the cardinal directions.

With a good design we create a class "Communicator" that encapsulate the communicationg channel and expose a method Move() called to move the element on all the listening instances and an event "PositionChanged" that notify to the user interface that the elements position needs to be changed. The Communicator class instance a single LocalMessageReceiver with the name of the local plugin a three LocalMessageSender named with the names of the other plugins.

   1: private List<LocalMessageSender> Senders { get; set; }
   2: private LocalMessageReceiver Receiver { get; set; }
   3:  
   4: public Communicator(string name, IEnumerable<string> clients)
   5: {
   6:     this.Receiver = new LocalMessageReceiver(name);
   7:     this.Receiver.MessageReceived += new EventHandler<MessageReceivedEventArgs>(Receiver_MessageReceived);
   8:     this.Receiver.Listen();
   9:  
  10:     this.Senders = new List<LocalMessageSender>();
  11:  
  12:     foreach (string client in clients)
  13:         this.Senders.Add(new LocalMessageSender(client));
  14: }

 

Now that we have created the channels to allow the position exchange we have to plan how to exchange the position coordinates. X and Y position wil be transmitted in a pipe-separated string. Deserializing this string imply to split the string in two parts and create a Point instance with the double values parsed in the strings. In the MessageReceived event we raise the PositionChanged event deserializing the incoming message and forwarding the point to the UI to apply it to a translate transform the shift the graphical element on the plugin area.

   1: public event EventHandler<PositionChangedEventArgs> PositionChanged;
   2:  
   3: private void Receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
   4: {
   5:     string[] coords = e.Message.Split('|');
   6:  
   7:     this.OnPositionChanged(
   8:         new Point(double.Parse(coords[0]), double.Parse(coords[1])));
   9: }
  10:  
  11: protected virtual void OnPositionChanged(Point point)
  12: {
  13:     EventHandler<PositionChangedEventArgs> handler = this.PositionChanged;
  14:  
  15:     if (handler != null)
  16:         handler(this, new PositionChangedEventArgs(point));
  17: }

In the Move() method we have to perform two actions. The first action serialize the Point to a string and send it to other plugins using the SendAsync() method. The message is delivered to the receivers that will execute the MessageReceived event code. The second action we have to perform is raising a local PositionChanged event to update the position of the local element:

   1: public void Move(Point point)
   2: {
   3:     string message = string.Format("{0}|{1}", point.X, point.Y);
   4:  
   5:     foreach (LocalMessageSender sender in this.Senders)
   6:         sender.SendAsync(message);
   7:  
   8:     this.OnPositionChanged(point);
   9: }

Now that the Communicator class is ready we have simply to wrap mouse events to handle dragging inside a plugin. This is a simple task using MouseLeftButtonDown, MouseLeftButtonUp and MouseMove. The Loaded event evaluate the name of the plugin and initially move the element in a particular position to let it appear the same under the plugins. Using a translate transform allow to exchange coordinated that are the same on all the instances so we do not need to apply any calculation. Here is the code:

   1: private void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
   2: {
   3:     // if StartPoint is not null the Mouse button is pressed
   4:     if (this.StartPoint.HasValue)
   5:     {
   6:         Point position = e.GetPosition(this.LayoutRoot);
   7:  
   8:         double x = this.StartTransform.Value.X + position.X - this.StartPoint.Value.X;
   9:         double y = this.StartTransform.Value.Y + position.Y - this.StartPoint.Value.Y;
  10:  
  11:         this.Communicator.Move(new Point(x, y));
  12:     }
  13: }
  14:  
  15: private void Communicator_PositionChanged(object sender, PositionChangedEventArgs e)
  16: {
  17:     transform.X = e.Point.X;
  18:     transform.Y = e.Point.Y;
  19: }

As you may see the incoming message is applied to the transformation. In this way the element appear to move on all the instances in the same moment. To have a try of this effect simply click this link and drag with the mouse into each circle. You will see the element moving in each plugin instance at the same time giving a strange effect.

But the best demostration is to open every instance in a new browser window. If you assign at every new window the correct string "nw", "ne", etc... you may move the element in a windows and you will see the same elements moving in the other windows. Local connections are capable to communicate also throught different browser instances.

I think this new feature is really interesting. I may imagine a couple of ways to use Local connections, but I leave to your fantasy to discover new useful scenarion where you may apply this tecnique.

Download Code: (856 KB)

Demo Video: (1.9 MB)


I know. Every time you try to animate something in a Silverlight scene, but also in WPF, the result is impressive, but also is always slightly unnatural. The problem here is that in the real world the movements is ruled by some natural forces that simply modify the path and speed of moving object. Friction, gravity,  centrifugal force and other phisical rules modify movements in a non linear way. We live in a world filled by this rules and every thing not respecting them appear unnatural.

Simulating a bounce or a gravity deceleration or acceleration in Silverlight 2.0 require to handle complex animations made of a big amount of keyframe, and is very hard to accomplish. All this rules are often easily to write as a mathematical formula so the Silverlight 3.0 team have introduced the Easing functions, a way to let the maths do its work and to let our animation to become very easy and simple.

Using an Easing function is really easy. You have to declare the function in the resources, using one of the built-in functions, and then refer to it from an animation EasingFuncion attribute. Here is an example:

   1: <Canvas.Resources>
   2:  
   3:     <SineEase x:Key="easeOut" EasingMode="EaseOut" />
   4:  
   5:     <Storyboard x:Name="animRXOut">
   6:         <DoubleAnimation To="0" Duration="00:00:00.300"
   7:                          Completed="animRXOut_Completed"
   8:                          EasingFunction="{StaticResource easeOut}"
   9:                          Storyboard.TargetName="ball1" Storyboard.TargetProperty="(RenderTransform).(Angle)" />
  10:     </Storyboard>
  11:  
  12: </Canvas.Resources>

Every easing function let us choice if we need an EaseIn, EaseOut or both EaseInOut. This cause the easing function to be applied to the start of the animation, to the end or to both start and end. There are a couple of built-in functions in silverlight 3.0:

  • BackEase: This moves the animation backwards a little before continuing. It’s a little bit like starting a car on a hill, you roll back a little before you move forward.
  • BounceEase: As we saw in the previous example, this creates a bouncing effect.
  • CircleEase: This accelerates the animation based on a circular function, where the initial acceleration is slower and the latter acceleration is higher.
  • CubicEase: This is similar to the CircleEase, but is based on the cubic formula of time causing a slower acceleration in the beginning and a more rapid one towards the end of the animation.
  • ElasticEase: This is similar to the BounceEase in that it oscillates the value until it comes to a rest.
  • ExponentialEase: Similar to the Circle and Cubic ease in that it is an exponential acceleration from one value to the next.
  • PowerEase: This is an exponential acceleration where the value of the ease is proportional to the power of the time.
  • QuadraticEase: This is very similar to the CubicEase except that in this case the value is based on the square of the time.
  • QuarticEase: Similar to Quadratic and Cubic. This time the value is based on the cube of the time.
  • QuinticEase: Again, similar to Quadratic, Cubic and Quartic. This time the value is based on the time to the power of 5.
  • SineEase: This accelerates the value along a sine wave.

Using Easing functions in practice

Now let you imagine to have to create a simple animation. We have to simulate two iron balls bouncing each on the other. Think at the games were you have two or more balls suspended by a wire.

The animation is composed of 4 segments:

1) the left ball goes to an angle of 40 degree

2) the left ball returns to an angle of 0 degrees

3) the right ball goes to an angle of -40 degrees

4) the right ball returns to an angle of 0 degrees

This animations are concatenated each other using the completed event. Every time an animation completes it start the next animation so we have a perpetual movement. If we run the example without any easing we see the balls moving in an unnatural way. We need to add at least two easing function. The first one is to easing-out the animations 1) and 3). The second is to easing-in the animations 2) and 4). The ball decelerate using a SineEase when goes to 40 (or -40) degrees because of the gravity and accelerate for the same reason when returns to zero using a CubicEase.

You may see a full size screenshot here:

How the Easing Function works

We already said that an easing function is simply a mathematical function applied to the steps of an animation. It is really simple to create your own easing functions by implementing the abstract class EasingFunctionBase, but you need to understand how the function has to calculate the values. If you think at the animation as a 0 when it starts and 1 when it stops, then you will have a value from 0 to 1 to represent a fraction of the animation itself.

The easing function has to work with this values to calculate a resulting position for each step. The values may also have a value out of the range of 0 to 1 resulting in the animation to overcome the bounds of the animation. This is the case of an ElasticEase where the end of the animation go up and down of the final value.

To better understand the easing function let me do another example using the previous scene. To avoid the balls moving perpetually after you start the animation clicking the button, every time the ball reach the upper bound the maximum limit has to be decreased so it reach the value of zero after a couple of bounces. In a real world also the ramp down is not linear. For this sample I've used a QuadraticEase initializing it in the code and calling by myself the Ease method. Here is the code snippet:

   1: /// <summary>
   2: /// Initializes a new instance of the <see cref="Page"/> class.
   3: /// </summary>
   4: public Page()
   5: {
   6:     this.OscillationEase = new QuadraticEase { EasingMode = EasingMode.EaseOut };
   7:     InitializeComponent();
   8: }

I've managed to have a number from 1 to 0 representing the steps of the animation decreased by 0.025 every time an animation reach the end. This number multiplied by 40 give the maximum amount of degree to rotate. To ease the transition from 1 to 0 I call the Ease method:

   1: private void animRXIn_Completed(object sender, EventArgs e)
   2: {
   3:     this.current -= 0.025;
   4:  
   5:     if (this.current > 0)
   6:     {
   7:         ((DoubleAnimation)animLXOut.Children[0]).To = this.OscillationEase.Ease(this.current) * 40.0;
   8:         animLXOut.Begin();
   9:     }
  10:     else
  11:         startButton.IsEnabled = true;
  12: }

I think this sample have explained how the EasingFunction work. Now you have to try by yourself. Easing Function are powerful tools in the hands of a designer because give an interface a better appeal and a more fluid and believable animations.

Download Code: (552 KB)

Demo Video: (720 KB)


WriteableBitmap Sample Dopo lunga attesa finalmente è stato annunciata oggi da Scott Guthrie durante la keynote al Mix 2009 la beta 1 di Silverlight 3.0. Ho avuto modo di lavorare per un po' con questa release e devo dire che la trovo davvero stabile e carica di numerosissime novità. Prima fra tutti la Out of Browser Experience, che consente all'utente di "staccare" dal browser le applicazioni Silverlight 3.0 appositamente sviluppate per questo scopo e lanciarle comodamente da un link sul desktop anche in modalità disconnessa dalla rete. Ci muoviamo quindi verso la creazione di applicazioni .NET che non richiedono installazione e che possono essere fatte girare su qualsiasi piattaforma anche fuori dal consueto browser.

Lasciando da parte il "pezzo grosso", in Silverlight 3.0 troviamo innumerevoli altri miglioramenti, alcuni dei quali ci si aspettava da tempo come il Databinding tra elementi, nuovi controlli quali la ViewBox, DockPanel e WrapPanel e il miglioramento degli stili e della gestione delle risorse.

Si potrebbe andare avanti a lungo a elencare le nuove feature, Pseudo 3D, Local Connection, Easing, miglioramenti al supporto testuale, Data Validation etc. Per evitare un lungo e inutile elenco ho preparato una serie di 5 articoli sulle caratteristiche che ho provato più divertenti e utili e vi invito a leggerli su :

In questo post mostro nei dettagli come configurare e strutturare una applicazione per funzionare in modalità Out Of Browser. Nell'articolo si costruisce una piccola applicazione ToDoList che sfrutta l'IsolatedStorage per persistere una lista di Task con Titolo, Descrizione e Data

 

Da questa versione Silverlight darà la possibilità di generare immagini all'interno del plugin per mezzo di una classe WriteableBitmap. Per mostrarne l'uso ho costruito una piccola applicazione che consente di esplorare l'insieme dei frattali di Mandelbrot.

Il dialogo tra istanze del plugin nella stessa pagina è una caratteristica certamente utile. La possibilità di dialogare anche tra istanze che girano su finestre diverse è addirittura sorprendente. In questo articolo quattro istanze del plugin sono coordinate per mostrare le potenzialità di questa tecnica.

Fare animazioni con silverlight è molto semplice. Renderle realistiche però è più complesso di quello che ci si può aspettare. Decelerazioni, effetti si rimbazo e elasticità sono di sicuro impatto ma richiedevano fino ad oggi l'uso di innumerevoli KeyFrame. In questa demo vedremo come usare le Easing Functions per arricchire le proprie applicazioni

Sviluppando applicazioni connesse con Silverlight l'avere a disposizione dei "servizi" che girano in background è molto utile. L'implementazione di IApplicationService è semplice e garantisce il pieno controllo del ciclo di vita di un Application Service.

I cinque articoli che vi propongo sono corredati tutti di un video e di un progetto di Visual Studio 2008 da scaricare e provare. Attenzione però che l'installazione di Silverlight 3.0 e dei Tools per Visual Studio vi potrebbe rendere impossibile lo sviluppo con Silverlight 2.0. Se dovete usare la macchina per lavoro fate molta attenzione.

Nei prossimi giorni troverò sicuramente il tempo di preparare altro materiale e non mancherò di proporvelo con dovizia di particolari.

Per ora, buon divertimento...


Era tanto tempo che ci pensavo e finalmente posso annunciare che è aperto al pubblico . Chi mi segue saprà che questo è il nome che ho dato alla categoria del mio weblog che contiene tutti i miei post su Silverlight. Molti però avranno osservato che sempre più spesso mi trovo a scrivere i miei articoli in inglese. Ecco perchè ho deciso di donare a questa categoria la dignità di un weblog separato nel quale da oggi inizierò a dirottare i post su Silverlight in questa lingua.

Naturalmente il mio weblog non ne sarà sminuito. Tutto ciò che riguarda ASP.NET, LINQ e probabilmente anche i post in italiano su Silverlight continueranno a uscire su http://blog.boschin.it tuttavia credo sia meglio per tutti che vi sia una netta separazione tra i contenuti italiani e inglesi.

Chi lo desidera può già sottoscrivere il nuovo feed: http://feeds2.feedburner.com/SilverlightPlayground

Su Silverlight Playground ho già provveduto a copiare alcuni articoli che sono usciti in precedenza su queste pagine, perciò chi li ha già letti per il momento si accontenti. Già nella giornata di domani usciranno una serie di articoli nuovi che sono certo susciteranno molto interesse.

Ora mi rilasso, e attendo l' di domani...


image Approfitto del fatto che finalmente le foto che ha scattato lo scorso 27 febbraio, per ringraziare tutti i presenti al meeting "Silverlight 2.0: Networking Explained", per la calorosa accoglienza e la numerosa partecipazione.

La serata è andata davvero bene dal mio punto di vista; Mi sono sentito decisamente sciolto e nonostante sulle prime mi fossi preoccupato di aver scelto un argomento eccessivamente tecnico il pubblico ha dimostrato di apprezzare lo scendere in particolari così profondi e dettagliati su un argomento. Ho l'impressione che la scelta di argomenti di questo tipo possa essere in realtà vincente perchè consentono alle persone di portarsi a casa un bagaglio che da la possibilità di valutare una tecnologia per quello che realmente sa dare.  Credo che per il prossimo semestre proverò nuovamente a proporre qualcosa del genere.

Entro breve (dove breve significa "non prima di due settimane") vorrei proporvi degli articoli sul blog che riprendono gli esempi che ho preparato per il meeting, cercando di dettagliarli il più possibile e magari includendo uno screencast che ne dimostri il funzionamento. Il mio preferito è sicuramente l'esempio del Socket, dove ho creato un piccolo Task Manager online che mostra il carico di lavoro sul server, ma non è da trascurare nemmeno l'esempio di PollingDuplex.

Vi consiglio quindi di continuare a seguirmi. I prossimi giorni potrebbero essere molto densi... ;)