Silverlight Tips of the Day - Blog by Mike Snow

Game Programming with Silverlight

July 2008 - Posts

Silverlight Tip of the Day #23 – How to Capture the Mouse Wheel Event

Silverlight currently does not support mouse wheel events. However, you can attach an event to capture the mouse wheel movement through the HtmlPage object. This tutorial will show you how to do it for IE, Opera, Mozilla and Safari browsers.

To start, we declare three events to capture the mouse event in order to cover all the possibilities for all browsers. For example, the DOMMouseScroll event is used by Mozilla.

public Page()
{
    InitializeComponent();
 
    HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheel);
    HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheel);
    HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheel);
}

In our event we can get the delta change in the wheel. The way we do this in Mozilla and Safari browsers is different than how we do it in IE or Opera.

  1. Mozilla/Safari: Check the property called “detail”
  2. IE/Opera: Check the property called “wheeldelta”

Here is the complete code:

private void OnMouseWheel(object sender, HtmlEventArgs args)
{
    double mouseDelta = 0;
    ScriptObject e = args.EventObject;
 
    // Mozilla and Safari
    if (e.GetProperty("detail") != null) 
    {
        mouseDelta = ((double)e.GetProperty("detail"));
    }
    // IE and Opera
    else if (e.GetProperty("wheelDelta") != null) 
        mouseDelta = ((double)e.GetProperty("wheelDelta"));
 
    mouseDelta = Math.Sign(mouseDelta);
}

Thank you,
--Mike Snow

 Subscribe in a reader

Silverlight Tip of the Day #22 – How to add Sound Effects, Music and Video to your Silverlight App.

No game is complete without a great sound track and sound effects! This tutorial will show you how to do both with a very small amount of code.

Silverlight currently supports the following formats:

Video

  1. WMV1-3 (Windows Media Video 7, 8 & 9 respectively).
  2. WMVA (Windows Media Video Advanced Profile, non VC-1).
  3. WMVC1 (Windows Media Video Advanced Profile, VC-1).

Audio

  1. MP3 – ISO/MPEG Layer-3
    • Sampling frequencies: 8, 11.025, 12, 16, 22.05, 24, 32, 44.1, and 48 kHz
    • Bit rates: 8-320 kbps, variable bit rate.
  2. WMA 7 (Windows Media Audio 7)
  3. WMA 8 (Windows Media Audio 8)
  4. WMA 9 (Windows Media Audio 9)

To add sound, music or video you will need to declare or create a MediaElement. Each media file you reference must be added to your web sites ClientBin folder in your Visual Studio web site project. Note: Do not add the media file to your Silverlight application project, add it to the web site project.

Let’s start by adding three buttons and three MediaElements to our Page.xaml. One button for playing music, one for sound, and the other for video.

<Grid x:Name="LayoutRoot" Background="White">
    <Canvas>
        <Button Click="Button_Click_Music" Canvas.Left="10" Canvas.Top="50" Width="80" Height="30" Content="Play Music"></Button>
        <Button Click="Button_Click_Sound" Canvas.Left="100" Canvas.Top="50" Width="80" Height="30" Content="Play Sound"></Button>
        <Button Click="Button_Click_Video" Canvas.Left="200" Canvas.Top="50" Width="80" Height="30" Content="Play Video"></Button>
        <MediaElement x:Name="SoundFile" Source="Boom.mp3" AutoPlay="False"></MediaElement>
        <MediaElement x:Name="MusicFile" Source="MySong.mp3" AutoPlay="False"></MediaElement>
        <MediaElement Width="300" Height="300" Canvas.Top="100" x:Name="VideoFile" AutoPlay="False"  Source="MyVideo.wmv"></MediaElement>
    </Canvas>
</Grid>

For our code behind:

 
namespace SoundDemo
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
        }
 
        private void Button_Click_Music(object sender, RoutedEventArgs e)
        {
            MusicFile.Stop();
            MusicFile.Play();
        }
 
        private void Button_Click_Sound(object sender, RoutedEventArgs e)
        {
            SoundFile.Stop();
            SoundFile.Play();
        }
 
        private void Button_Click_Video(object sender, RoutedEventArgs e)
        {
            VideoFile.Stop();
            VideoFile.Play();
        }
    }
}

