Thunder (beta)

I've been working on a Silverlight 3 drum machine/sequencer called Thunder, the first (beta) version is now available at:

http://www.dontcodetired.com/live/thunder/

 

It's a fully working version but the code base requires some re-factoring. Playback smoothness varies depending on the machine specs and how many instruments are playing at the same time.

Future improvements include the addition of more than 2 drum kits, the ability to use your own drum samples and attempting to improve the performance (although this seems to be limited by having to use a MediaElement to play a stream rather than an immediate .Play() method - hopefully in Silverlight 4 a lower latency sound playback mechanism will be introduced).

SHARE:

Countdown To Christmas 2009 Silverlight 3 app

For a bit of Silverlight 3 fun:

 



Use\install at: http://www.dontcodetired.com/live/christmascountdownbeta/

There are some things which need improving such as resizing when run out of browser, abilty to sort/hide/filter data etc.

SHARE:

Silverlight 3 WritableBitmap pixel colour values

Rather than taking an array of Color objects, the WritableBitmap.Pixels property holds an array on ints which represent premultiplied ARGB colour values. To set a given pixel to a given colour you have to take the alpha, red, green and blue values and convert them to a premultiplied ARGB int value.

The following code contains some helpers/extension methods to make the process bit easier, for example to set every pixel to red:

for (int index = 0; index < bmp.Pixels.Length; index++)
{
   Color myColour = new Color() { A = 255, R = 255, G = 0, B = 0 };
   bmp.Pixels[index] = myColour.ToWritableBitmapPixelValue();
}

 or using the Fill extension method:

bmp.Fill( new Color() { A = 255, R = 255, G = 0, B = 0 } );

WritableBitmapHelper class listing

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;

namespace BitmapAPI
{
    /// <summary>
    /// Helper methods\extension methods for dealing with WritableBitmap,
    /// in real world implementation you'd probably choose to separate the
    /// extension methods iinto separate static classes;
    /// eg WritableBitmapExtensions & ColorExtensions
    /// </summary>
    public static class WritableBitmapHelper
    {
        /// <summary>
        /// Converts ARGB byte values to an integer
        /// Based on info found at:
        /// http://blogs.silverarcade.com/silverlight-games-101/15/silverlight-writeablebitmap-pixel-format-in-silverlight-3/
        /// </summary>
        /// <param name="alpha">Alpha transparency: 0=transparent  255=solid</param>
        /// <param name="red">Amount of red: 0 to 255</param>
        /// <param name="green">Amount of green: 0 to 255</param>
        /// <param name="blue">Amount of blue: 0 to 255</param>
        /// <returns>An integer representing an ARGB color</returns>
        public static int CalcWritableBitmapPixelValue(byte alpha, byte red, byte green, byte blue)
        {
            // Calc premultiplier once only. We need to use premultiplied ARGB32
            // rather than convential ARGB.
            double alphaPreMultiplier = alpha / 255d;

            // Premultiply rgb values with alpha
            byte r = (byte)(red   * alphaPreMultiplier);
            byte g = (byte)(green * alphaPreMultiplier);
            byte b = (byte)(blue  * alphaPreMultiplier);

            return (alpha << 24) | (r << 16) | (g << 8) | b;
        }



        /// <summary>
        /// Extension method to convert a System.Windows.Media.Color to an integer value.
        /// </summary>
        /// <param name="color">The System.Windows.Media.Color to convert</param>
        /// <returns>An integer representing the ARGB values of <paramref name="color"/></returns>
        public static int ToWritableBitmapPixelValue(this Color color)
        {
            return CalcWritableBitmapPixelValue(color.A, color.R, color.G, color.B);
        }



        /// <summary>
        /// Extension method to 'fill' a WriteableBitmap with a given solid color
        /// </summary>
        /// <param name="bmp">The WriteableBitmap to fill</param>
        /// <param name="color">The Color to fill with</param>
        public static void Fill(this WriteableBitmap bmp, Color color)
        {
            for (int i = 0; i < bmp.Pixels.Length; i++)
            {
                bmp.Pixels[i] = color.ToWritableBitmapPixelValue();
            }
        }

    }
}

 

 

 

SHARE:

Silverlight 3 3D Animation Using PlaneProjection and Storyboards

While SL3 doesn't add full 3D support, you can now transform elements in a 3D fashion.

The following videos are recommended (and form the basis of this example) but I've used simple opacity rather than transforming an element off the screen:

http://silverlight.net/learn/learnvideo.aspx?video=187308

