You are browsing the archive for Development.

Hidden Object: Episode 6 – Create a CheckBox from an Image

September 26, 2009 in Hidden Object Game, Silverlight

This is episode 6 of Creating a Hidden Object Game is Silverlight 3.

In our last official episode, we added the MagnifierOverBehavior, but now the magnifier is on all the time. To allow the player to turn it on or off, we will create a CheckBox control from an image of a magnifying glass. Creating controls from objects on the artboard is really powerful.

Magnifying glass image

Start Blend and add the image of the magnifying glass to the Images folder then drag an instance of it onto the bottom of LayoutRoot. With the image still selected, right click and select Make Into Control… from the context menu.

Select the CheckBox control from the list, enter the name as MagnifierCheckBoxStyle and define it in the Application. Click OK to create the control and have it open on the artboard.

  • Select the image and set its Stretch to Uniform.
  • Select the ContentPresenter and move it below the image.
  • Select the Grid and set the Height to 70 and the Width to 100.

Now we are going to change the States associated with this CheckBox. In this case the Checked state will show the ContentPresenter while the UnChecked state will set the Opacity to 0%

Click on the States tab and then on the Unchecked state in the CheckStates group. There will be a red dot next to Unchecked indicating that state recording is on. Click on the ContentPresenter and set the Opacity to 0%:

092609_1139_HiddenObjec2.png

Click on the Unchecked state to turn recording off.

For the MouseOver state, we will add a glow effect using the DropShadowEffect. Click on the MouseOver state in the CommonStates group to turn on recording. Click the Image and set its Effect property to a DropShadowEffect:

I changed the Direction and ShadowDepth to 0 and the Color to Yellow.

Click on the MouseOver state to turn off recording.

Close the style editor and with the styled CheckBox selected, set the Content value to the text: “x2″. You might also want to set the Text size to about 14 pt and change the Foreground color of the text.

Run the application and verify the Checked, Unchecked, and MouseOver states.

The completed style looks like this:

<Style x:Key="MagnifierCheckBoxStyle" TargetType="CheckBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid Height="70" Width="100">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked"/>
<VisualState x:Name="Indeterminate"/>
<VisualState x:Name="Unchecked">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="InvalidUnfocused"/>
<VisualState x:Name="InvalidFocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.Direction)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.Color)">
<EasingColorKeyFrame KeyTime="00:00:00" Value="#FFEFFF00"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="Normal"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image x:Name="image" Source="Images/magnifier.png" Margin="0,0,0,19">
<Image.Effect>
<DropShadowEffect/>
</Image.Effect>
</Image>
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="Bottom" Margin="0,0,0,3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Now we have a control to use to toggle the magnification state. In the next episode we will use the behavior base classes, SetInteractionPropertyAction, and this styled CheckBox to complete the magnification feature.

Proof that Silverlight is Evil

September 23, 2009 in Silverlight

Just check out the number of applications in the Silverlight Showcase.  This is for those who think Silverlight is evil.

Creating an Action to set Properties on Actions & Behaviors

September 22, 2009 in Silverlight

Behaviors and actions have properties of their own and it would be useful to be able to set those properties as a result of a trigger.

For this post, we will use the ShowMessageBox action that is part of the Expression Blend Samples CodePlex project.

To explore this, create a Silverlight project in Blend 3, drop a Rectangle on LayoutRoot, and drop a ShowMessageBox action onto the Rectangle. The ShowMessageBox action is under the Behaviors > Experimental in the Assets tab:

Change the rectangle stroke and fill so that it is easily seen.

Set the Caption and Message properties of ShowMessageBox:

When we run the application and click on the rectangle, the message box shows:

What if we wanted to change the value of the Message property when a CheckBox control is checked or unchecked?

To accomplish this, we will create SetInteractionPropertyAction that derives from TargetedTriggerAction and adds properties for the name of the action or behavior, the property on that action/behavior, and the value to set on that property:

public string ObjectName
{
  get { return (string)GetValue(ObjectNameProperty); }
  set { SetValue(ObjectNameProperty, value); }
}
public static readonly DependencyProperty ObjectNameProperty =
  DependencyProperty.Register("ObjectName",
  typeof(string), typeof(SetInteractionPropertyAction),
  new PropertyMetadata(null));</pre>