Fewthings to notice:

  1. We set AutoPlay=”False” to prevent the media element from playing as soon as the app starts.
  2. We use the Source property to point to the name of the file we want to play (that you have placed in your ClientBin folder).
  3. We use the x:Name attribute to identify the sound element. We can then use this ID to play the sound file. Example: VideoFile.Play().

Here is the screen shot of our app with the “Play Video” button pressed:

image

Thank you,
--Mike Snow

 Subscribe in a reader

Silverlight Tip of the Day #21 – How to work with Shapes, Brushes and Gradients

Shapes, gradients and brushes provide the user with a lot of power to make custom controls that look beautiful. For example, gradients can be made to look like shadows or lighting for stunning visual effects. For this demo, we are going to make the beginnings of what could end up being a clock, or taken further, a gauge. The screen shot below is what we will be achieving in this tutorial.

image

Before we make this clock, let’s review the properties that will make these controls.

Shapes Elements

Silverlight supports what is called vector graphics by providing the following basic shapes:

  • Ellipse – Describes an oval or circle.
  • Rectangle – Describes a rectangle or a square (can have rounded corners).
  • Line – Describes a line that connects two points.
  • Polygon – Describes a closed shape with an arbitrary number of sides.
  • Polyline – Descries a series of connected lines that may or may not form a closed shape.
  • Path – Describes complex shapes that include curves and arcs.

Color

Color can be specified in one of the following formats:

  • Its name such as “Red”, “Blue”, “Black”, etc.
  • A 6 digit RGB (red, green, blue) notation #rrggbb where rr, gg and bb are the two digit hexadecimal value that describes the amount of color for red, green and blue respectively. Example: #FF0000 would be a bright red color.
  • A 8 digit ARGB (alpha, red, green, blue) notation with two extra values that describe the alpha value (opacity) of the color. For example, #FFFF0000 would be bright red with no opacity.

Fill & Fill

Shapes consist of two parts, the outer outline (border) and the inner part. The color of these parts are controlled through the Stroke and Fill properties. Some shapes such as a Line only have a stroke. Specifying a Fill for a line would have no affect.

image

Brushes

Using <Ellipse.Fill> we can fill this ellipse using one of the following brush options:

  • A linear gradient brush – Paints a gradient along a line. The line is diagonal and stretches from the upper left to the lower right corner by default. The properties StartPoint and EndPoint can change these positions.
  • A Image brush – Paints with an image.
  • A radial gradient brush – Paints a gradient along a circle. By default, the circle is centered on the area being painted.
  • A solid color brush – Paints an area with a solid color.
  • A video brush – Paints an area with live streaming video.

Here is an example of each:

image

As appears in source code:

<MediaElement Canvas.Top="300" x:Name="MyMedia" Source="MyVideo.wmv" Width="300" Height="300" />
 
<Ellipse Canvas.Top="20" Canvas.Left="5" Width="100" Height="100" StrokeThickness="2">
    <Ellipse.Fill>
        <LinearGradientBrush
        StartPoint='0.1,0.06'
        EndPoint='0.5,0.6'>
            <GradientStop Color='#FFFFFFFF' Offset='0'/>
            <GradientStop Color='#FF000000' Offset='1'/>
        </LinearGradientBrush>
    </Ellipse.Fill>
</Ellipse>
 
<Ellipse  Canvas.Top="20" Canvas.Left="105" Width="100" Height="100" StrokeThickness="2">
    <Ellipse.Fill>
        <ImageBrush ImageSource="clock.png" Stretch="Uniform"></ImageBrush>
    </Ellipse.Fill>
</Ellipse>
 