http://silverlight.net/learn/learnvideo.aspx?video=189248

 

The new PlaneProject allow you to 'skew' an element in X, Y and Z dimensions. UI elements now have a Projection property which can contain a PlaneProjection:

<Grid.Projection>
  <PlaneProjection x:Name="HeadsGridProjection" RotationX="0" RotationY="0" RotationZ="0"></PlaneProjection>
</Grid.Projection>

The example below uses 2 grids containing a circle (the coin face) and text "Heads" or "Tails". There are 2 storyboards, the 1st rotates the heads to tails, the 2nd animation tails to heads. The storyboards call each other wheneach one finishes using the Completed event of the storyboard.

The code behind looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace _3DCoinFlip
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }
      
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            // Automatically start the 1st storyboard when the page loads
            FlipToTails.Begin();
        }
       
        private void FlipToTails_Completed(object sender, EventArgs e)
        {
            // When the head-->tails animation completes, start the tails-->heads animation
            FlipToHeads.Begin();
        }

        private void FlipToHeads_Completed(object sender, EventArgs e)
        {
            // When the tails-->heads animation completes, start the head-->tails animation
            FlipToTails.Begin();
        }
    }
}

 And the XAML:

<UserControl x:Class="_3DCoinFlip.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    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"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    Loaded="UserControl_Loaded" >
    <UserControl.Resources>
       
        <Style TargetType="Grid" x:Key="CoinFace">
            <Setter Property="Width" Value="200"/>
            <Setter Property="Height" Value="200"/>
        </Style>

        <Style TargetType="TextBlock" x:Key="CoinText">
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="FontSize" Value="48"/>
        </Style>
       

        <Storyboard x:Name="FlipToTails" Completed="FlipToTails_Completed" >
            <!-- heads side -->
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="HeadsGridProjection"
                                           Storyboard.TargetProperty="RotationY">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="90"/>
            </DoubleAnimationUsingKeyFrames>



            <!-- Tails Side -->
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="TailsGridProjection"
                                           Storyboard.TargetProperty="RotationY">
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="270"/>
                <SplineDoubleKeyFrame KeyTime="00:00:02" Value="360"/>
            </DoubleAnimationUsingKeyFrames>

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="Tails"
                                           Storyboard.TargetProperty="Opacity">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01.01" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>



        <Storyboard x:Name="FlipToHeads" Completed="FlipToHeads_Completed">
            <!-- tails side -->
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="TailsGridProjection"
                                           Storyboard.TargetProperty="RotationY">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="90"/>
            </DoubleAnimationUsingKeyFrames>



            <!-- heads Side -->
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="HeadsGridProjection"
                                           Storyboard.TargetProperty="RotationY">
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="270"/>
                <SplineDoubleKeyFrame KeyTime="00:00:02" Value="360"/>
            </DoubleAnimationUsingKeyFrames>

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="Heads"
                                           Storyboard.TargetProperty="Opacity">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01.01" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">
        <StackPanel>
            <Grid>
                <Grid Name="Heads" Style="{StaticResource CoinFace}">
                    <Grid.Projection>
                        <PlaneProjection x:Name="HeadsGridProjection" RotationX="0" RotationY="0" RotationZ="0"></PlaneProjection>
                    </Grid.Projection>
                    <Ellipse Stretch="Fill" Fill="Silver"/>
                    <TextBlock Style="{StaticResource CoinText}">Heads</TextBlock>
                </Grid>
                <Grid Name="Tails" Style="{StaticResource CoinFace}" Opacity="0">
                    <Grid.Projection>
                        <PlaneProjection x:Name="TailsGridProjection" RotationX="0" RotationY="0" RotationZ="0"></PlaneProjection>
                    </Grid.Projection>
                    <Ellipse Stretch="Fill" Fill="Gold"/>
                    <TextBlock Style="{StaticResource CoinText}">Tails</TextBlock>
                </Grid>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

 

SHARE:

Silverlight 3 - Detecting Network Availability

One of the new SL3 features is the ability to test if there is a network connection currently active. This complements the out of browser experience by letting an app use local (isolated) storage when off-line, and when a network connection becomes available, upload data to a central server.

To be informed when the network changes:

System.Net.NetworkInformation.NetworkChange.NetworkAddressChanged += (sender, e) => txtNetStatus.Text = "Network available: " + System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable().ToString();

This example uses a lambda expression as a shortcut (rather than having a separate method); when the network status changes, we set the text of txtNetStatus to 'Network available: True' or 'Network available: False' depedning on the (boolean) result of GetIsNetworkAvailable().

