Silverlight Tips of the Day - Blog by Mike Snow

Game Programming with Silverlight
Silverlight Tip of the Day #33: How to Scale your entire App and its Elements to your Browsers Size

Let’s say you have a Silverlight application that you want to be scaled to the same width and height of your browser window. This way the application takes up the entire window and not just a fixed sized within the window. To illustrate this I have created a sample application that has a number of random UI elements on it.

You can preview and run this application here: http://silverlight.services.live.com/invoke/66033/Page%20Scaling/iframe.html

Also, the following screen shots show this sample application scaled to different sizes in the browser (tall, normal, wide). As you can see, each UI element in the application is scaled proportionally to the browser size.

 image imageimage

In order to accomplish this, all you have to do is add a RenderTransform of the type ScaleTransform to your Grid or Canvas of your Silverlight control.

For example, add the following code to your Page.xaml file:

<Canvas>
    <Canvas.RenderTransform>
        <ScaleTransform x:Name="CanvasScale" ScaleX=”1” ScaleY=”1”></ScaleTransform>
    </Canvas.RenderTransform>
</Canvas>

Setting ScaleX and ScaleY to “1” is equivelent to a 100% scale. If you set the ScaleX and ScaleY to “0.33” the control would render at 1/3 its original.

Now, as demonstrated in Tip of the Day #9 monitor for your browser resize in your Page.xaml.cs file. Set the CanvasScale ScaleX and ScaleY to be a new percentage of the original width and height:

namespace ScaleTransform
{
    public partial class Page : UserControl
    {
        private int _startingWidth = 800;
        private int _startingHeight = 600;
 
        public Page()
        {
            InitializeComponent();
 
            App.Current.Host.Content.Resized += new EventHandler(Content_Resized);
        }
        void Content_Resized(object sender, EventArgs e)
        {
            double height = App.Current.Host.Content.ActualHeight;   
            double width = App.Current.Host.Content.ActualWidth;
 
            CanvasScale.ScaleX = width / _startingWidth;
            CanvasScale.ScaleY = height / _startingHeight;
        }
       
    }
}

 

Thank you,
--Mike Snow

 Subscribe in a reader

Silverlight Tip of the Day #32: How to Declare a Custom User Control from a XAML Page.

UserControls are a great way to separate objects into smaller, more manageable chunks of logic. These controls can reused by different applications and are independent from other controls. Each UserControl can contain any amount of content and logic and can be directly added to your Canvas tree (I.e. MyCanvas.Children.Add(myControl)).

Once you have a UserControl created, how do you reference or declare it from another XAML file (such as Page.xaml)? Doing this is relatively straight forward and we will demonstrate how to do this in this tutorial.

Run this application here to preview: http://silverlight.services.live.com/invoke/66033/Custom%20UserControl/iframe.html

To start, let’s create a custom UserControl called “Card”. In Visual Studio 2008, Right click on your Silverlight Application and choose “Add->New Item…”.

image

In the Add New Item dialog, choose “Silverlight User Control” and change the name to “Card.xaml”.

image

Add the following two images to your Silverlight application project:

CardDiamond3.png                           CardHeartAce.png

image          image

Now, open up Card.xaml and:

  1. Add an Image in the UserContol.
  2. Set the “x:Name” of the Image to “CardImg”.
  3. Set the source default value to”CardHeartAce.png”
  4. Remove the controls Width, Height and Grid tags as they are not needed. Your Card.xaml should now look like this:
<UserControl x:Class="UserCtrl.Card"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Image x:Name="CardImg" Source="CardHeartAce.png"></Image>
</UserControl>

Next we want to give users the ability to change the image of the card. Open Card.xaml.cs and add the following code which will allow the user to change the image:

namespace UserCtrl
{
    public partial class Card : UserControl
    {
        public Card()
        {
            InitializeComponent();
        }
 
        public ImageSource CardImage
        {
            get { return CardImg.Source; }
            set
            {
                this.CardImg.Source = value;
            }
        }
    }
}

Our final step is to show you how to reference this UserControl from a xaml file such as Page.xaml.

In Page.xaml:

  1. Add a local reference to your Page.xaml UserControl which will allow you to reference objects in your assembly:

    <
    UserControl x:Class="UserCtrl.Page"
        xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
        xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
        xmlns:local="clr-namespace:UserCtrl;assembly=UserCtrl" 
        Width="480" Height="300" >
  2. Declare a couple references to your Card control like this:

    <UserControl x:Class="UserCtrl.Page"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:local="clr-namespace:UserCtrl;assembly=UserCtrl" 
        Width="480" Height="300" >
        <Canvas x:Name="MyCanvas" Background="Gray">
     
            <local:Card Canvas.Left="50" Canvas.Top="20" x:Name="MyCard" CardImage="CardDiamond3.png" >
            </local:Card>
     
            <local:Card Canvas.Left="250" Canvas.Top="20" x:Name="MyCard2" CardImage="CardHeartAce.png" >
            </local:Card>
     
        </Canvas>
    </UserControl>
  3. Run the application and you will see two cards are shown!

    image 

Thank you,
--Mike Snow

 Subscribe in a reader

Silverlight Terrain Tutorial Part 3 - Creating Smooth Tile Transitions using Opacity Masks

This tutorial will look into using the Opacity property on the tiles to create smooth, natural looking transitions between tiles. For example, blending a dirt tile into a grass tile, a grass tile into a rock tile, etc.

Run the demo here: http://silverlight.services.live.com/invoke/66033/Terrain%20Transititions/iframe.html

Below, on the left, is a screen shot of a grass and dirt tile without the use of an opacity mask. As you can see, the straight line does not make for a very real looking transition! On the right is the result with an opacity mask applied, making for a much more realistic transition.

No Opacity Mask                                           With Opacity Mask

image image

So how does this work? What we do is we put down a grass layer followed by a layer of dirt on the same tile location. We then apply an Opacity mask to the dirt image which tells Silverlight what level of rendering to do on each pixel within the dirt image.

Example:

Layer 1                                    Layer 2                                  Opacity Mask                          Result

image + image+image =image

The checker boxes in the opacity image (image #3) above represent the areas that are transparent in the PNG opacity mask. In this area, the image will not be drawn allowing any image below it show through.

Here’s how it looks in our XAML code:

<Image Source="grass.png"></Image>
<Image Source="dirt.png" >
    <Image.OpacityMask >
        <ImageBrush ImageSource="opacityMask.png"></ImageBrush>
    </Image.OpacityMask>
</Image>

Using a PNG image file, transparency is represented by the alpha value only. All the other colors in this image have no impact. Each color is represented by “#AARRGGBB” where AA=alpha hex value, RR=red hex value, GG=green hex value and BB= blue hex value.  Transparency in a pixel increases starting at #00000000 (completely non-transparent) to #FF000000 (completly transparent.

If you do not want to use an image for your opacity mask, you can also use a Brush. For example, this tile shows a transition using a LinearImageBrush:

Layer 1                               Layer 2                                 LinearGradientBrush            Result

image+image+ image=image 

Here is the XAML code:

<Image Source="grass.png"></Image>
<Image Source="dirt.png">
    <Image.OpacityMask >
        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
            <GradientStop Color="#FF000000" Offset="0.0" />
            <GradientStop Color="#00000000" Offset="1.0" />
        </LinearGradientBrush>
    </Image.OpacityMask>
</Image>

Using other brush types, such as the RadialGradientBrush, you can do a lot of other cool effects.

Example:

image+image+image= image

XAML Code:

<Image Source="grass.png"></Image>
<Image Source="dirt.png">
    <Image.OpacityMask >
        <RadialGradientBrush GradientOrigin="1,0">
            <GradientStop Color="#FF000000" Offset="0.0" />
            <GradientStop Color="#FF000000" Offset="0.25" />
            <GradientStop Color="#AE000000" Offset="0.75" />
            <GradientStop Color="#00000000" Offset="1.0" />
        </RadialGradientBrush>
    </Image.OpacityMask>
</Image>

Thank you,
--Mike Snow

 Subscribe in a reader

Silverlight Tip of the Day #31: How to Detect Alt, Shift, Control, Windows and Apple keys with Left Mouse Down in Silverlight

When clicking on your Silverlight application how do you know if the <Alt>, <Shift>, <Ctrl>, <Windows> and/or <Apple> key is down as well?

To do this, you simply need to check the Keyboard.Modifiers member which returns a ModifierKeys object.

The following code below in our Page.xaml.cs shows how this is done.

Run and preview this app here: http://silverlight.services.live.com/invoke/66033/Left%20Mouse%20Down/iframe.html

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 ShiftMouseClick
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
 
            this.MouseLeftButtonDown += new MouseButtonEventHandler(Page_MouseLeftButtonDown);
        }
 
        void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Data.Text = String.Empty;
 
            ModifierKeys keys = Keyboard.Modifiers;
 
            bool shiftKey = (keys & ModifierKeys.Shift) != 0;
            bool altKey = (keys & ModifierKeys.Alt) != 0;
            bool appleKey = (keys & ModifierKeys.Apple) != 0;
            bool controlKey = (keys & ModifierKeys.Control) != 0;
            bool windowsKey = (keys & ModifierKeys.Windows) != 0;
 
            if (true == shiftKey)
                Data.Text += "<shift>";
            if (true == altKey)
                Data.Text += "<alt>";
            if (true == appleKey)
                Data.Text += "<apple>";
            if (true == controlKey)
                Data.Text += "<ctrl>";
            if (true == windowsKey)
                Data.Text += "<windows>";
 
            Data.Text += " Left Clicked";
 
        }
    }
}

And our Page.xaml:

<UserControl x:Class="ShiftMouseClick.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">
        <TextBlock x:Name="Data">Click here (also use alt,ctrl,windows,apple,shift keys)</TextBlock>
    </Grid>
</UserControl>

Thank you,
--Mike Snow

 Subscribe in a reader

Silverlight Tip of the Day #30: Fast Sprite Animation in Silverlight

For this tutorial I will be demonstrating how to create a fast, optimized Sprite animation class. In my demo, you will be able to:

  1. Increase the speed of the sprites.
  2. Increase the count of the sprites.

To stress the sprites further, I have added random movement (bouncing off walls), rotational transforms and opacity/transparency shifting.

Run this application now by clicking on this link: http://silverlight.services.live.com/invoke/66033/Fast%20Sprites/iframe.html. If you increase the speed and the number of sprites, you should notice the animation continues to run smoothly despite the large number and speed.

Preview Screen Shot (left = 100 sprites, right = 1000 sprites)

image  image

Each sprite is encapsulated in its own class I have called Sprite. Let’s take a look at the complete class for a Sprite below. In summary:

  1. The Sprite class inherits from Control. This way you can directly add your Sprite to the Canvas tree. The XAML that represents the sprite is put into a template (_spriteTemplate) and which is applied to the class by calling ApplyTemplate();
  2. The Sprite constructor takes the width and height of the sprite. It also loads and applies the template.
  3. To set the image of the Sprite you must call SetImage() passing the file name of the image resource which must be included as part of your project.
  4. The rest of the routines in this class control the movement, position and rotation of the Sprite.
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.Markup;
 
namespace FastAnimation
{
    public class Sprite : Control
    {
        private Image _spriteImage;
        private RotateTransform _rotateTransform;
        private int _width;
        private int _height;
        private double _posX = 0;
        private double _posY = 0;
        private double _xInc = 0;
        private double _yInc = 0;
        private int _opacityDir = 1;
 
        private string _spriteTemplate =
          "<ControlTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"" +
          "                  xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">" +
          "<Image x:Name=\"SpriteImage\">" +
          "   <Image.RenderTransform>" +
          "      <RotateTransform x:Name=\"ImageTransform\">" +
          "      </RotateTransform>" +
          "   </Image.RenderTransform>" +
          "</Image>" +
          "</ControlTemplate>";
 
        public Sprite(int width, int height)