<Ellipse Canvas.Top="20" Canvas.Left="205" Width="100" Height="100" StrokeThickness="2">
    <Ellipse.Fill>
        <RadialGradientBrush>
            <GradientStop Color="Yellow" Offset="0.0" />
            <GradientStop Color="Red" Offset="1.0" />
        </RadialGradientBrush>
    </Ellipse.Fill>
</Ellipse>
 
<Ellipse Canvas.Top="20" Canvas.Left="305" Width="100" Height="100" StrokeThickness="2">
    <Ellipse.Fill>
        <SolidColorBrush Color="Blue"/>
    </Ellipse.Fill>
</Ellipse>
 
<Ellipse Canvas.Top="20" Canvas.Left="405" Width="100" Height="100" StrokeThickness="2">
    <Ellipse.Fill>
        <VideoBrush SourceName="MyMedia" />
    </Ellipse.Fill>
</Ellipse>

For the Video brush, make certain the video file is placed in your web sites ClientBin folder.

On to making the clock! We start with a simple ellipse in a <Canvas> object setting it’s Width and Height to 200. For the Fill property, we will apply the LinearGradientBrush

<Grid x:Name="LayoutRoot" Background="Gray">
    <Canvas x:Name="Clock">
        <Ellipse Canvas.Top="150" Canvas.Left="5" Width="200" Height="200" StrokeThickness="2">
            <Ellipse.Fill>
                <LinearGradientBrush StartPoint='0.1,0.06' EndPoint='0.5,0.6'>
                    <GradientStop Color='#FFFFFFFF' Offset='0'/>
                    <GradientStop Color='#FF000000' Offset='1'/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
    </Canvas>
</Grid>

 

This code will generate the following circle:

image

Let’s add another small circle in the middle plus 2 sets of 3 red lines to make the the hour and minute hands for the clock.

            <Line X1="100"  X2="25"  Y1="246" Y2="247" Stroke="Red" StrokeThickness="1" />
            <Line X1="100"  X2="25"  Y1="247" Y2="247" Stroke="Red" StrokeThickness="1" />
            <Line X1="100"  X2="25"  Y1="248" Y2="247" Stroke="Red" StrokeThickness="1" />
 
            <Line X1="106"  X2="60"  Y1="246" Y2="290" Stroke="Red" StrokeThickness="1" />
            <Line X1="106"  X2="60"  Y1="247" Y2="290" Stroke="Red" StrokeThickness="1" />
            <Line X1="106"  X2="60"  Y1="248" Y2="290" Stroke="Red" StrokeThickness="1" />
 
            <Ellipse Width="15" Height="15" Canvas.Left="100" Canvas.Top="240">
                <Ellipse.Fill>
                    <LinearGradientBrush
                        StartPoint='0.1,0.06'
                        EndPoint='0.5,0.6'>
                        <GradientStop Color='#FFFFFFFF' Offset='0'/>
                        <GradientStop Color='#FF000000' Offset='1'/>
                    </LinearGradientBrush>
                </Ellipse.Fill>
            </Ellipse>

image

Finally, for the numbers we will programmatically generate and add them as Textblocks. We do this by calculating the X & Y location along a circle with a radius of 85 pixels. Each number is separated by 30 degrees. We start at 1:00 which is located at an angle of 300 degrees.

   1: private void AddNumbers()
   2: {
   3:     double angle = 300;
   4:     for (int i = 1; i < 13; i++)
   5:     {
   6:         TextBlock tb = new TextBlock();
   7:         tb.Foreground = new SolidColorBrush(Colors.White);
   8:         tb.Text = i.ToString();
   9:  
  10:         double radians = angle * (Math.PI / 180);
  11:         double radius = 85;
  12:         int x = (int)(radius * Math.Cos(radians));
  13:         int y = (int)(radius * Math.Sin(radians));
  14:         tb.SetValue(Canvas.LeftProperty, (double) x+97);
  15:         tb.SetValue(Canvas.TopProperty, (double) y+239);
  16:  
  17:         angle += 30;
  18:         Clock.Children.Add(tb);
  19:     }
  20: }

Here is our final result:

image

Thank you,
--Mike Snow

 Subscribe in a reader

GameFest 2008 – Silverlight Game Programming

Yesterday I had the opportunity to speak on Silverlight game programming at GameFest 2008. Working with Tim Heuer and two Silverlight MVP’s Bill Reiss and Joel Neubeck, we were able to hold two sessions that covered Silverlight.

During my talk we covered topics including:

  1. Creating Silverlight projects using Visual Studio 2008.
  2. Creating a tile based map
  3. Scrolling the map via keyboard events.
  4. Demo of a multi-player game called “Fireball” as well as “Tank Wars”.

I am going to be polishing up and optimizing the source code for both of these games and then I will post the full source with a tutorial and a live version of each game on my blog here. You can expect these by next week.

If you met me at GameFest and did not get my contact you can reach me at msnow@microsoft.com for any questions or suggestions. It was a great privilege to be at the conference and I am looking forward to the next opportunity to speak on Silverlight.

Thank you,

--Mike

Silverlight Tip of the Day #20 – How to Increase your Isolated Storage Quota.

Each application by default is given 1 MB of storage space through Isolated Storage where the server is able to store client specific data on the clients machine. So what if you need more than 1 MB? Fortunately, the IsolatedStorageFile object provides a method called IncreaseQuotaTo() that allows a server to prompt the user for permission to increase the amount of storage.

You can see the list of applications that are using IsolatedStorage on your box. To do this:

  1. Right click on any Silverlight Application and from the context menu choose the only option: “Silverlight Configuration”.

SC

  1. This will bring up the Silverlight Configuration Dialog: 

    1
  2. Click on the last tab “Application Storage” and you will see a list of all web sites that are using Isolated Storage on this machine. The current amount of space each app is using as well as the quota allowed per app are also listed. 

    2

For our demo we will be displaying 3 pieces of information:

  1. Current Spaced Used
  2. Current Space Available
  3. Current Quota

I have added a text box where you can specify the number of bytes you wish to increase your quota by. By clicking on the button, this will then make the necessary call to IncreaseQuotaTo() in order to increase the quota.

***Note: You must call this function from a user event such as a button click. For security reasons, calling IncreaseQuotaTo() directly will automatically return false since the user did not instantiate the call.

Below is a screen shot of the test application we are creating:

3

When you enter a value and click on the button you will be prompted with this dialog:

4

If you click yes, you will see the new results:

image

Now, let’s look at our XAML code in Page.xaml that we use to set up this interface:

<UserControl x:Class="IncreaseIsolatedStorage.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="800" Height="600">
<Grid x:Name="LayoutRoot" Background="White">
    <Canvas Canvas.Left="10" Canvas.Top="10">
        <TextBlock Canvas.Left="10" x:Name="SpacedUsed" >Current Spaced Used=</TextBlock>
        <TextBlock Canvas.Left="10" x:Name="SpaceAvaiable" Canvas.Top="20">Current Space Available=</TextBlock>
        <TextBlock Canvas.Left="10" x:Name="CurrentQuota" Canvas.Top="40">Current Quota=</TextBlock>
        <TextBlock Canvas.Left="10" x:Name="NewSpace" Canvas.Top="70">New space (in bytes) to request=</TextBlock>
        <TextBox Canvas.Left="255" Canvas.Top="70" Width="100" x:Name="SpaceRequest"></TextBox>
        <TextBlock Canvas.Left="365" Canvas.Top="70" Width="60">(1048576 = 1 MB)</TextBlock>
        <Button Canvas.Left="10" Content="Increase Storage" Canvas.Top="100" 
                Width="100" Height="50" Click="Button_Click">                
        </Button>
        <TextBlock Canvas.Left="10" Canvas.Top="160" x:Name="Results"></TextBlock>
    </Canvas>
</Grid>
</UserControl>

And our code behind:

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;
using System.IO.IsolatedStorage;
 
namespace SilverlightApplication11
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
 
            SetStorageData();
        }
 
        private void SetStorageData()
        {