public string PropertyName
{
  get { return (string)base.GetValue(PropertyNameProperty); }
  set { base.SetValue(PropertyNameProperty, value); }
}
public static readonly DependencyProperty PropertyNameProperty =
    DependencyProperty.Register("PropertyName",
    typeof(string), typeof(SetInteractionPropertyAction),
    new PropertyMetadata(null));

public string Value
{
  get { return (string)base.GetValue(ValueProperty); }
  set { base.SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
  DependencyProperty.Register("Value",
  typeof(string), typeof(SetInteractionPropertyAction),
  new PropertyMetadata(null));

Next we need to implement the Invoke method which finds the action or behavior under the Target control that matches the ObjectName and sets its property defined by PropertyName to the value specified in Value.

To find actions and behaviors associated with a given object, we use the GetTriggers and GetBehaviors static methods defined on the Interaction class in the System.Windows.Interactivity namespace:

  public static TriggerCollection GetTriggers(DependencyObject obj);
  public static BehaviorCollection GetBehaviors(DependencyObject obj);

If we look at some sample XAML we can see that when an action is defined, it becomes part of the Triggers collection and appears under the trigger that causes the action to be invoked. If multiple actions are associated with the same object and use the same trigger, the actions appear together:

<Path>
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseLeftButtonDown">
      <ic:ChangePropertyAction x:Name="changeProperty" TargetName="staplerText" PropertyName="Opacity" Value="0.4"/>
      <im:PlaySoundAction x:Name="playSoundAction" Source="/Audio/magic_wand.mp3"/>
      <ic:RemoveElementAction x:Name="removeElement" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Path>

Calling Interaction.GetTriggers passing a reference to the hosting Path, will return a single trigger of type EventTrigger. For that trigger, we can use its Actions property to access the three actions.

If we look at some XAML for a behavior, we notice that there is a Behaviors collection containing one or more behaviors and for each behavior there is an optional Triggers collection:

<Canvas>
  <i:Interaction.Behaviors>
    <local:ParticlesBehavior x:Name="particles">
      <i:Interaction.Triggers>
        <i:EventTrigger SourceName="staplerPath" EventName="MouseLeftButtonDown">
          <i:InvokeCommandAction CommandName="ShowParticles"/>
        </i:EventTrigger>
        <i:EventTrigger SourceName="idolPath" EventName="MouseLeftButtonDown">
          <i:InvokeCommandAction CommandName="ShowParticles"/>
        </i:EventTrigger>
      </i:Interaction.Triggers>
    </local:ParticlesBehavior>

    <Behaviors:MagnifierOverBehavior x:Name="magnifier"/>
  </i:Interaction.Behaviors>
</Canvas>

In the case, calling Interaction.GetBehaviors with a reference to the Canvas will return two behaviors: ParticlesBehavior and MagnifierOverBehavior

Just like any object you add to the Object tree, you can set the name of an action or behavior. This adds an x:Name property in the XAML and the name appears in the name textbox in the Properties panel. To access the x:Name property in code, get a reference to the behavior or action and call GetValue passing the FrameworkElement.NameProperty dependency property:

  string actionName = (string)action.GetValue(FrameworkElement.NameProperty);
  string behaviorName = (string)behavior.GetValue(FrameworkElement.NameProperty);

For SetInteractionPropertyAction we define the following methods that will try to find an action or behavior that matches the same name as the ObjectName property:

private object TryFindBehavior()
{
  var behaviors = Interaction.GetBehaviors(Target);

  foreach (Behavior behavior in behaviors)
  {
    string behaviorName = (string)behavior.GetValue(FrameworkElement.NameProperty);

    if (behaviorName == ObjectName)
    {
      return behavior;
    }
  }

return null;
}

private object TryFindAction()
{
  var triggers = Interaction.GetTriggers(Target);

  foreach (System.Windows.Interactivity.TriggerBase trigger in triggers)
  {
    foreach (System.Windows.Interactivity.TriggerAction action in trigger.Actions)
    {
      string actionName = (string)action.GetValue(FrameworkElement.NameProperty);

      if (actionName == ObjectName)
      {
        return action;
      }
    }
  }

return null;
}

Finally, the Invoke method uses the previous two methods to find a matching action or behavior and to set one of its properties. I admit that a large portion of the Invoke method in the full project source code was borrowed from the SetProperty action defined in the Expression.Samples.Interactivity assembly (thanks .NET Reflector) :

Type itemType = null;
object item = null;

item = TryFindBehavior();

if (item == null)
{
  item = TryFindAction();
}

if (item == null)
{
  return;
}
else
{
  itemType = item.GetType();
}

PropertyInfo property = itemType.GetProperty(this.PropertyName);
property.SetValue(item, this.Value, null);

After compiling the code, the SetInteractionPropertyAction will appear in the Assets panel.

Let’s add a CheckBox control to LayoutRoot and drop two instances of SetInteractionPropertyAction onto the CheckBox. Next select the ShowMessageBox action and set its name property to showMessageBox.

Click on the first instance of SetInteractionPropertyAction and set it’s EventName, TargetName, ObjectName, PropertyName, and Value properties:

For the second instance of SetInteractionPropertyAction, set the same properties as follows:

Now run the application. Initially, when you click on the rectangle, you will see a message box that says “Hello World”.

When you click on the CheckBox and then click the rectangle, the message will change to “I am checked”. Unchecking the CheckBox and clicking on the rectangle will cause the message to change to “I am unchecked”.

Install Microsoft Silverlight

 

We have successfully set the property of one action based on a trigger invoking SetInteractionPropertyAction.

If you look at the Common Properties group for our custom action, you will notice that the editors for ObjectName, PropertyName, and Value are just TextBox controls. I would like the design-time experience to be better for this action. Maybe in a future post, this will be revisited. You might have noticed that the Value property is of type string even though the data type of the property that corresponds to PropertyName might be another data type. I originally had Value as type object, but there were times when the textbox would gray out and not allow any input. This would also be solved if custom property editors were defined.

The Microsoft.Expression.Interactions assembly defines an action called ChangePropertyAction that sets the property on an object and has a much better designer experience. It provides a drop-down containing property names and when a property is selected, the editor for the Value reflects the underlying data type of the property:

I would like to include these editors plus one that would show a list of named actions and behaviors for the given Target grouped in the list under the words: Actions or Behaviors.

Source code

Base Classes for Custom Behaviors

September 21, 2009 in Silverlight

Behaviors are a powerful way to encapsulate functionality and make it available for designers to use in Blend 3. As your library of custom behaviors grows, you might notice a number of properties that most of your behaviors use. You know that duplication of each of these properties in each of your behaviors is not the right way to go, so you want to create a base class that all your custom behaviors use.

The TriggerAction base class includes an IsEnabled property which I would like to include in all my custom behaviors. Instead of adding the DependencyProperty and associated code to each custom behavior, let’s create a base behavior class.

The existing behavior base classes are:

If we create a custom behavior, MyBehavior, that inherits from Behavior<DependencyObject> and in Blend 3 drop the behavior on the LayoutRoot Grid, we get a behavior with no properties defined:

 

 

To create a base class for our custom behaviors, create an abstract BaseBehavior class that inherits from Behavior<T>. This is where we add the IsEnabled property and any other common properties we want our custom behaviors to have. Next derive a BaseBehavior<T> class from BaseBehavior. It is from BaseBehavior<T> that we will derive our custom behaviors:

 

092209_0022_BaseClasses4.png

The code for BaseBehavior simply derives from Behavior<T> and defines the property, dependency property, and callback method for the IsEnabled property:

 

public abstract class BaseBehavior : Behavior<DependencyObject>
{
    internal BaseBehavior()
    {
    }

    #region IsEnabled (Dependency Property)

    [Category("Common Properties")]
    public bool IsEnabled
    {
        get
        {            
            return (bool)base.GetValue(IsEnabledProperty);
        }
        set
        {
            base.SetValue(IsEnabledProperty, value);
        }
    }

    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.Register(
            "IsEnabled",
            typeof(bool),
            typeof(BaseBehavior),
            new PropertyMetadata(true, new PropertyChangedCallback(OnIsEnabledChanged)));

    private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((BaseBehavior)d).OnIsEnabledChanged(e);
    }

    protected virtual void OnIsEnabledChanged(DependencyPropertyChangedEventArgs e)
    {
    }
    #endregion
}

The BaseBehavior<T> class derives from BaseBehavior and redefines the AssociatedObject property using the new keyword. We do this so that the AssociatedObject can be typed when we derive our custom behavior from it:

 
public abstract class BaseBehavior<T>
    : BaseBehavior where T : DependencyObject
{
    protected BaseBehavior() : base() {}

    protected new T AssociatedObject
    {
        get
        {
            return (T)base.AssociatedObject;
        }
    }
}

In MyBehavior, change the class definition from:

 
public class MyBehavior : Behavior<DependencyObject>

to

public class MyBehavior : BaseBehavior<DependencyObject>

BaseBehavior<T> is a drop-in replacement for Behavior<T>. When we recompile and view the behavior’s properties in Blend, we see that the IsEnabled property has been added:

 

  

As needed, we can add additional properties to BaseBehavior.

What other properties do you think should be added to BaseBehavior?

Source code

Hidden Object: Episode 5 – Add a Magnifier Behavior

September 18, 2009 in Hidden Object Game, Silverlight

 This is episode 5 in the series, Creating a Hidden Object Game in Silverlight 3.

In this episode, we will add an existing MagnifierOverBehavior to our project to allow the user to get a closer look at areas of the picture.

On the SilverlightShow site, there is an article titled, Behaviors and Triggers in Silverlight 3, by Pencho Popadiyn. The article does a great job showing the base classes for Actions and Behaviors as well as shows some custom Behaviors. One of them is the MagnifierOverBehavior which magnifiers an oval area as you move the mouse around the image:

 

It does this by setting the Effect property on the AssociatedObject to a custom pixel shader effect.

Go to the Behaviors and Triggers in Silverlight 3 and download the source. Open Visual Studio and under the Interactivity folder create a folder called MagnifierOverBehavior. Into that folder copy the following files from the Behaviors and ShaderEffectsLibrary projects:

  • MagnifierOverBehavior.cs
  • EffectLibrary.cs
  • Magnifier.cs
  • Magnifier.fx
  • Magnifier.ps
  • ShaderEffectBase.cs

 

Include the files into your project.

I have spent little time researching effects (classes derived from System.Windows.Media.Effects.Effect), but here is an introduction. Silverlight 3 ships with two concrete effects (BlurEffect & DropShadowEffect) and one abstract effect (ShaderEffect). Any class that derives from UIElement has a single Effect property that can be set. To apply multiple effects, you need to use a hierarchy of UIElement-derived objects assigning the Effect property on different objects to get a composite effect. The pixel shader effect is software rendered in Silverlight and hardware rendered in WPF.

In our case, the Magnifier.cs file contains the Magnifier class that derives from ShaderEffectBase which derives from ShaderEffect which derives from Effect. A good article is Pixel Effects in Silverlight 3. The next file to look at is Magnifier.fx which contains code in High Level Shading Language (HLSL). The important thing to know is that the fx file must be compiled into a ps file. Since we already have Magnifier.ps then we don’t have to worry about it.

More details on HLSL, can be found at the following links:

The EffectLibrary.cs file contains helper method, MakePackUri, which is used in line 42 of Magnifer.cs. We need to change the string to identify the correct location in our project of the pixel shader file. Change “Source/Magnifier.ps” to “Interactivity/MagnifierOverBehavior/Magnifier.ps” since the ps file resides in the MagnifierOverBehavior project folder under the Interactivty folder.

Compile and then open the solution in Blend 3.

We want the magnifier to show when over the main office picture. But if we attach the behavior to the image, then magnification will turn off when we mouse over one of the path objects. To solve this problem, we will group the office image and all paths into a canvas object which we will name magnifierCanvas. Now drag the MagnifierOverBehavior from the Assets panel to associate it with magnifierCanvas:

 

Run the game and now you will see that when mouse is over the image it shows a magnifier. You can click when in magnification mode and the items will be removed from the list:

 

We don’t want to always be in magnification mode, we need a way to toggle the magnifier on or off. We will tackle that in the next episode as we create an action that is able to set a property on an action or behavior that is associated to an object.

Hidden Object: Episode 4 – Adding Particles with a Behavior

September 17, 2009 in Hidden Object Game, Silverlight

In the previous episodes of Creating a Hidden Object Game in Silverlight 3 we spent all of our time in Blend. Now we are going to put on our developer hat and create our own custom behavior that will shoot particles when an item is clicked. This will give a nice visual effect that compliments marking the item off the list and the sound effect.

Click here to see a demo of what the game will look like after we finish this episode.

I want to make sure that I give tons of credit to Robby Ingebretsen from nerdplusart for his Silverlight Particle Generator. If you haven’t had a chance to play with it before (and I do mean play) then take a few minutes to do so.

The plan is to take the ParticleControl from that solution and wrap it in a ParticlesBehavior that will be triggered when the item’s path (ex: staplerPath) is clicked.

First we need to take a side-step into the differences between an action and a behavior and their relationship to triggers. We will start to see some patterns emerge. In the previous episodes we looked at two actions (ChangeProperyAction & PlaySoundAction) and noticed that an action specifies what will happen and to which object. When the action will occur is specified by a trigger. There are two main types of actions (TriggerAction & TargetedTriggerAction). The first allows us to attach to an object and invoke some code when a trigger is fired whereas the second does that and defines a target for the action that is different from the object that the action is associated with. Both of these actions can only be called by a single trigger. A behavior in some ways appears simpler than an action because there is no direct connection to a trigger and therefore no Invoke method to be called. All we get are overrides for OnAttached and OnDetaching. But behaviors are powerful, can hold state, and can do various things based on many triggers.

For a class diagram of the Action and Behavior base classes as well as some great sample behaviors, check out the Behaviors and Triggers in Silverlight 3 article on SilverlightShow.

Hint: We will use the MagnifierOverBehavior in the next episode.

Enough talking lets open Blend and get coding. In the Projects panel, create a folder called Interactivity and under it a folder called ParticlesBehavior. Interactivity seems to be the term Microsoft uses to talk about both actions and behaviors and by the time we are done with this tutorial we will have created both types. I am doing this in Blend, because it has a template to create a basic behavior.

Right-click the ParticlesBehavior folder, pick Add New Item, select Behavior, and enter the name: ParticlesBehavior.

Click OK to create the behavior.

After saving all the files, open the solution concurrently in Visual Studio 2008 so that you can switch back and forth between Visual Studio and Blend as needed.

Go to the Silverlight Particle Generator page and download the source code. Copy the following files into the Interactivity\ParticlesBehavior folder and include them in the project:

  • ParticleControl.xaml
  • ParticleControl.xaml.cs
  • LICENSE.TXT

The quick idea behind the ParticlesBehavior is that it will be attached to the LayoutRoot canvas. The behavior will handle the canvas’s MouseMove event and store the current mouse position. When the ParticlesBehavior is notified that it should show particles, it creates an instance of the ParticleControl and positions it by setting the OffsetX and OffsetY properties.

We start by changing the class declaration so that ParticlesBehavior inherits from Behavior<Canvas>. Now throughout code, AssociatedObject will be of type Canvas. This also means that this behavior can only be attached to objects of type Canvas. In the OnAttached method an event handler is created for the canvas’ MouseMove event whereas in the OnDetaching method the event handler is removed. When the mouse is moved, the current position is recorded.

public class ParticlesBehavior : Behavior<Canvas>
{
private Point currentMousePosition;

protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseMove += new MouseEventHandler(AssociatedObject_MouseMove);
}

protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseMove -= new MouseEventHandler(AssociatedObject_MouseMove);
}

void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
{
currentMousePosition = e.GetPosition(null);
}
}

Now we need a way to trigger when the control will be shown or hidden. This is achieved by adding a command to ParticlesBehavior called ShowParticles. In the constructor, instantiate a new ActionCommand and pass it an Action delegate. The method that you use can have no parameters or one parameter (of type object). Later when triggers are associated with this behavior you can set the value of the CommandParameter. If you have a method that passes an object parameter, you can get access to the parameter.

This command then creates and sets properties on the ParticleControl and adds it to the Children collection of LayoutRoot. Update the ParticlesBehavior class to include this code:

public class ParticlesBehavior : Behavior<Canvas>
{
    private Point currentMousePosition;

    public ParticlesBehavior()
    {
        this.ShowParticles = new ActionCommand(this.OnShowParticles);
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.MouseMove += new MouseEventHandler(AssociatedObject_MouseMove);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.MouseMove -= new MouseEventHandler(AssociatedObject_MouseMove);
    }

    void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
    {
        currentMousePosition = e.GetPosition(null);
    }

    public ICommand ShowParticles
    {
        get;
        private set;
    }

    private void OnShowParticles()
    {
        ParticleControl p = new ParticleControl();

        p.OffsetX = currentMousePosition.X;
        p.OffsetY = currentMousePosition.Y;

        AssociatedObject.Children.Add(p);
    }

}

If you compile the project, you will now see the ParticlesBehavior in the Assets panel. Drag and drop it on LayoutRoot. The properties panel will have a ShowParticles group created for the ShowParticles Command and an empty Triggers list:

Click the plus sign to the right of Triggers and pick EventTrigger and set the SourceName (staplerPath) and EventName (MouseLeftButtonDown):

What this is saying is that when the staplerPath object is clicked then it will call the ShowParticles command on this behavior. You could also specify a CommandParameter which you then could use if OnShowParticles had an object parameter defined. When you click the plus sign next to Triggers again, you can specify another EventTrigger to be called when idolPath is clicked. Repeat this for all the paths.

Run the game now and click on an item. You should see lots of particles flowing from that location. Find another object and click it and there will be a second stream of particles. The ParticleControl currently doesn’t have a way to stop the particles from flowing. We will take care of that in a minute. But let’s first try to figure out what is happening between the triggers and the behavior. The command defined on the behavior is an ICommand, it knows nothing about triggers. And triggers just know when something is going to happen. How does it know to call a command on the behavior? The answer is the InvokeCommandAction that is created behind the scenes. This can be seen in a diagram from the Expression Blend SDK User Guide:

It might be a good learning opportunity to intercept the trigger before it calls InvokeCommandAction and see it doing the work of connecting the trigger to the behavior. Let’s create a trigger that we can use and debug. In a new folder under Interactivity called DebugEventTrigger, create a class called DebugEventTrigger.cs:

public class DebugEventTrigger : System.Windows.Interactivity.EventTrigger
{
    public DebugEventTrigger() : base() {}

    public DebugEventTrigger(string eventName) : base(eventName) {}

    protected override void OnEvent(EventArgs eventArgs)
    {
        base.OnEvent(eventArgs);
    }
}

We derive the class from EventTrigger so we can use it just list the triggers we have already used. Compile the project and in Blend, add this trigger to the triggers collection on the ParticlesBehavior and select the SourceName and EventName. Switch back to Visual Studio and set a breakpoint in the OnEvent method. Run the app in debug mode from Visual Studio. When the breakpoint is hit, look at the Locals window and you will see that the trigger has a single action, InvokeCommandAction, the CommandName is “ShowParticles” and the AssociatedObject is ParticlesBehavior.

Back to the issue of the never-ending particles. To fix this, we need just one set of changes to ParticleControl. The UpdateParticles method is the heart of the control and is called each time the Silverlight rendering process renders a frame. It updates existing particles, creates new particles, and removes expired particles. The particles are created 10 at a time until the particles collection reaches the MaxParticleCount. But because particles are also being removed from this collection the result is a never-ending flow of particles. Change this.particles.Count to totalParticlesCreated and define it as a private field at the top of the class definition as type int. At the end of the SpawnParticle method, increment the totalParticlesCreated value by one. Now the control will create particles until MaxParticleCount is reach and stop.

We have a fully-functioning ParticlesBehavior.

The last thing I did was add the dependency properties from ParticleControl (StartColor, EndColor, Fuzziness, MaxParticleCount, OriginVariance, Speed, ParticleSize, ParticleSizeVariance, Life, and LifeVariance) to ParticlesBehavior and set the values for each created instance of the ParticleControl. This gives a nice design-time experience to configure the particles. I changed a few values to better time the particles with the length of the sound played when an item is clicked:

There is one final thing we need to do when an item is clicked. Right now you can click on an item multiple times and it will play a sound and generate particles each time. To solve this, we can use the RemoveElementAction. This action removes the targeted element from its parent control. Drag the RemoveElementAction onto the staplerPath object. Since the trigger defaults to EventTrigger and MouseLeftButtonDown and the TargetName defaults to the AssociatedObject then we don’t have to change anything:

When we click on the staplerPath then it will:

  • Change the opacity of its corresponding TextBlock (ChangePropertyAction)
  • Play a sound (PlaySoundAction)
  • Spray particles (ParticlesBehavior)
  • Be removed from its parent control (RemoveElementAction)

Download the source for this episode.

In our next episode, we will use a magnifer pixel shader effect wrapped in a behavior to allow the user to zoom in on portions of the image.

Hidden Object: Episode 3 – Marking Items off the List

September 17, 2009 in Hidden Object Game, Silverlight

In this episode of Creating a Hidden Object Game in Silverlight 3 we overlay the items in the image with paths and associate the ChangePropertyAction to change the Opacity of the TextBlock to indicate that the item has been found.

In order to detect when an item is clicked in the office image, we need a hotspot. A Path works great for this and odd-shaped items can be outlined using the Pen tool. Press the “P” key and the Pen tool will be selected:

Center the mouse cursor over the stapler and use the mouse wheel to zoom in. Now click various points to outline the shape of the stapler:

I started with a point in the bottom left corner and clicked a total of 7 points to outline the shape. When I got to point 5 in the top left, the path’s fill started obstructing my view of the rest of the object. Simply move over to the Appearance group and set the Opacity below 50%. Continue to draw the remaining points. When you get back to the first point, the mouse cursor will change to the pen with a loop indicating that clicking will close the path. Give the path a name (staplerPath) and set the Opacity to 0%. It is time to add the ChangePropertyAction to the path. On the Assets tab, click Behaviors and find the ChangePropertyAction.

 

 

Drag the action from the Assets tab to the Object tree and drop it on staplerPath:

 

 

This action is now associated with staplerPath object. Adding this action to our object tree caused two assemblies to be added to the References folder:

  • Microsoft.Expression.Interactions.dll
  • System.Windows.Interactivity.dll

Looking at the Properties panel you will see the properties for this action and notice that there is already an EventTrigger selected to fire when the staplerPath object gets a MouseLeftButtonDown event. That is exactly what we want. In the Common Properties group you will see TargetName and PropertyName. The TargetName is the name of the object that will have its property changed and PropertyName is the name of the property. Since we want the staplerText TextBlock to be dimmed when the staperPath object is clicked, then we set the TargetName to staplerText. An easy way to do that is to grab the bullseye (called the artboard element picker) and drag it to staplerText on either the canvas or the object tree. The Properties for this action should now look like this:

 

Press F5 to run the project and click on the stapler. The text for stapler is now dimmed.

 

You can play with the Animation Properties by setting duration for the property change and even set an easing value if you don’t want a linear change in the Opacity from 100% to 40%. Let’s make this even better by adding a sound effect when an item is clicked.  I found a nice magic wand sound on SoundBible.com and added it to the project in an Audio folder. Now drag the PlaySoundAction from the Assets list onto staplerPath and set the Properties to use the audio file:

 

 

Now repeat this same process for the other 12 items. We have the core of our very own hidden object game!

 

In our next episode, we will write a custom behavior that will shoot a spray of particles when an item is clicked.

Hidden Object: Episode 2 – Create the Item list

September 17, 2009 in Hidden Object Game, Silverlight

This is the second episode in the series: Creating a Hidden Object Game in Silverlight 3

In this episode, we will add the image for the item list and create a wrap panel with a text blocks for each item in the list. If you have not already installed the Silverlight Toolkit, you will need to download and install it. This project is using the Silverlight 3 Toolkit July 2009 Installer.

I found an image of a whiteboard and did some cropping, removed the background, and saved it as a PNG. This was added to the Images folder.

Drag the image to the bottom center of the canvas and resize it to 328×205 and position it at 202, 395.

Now it is time to add the WrapPanel and position it on top of the whiteboard image:

  • Go to the Assets tab
  • Select Controls
  • Scroll down until you find the WrapPanel control and select it

Now you can draw the WrapPanel over the whiteboard image. If you go to the Projects tab and open the References folder you will notice that the following references have been added:

  • System.Windows.Controls.dll
  • System.Windows.Controls.Toolkit.dll

Before we start adding TextBlock controls to the WrapPanel, lets create a style called WhiteboardText and add it to the Application.Resources section of App.xaml:

<Style x:Key="WhiteboardText" TargetType="TextBlock">
 <Setter Property="Foreground" Value="#FFB11111"/>
 <Setter Property="FontFamily" Value="Comic Sans MS"/>
 <Setter Property="FontSize" Value="16"/>
 <Setter Property="Margin" Value="10,15,10,0"/>
</Style>

To keep the Image and WrapPanel together, select both in the tree and from the context menu pick Group Into > Canvas:

Select the WrapPanel in the tree, select the TextBlock tool in the Tools panel and double-click the canvas. This will add a TextBlock control as a child of the WrapPanel. Make sure the Width and Height properties are set to Auto. In the Miscellaneous group click the peg to the right of the textbox and pick Local Resources > WhiteboardText. We will now copy that TextBlock and paste it 12 more times so that the WrapPanel has a total of 13 items. For each TextBlock, change the Text and Name properties based on this list of items:

  • Stapler
  • Idol
  • Candies
  • Ball
  • Tape
  • Eeyore
  • Pencil
  • Tigger
  • Business Cards
  • Learning WCF
  • Rock
  • Dog
  • Post Its

So for the first TextBlock the Text is “Stapler” and the Name is staplerText. When I copied and pasted the TextBlock in the tree, it added a Margin property to the 12 items pasted. Since that property is contained in WhiteboardText I did a quick manual clean-up of the xaml. The hidden object game now looks like this:

In the next episode, we will use the ChangePropertyAction to mark items off the list when we select them as well as the PlaySoundAction.

Hidden Object: Episode 1 – Create the Project & Picture

September 17, 2009 in Hidden Object Game, Silverlight

This is the first episode in the series: Creating a Hidden Object Game in Silverlight 3

In this episode, we will get the project started and layout the UI.

Before we begin, make sure that you have the following installed:

  • Silverlight 3 Tools for Visual Studio 2008 SP1
  • Blend 3
  • Silverlight Toolkit
  • Expression Blend 3 SDK

The first 3 can be downloaded from the Get Started tab at Silverlight.net. You can download the Expression Blend 3 SDK from here.

First we launch Blend 3 and create a new Silverlight project:

The project is called ClutteredCube as that will be the main image for the hidden object game. The two things that we are going to do is change the LayoutRoot so that its LayoutType is Canvas and change the UserControl’s dimension to 800×600:

One day I brought a digital camera to work and took a photo of my cube. This is really how it looks with just a few items added to make the game more interesting:

You can use your own images as you follow this tutorial or download these.

The image has been resized and cropped to 800×500. Create an Images folder in the project and add the file to it.

Now drag-n-drop the office image onto the canvas and position it at the top. Select the LayoutRoot again, select Background under Brushes and use the color eyedropper to pick a dark color from the shelf or drawer. The page should now look like this:

In the next episode, we will create the list of items.

Creating a Hidden Object Game in Silverlight 3

September 16, 2009 in Hidden Object Game, Silverlight

According to the Casual Games Association, there are over 200 million casual game players worldwide.  One genre of casual game is the hidden object game. Mystery Case Files: Return to Ravenhearst by Big Fish Games is one example.  You are presented with a picture full of images and a list of items you must find.  As you click on the item in the picture, it is crossed off the list.  When you find the last item, you progress in the story to the next location or puzzle.

In this series, we will create a hidden object game using Silverlight 3 and behaviors, actions, and triggers.