SHARE:

Silverlight 3 Out Of Browser Install Errors

If a user attempts to install a SLOOB app when it is already installed an InvalidOperationException is thrown with the message "Application is already installed.". We can wrap in a try..catch to handle this:

 try
{
    Application.Current.Install();
}
catch (InvalidOperationException ex)
{
    MessageBox.Show(ex.Message);
}

 

Or better yet, remove the install button from the UI if the application is already installed:

 

if (Application.Current.InstallState == InstallState.Installed)
    btnInstall.Visibility = Visibility.Collapsed;
else
    btnInstall.Visibility = Visibility.Visible;

SHARE:

Silverlight 3 Out Of Browser Automatic Updates

When running a Silverlight 3 application out of browser (OOB) {or SLOOB=Silverlight Out Of Browser} you can enable the automatic download\install of updated versions.

This is not an automatic feature and requires some (simple) coding on the developers part.

In the App.xaml.cs add a callback for the CheckAndDownloadUpdateCompleted event (the example below uses a lambda but you could use a separate method with the signature void App_CheckAndDownloadUpdateCompleted(object sender, CheckAndDownloadUpdateCompletedEventArgs e).

public App()
{
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    // Add callback to be executed when the check (and possible download) has been performed
    this.CheckAndDownloadUpdateCompleted += (sender,  e) =>
    {
        if (e.UpdateAvailable)
            MessageBox.Show("Update downloaded, plese restart to take effect.");
    };

     
    InitializeComponent();
}

 Next add a call to check for updates, this can be placed in the Application Startup event handler or you could have a "Check for updates" button in th U.

 private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new MainPage();
    this.CheckAndDownloadUpdateAsync();
}

Now, every time the app start an update check will be performed, and if there is an updated version available it will be downloaded asynchronously. Once download the user will get the message box advising them to restart the app to start using the new version. The download\update just happens if available and there is currently no mechanism to allow the user to opt-out of an update.

 

SHARE:

Running Silverlight 3 Application Out Of Browser

This is pleasantly simple to do :)

In response to a user action (clicking a button, etc.) add the following code: Application.Current.Install(); e.g.

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    Application.Current.Install();
}

Configure the application to run out of browser (OOB):

In the project properties page, tick the"Enable running application out of the browser"

 

The Out-Of-Browser Settings button now becomes enabled: this lets you specify OOB window and shortcut names, description, and an application icon in 4 different sizes.

You can un-install an OOB app by running it (from the desktop or programs menu) and right-clicking and choosing "Remove this application".

SHARE:

Understanding Lambda Expressions

Of all the additions to the C# language, lambda expressions are potentially the most confusing. Part of this might be due to the odd look of the syntax when you first see it or that a lot of the examples use single letters in the expressions.

The best way to start thinking about lambdas is to think of them simply as anonymous methods.

Suppose we have declared a Timer in a WinForms application as: Timer t = new Timer(); We now need to wire up the Timer's Tick event to some code that is to be executed when the Timer fires.

There are various ways to do this without using lambdas:

  1. Declare an explicit event handler method

    void t_Tick(object sender, EventArgs e)
    {
        textBox1.Text = DateTime.Now.ToString();
    }

    ...
    t.Tick +=new EventHandler(t_Tick); or using delegate inference t.Tick += t_Tick;

  2. Use an anonymous method

    t.Tick += delegate(object sender2, EventArgs e2)
    {
       textBox1.Text = DateTime.Now.ToString();
    };


To write the above using a lambda expression you could write:

t.Tick += (object sender2, EventArgs e2) => textBox1.Text = DateTime.Now.ToString();

Or using type inference:

t.Tick += (sender2, e2) => textBox1.Text = DateTime.Now.ToString(); 

These are examples of expression lambdas. Another type is the statement lambda which is similar but can contain multiple statements enclosed in braces; for example we could write the following:

t.Tick += (object sender2, EventArgs e2) =>
{
    textBox1.Text = DateTime.Now.Minute.ToString();
    textBox2.Text = DateTime.Now.Second.ToString();
};

Note: the => is typically read as "goes to".

Another Example

To print all numbers in a list of ints to a TextBox:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
numbers.ForEach(number => textBox1.Text += number.ToString());

To print all number from 6 and up:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
numbers.ForEach(number =>
{
    if (number > 5)
        textBox1.Text += number;
});

or:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
numbers.FindAll(number => number > 5).ForEach(number => textBox1.Text += number.ToString());

SHARE: