You are browsing the archive for 2009 September.

Hidden Object: Episode 9 – Add the Splash, Menu & Options Screens

September 29, 2009 in Hidden Object Game, Silverlight

This is episode 9 of Creating a Hidden Object Game is Silverlight 3. In the last episode we added background music. Now we will create a screen to set the music volume. While we are at it, we will create the splash and menu screens.

When the game is finished, it will have a screen flow as follows:

HiddenObject-ScreenFlow

 

 Currently we have been working on the Game screen which in the project is a UserControl called MainPage.xaml. We are going to create the menu, options, and splash screens as Canvas objects that are off screen of MainPage and are at the bottom of the Objects tree (or top of the z-order) so they show up over all other controls. I chose to do this since I want the background music to play during all of these screens and the MediaElement on MainPage is the one that is playing it. There are various other ways to approach this.

Let’s open Blend, add 3 Canvas controls, and name them splashCanvas, menuCanvas, and optionsCanvas. Make each Canvas have a black background and position them on the artboard so that they are to the left of MainPage:

 

On splashCanvas, add 4 TextBlock controls and enter the title information:

 

On menuCanvas, add the page title in a TextBlock and 2 Buttons:

 

On optionsCanvas, add a TextBlock for the page title, a TextBlock for the slider title, a Slider control, and a Button:

Since the silder’s Value property will be used in a Binding for the MediaElement’s Volume property, we will name the slider as sliderMusicVolume.

 

To control the screen flow, we will use the States panel to create a ScreenStates group with the following states: GameScreen, SplashScreen, MenuScreen, OptionsScreen.

On the States tab, click the Add state group button:

Then next to the ScreenStates group, click the Add state button:

As I entered the following states, I didn’t worry about setting any properties for any of the controls and after adding OptionsScreen I just turned off recording.

The GameScreen state represents when the MainPage is visible which is how it currently is shown on the artboard. No change will need to be made for this state. Click on the SplashScreen state in the States tab to turn on recording mode. Then click splashCanvas and change its Top and Left properties both to 0. Turn off recording of the SplashScreen state.

Repeat for the MenuScreen and OptionsScreen states so that when one of these states is active, MainPage will be covered by menuCanvas or optionsCanvas.

Click on the Base state at the top of the States tab to make sure you are completely out of state editing mode.

We now need to add a TextBlock with the Text property set to “MENU” and add it to LayoutRoot in MainPage. Because I added this control after the Canvas controls for the other screens, I need to change its order in the Objects tree so that it doesn’t appear above those screens. We will worry about making the MENU TextBlock into a Button at a later time.

 

Now it is time to use the GoToStateAction to connect the screen flow. Drop the first GoToStateAction onto LayoutRoot and change the EventName to Loaded and the StateName to SplashScreen:

This will cause the splash screen to show first.

NOTE: While we are still in development, we can set the loaded StateName to GameScreen to reduce testing time.

 

Drop another GoToStateAction onto splashCanvas and change the StateName to MenuScreen. When the splash screen is clicked, we will advance to the menu screen:

Drop two more GoToStateAction actions onto the Start and Options buttons on menuCanvas. Change the StateNames to GameScreen and OptionsScreen respectively.

Start button:

 

Options button:

 

Drop another GoToStateAction onto the Close button in optionsCanvas and change the StateName to MenuScreen:

 

Finally, drop the last GoToStateAction onto the MENU TextBlock in MainPage and set the StateName to MenuScreen:

 

We have just put together the majority of our screen flow with 6 instances of GoToStateAction and no code!

 

The last thing we need to do is to connect the slider value to the volume of the background music. In the Objects tree, select the musicMediaElement and in the Properties pane, click the peg to the right of the Volume property and click Data Binding to open the Create Data Binding window:

 

Select the ElementProperty tab. In the Scene elements list, open optionsCanvas and select sliderMusicVolume. In the Properties list, select Value. Now when the slider in the options screen is changed, it will set the volume on the MediaElement playing the background music.

The last thing we need to do is set some values on the slider control. Select the control and change the following properties found in the Common Properties group: LargeChange, Maximum, and Value.

 

In the next episode, we will try to figure out a way to determine when all the objects have been found so we can show a Win screen.

Demo
Source code

Hidden Object: Episode 8 – Loop Game Music with a Behavior

September 28, 2009 in Hidden Object Game, Silverlight

In the last episode of Creating a Hidden Object Game is Silverlight 3 we finished off the magnifier feature. Now it is time to add some background music to the game and have it continually play.

First off, we need to find some appropriate music. I did a search for royalty-free & public domain music and came across a site by Derek R. Audette that contained such music. On the site, I found a piece of music called “Combustible Coffee Pot” written & performed by Derek R. Audette – ©MMV(Socan). Derek’s own description of the music is:

“Out of control drum fills, singing string sections and odd, tense, electronic pads make up this bizarre composition. Created to add a mood of intrigue to an independent film produced in 2005.”

Download the mp3 file and add it to the Audio folder.

In order to use the music in our game, we need to add a MediaElement control to LayoutRoot:

<MediaElement x:Name="musicElement"
    Source="/Audio/Combustible_Coffee_Pot.mp3"
    MediaEnded="musicElement_MediaEnded"/>

To get the continuous loop, we need to handle the MediaEnded event which sets the media position back to 0 and plays it again:

private void musicElement_MediaEnded(object sender, System.Windows.RoutedEventArgs e)
{
    musicElement.Position = new TimeSpan(0);
    musicElement.Play();
}

But as developers, we don’t want to make our designers go into code behind and enter C# code. Since Silverlight doesn’t support continuously lookping media, we are going to package this functionality into a custom behavior.

Create a folder under Interactivity called ContinuousMediaPlayBehavior and create new class based on the Behavior template by the same name. All we are going to do is attach an event handler to the MediaEnded event in the OnAttached method and remove the handler in the OnDetaching method. To get the AssociatedObject property to be strongly typed to a MediaElement and thus allow this behavior to only be attached to the MediaElement type we change the behavior so it is typed to MediaElement. When the MediaEnded event is fired, we set the position of the media back to 0 and replay it:

public class ContinuousPlayMediaBehavior : Behavior<MediaElement>
{
    public ContinuousPlayMediaBehavior() {}

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.MediaEnded += AssociatedObject_MediaEnded;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.MediaEnded -= AssociatedObject_MediaEnded;
    }

    void  AssociatedObject_MediaEnded(object sender, RoutedEventArgs e)
    {
        AssociatedObject.Position = new TimeSpan(0);
        AssociatedObject.Play();
    }
}

This is what the XAML looks like for this:

<MediaElement x:Name="musicMediaElement" Source="/Audio/Combustible_Coffee_Pot.mp3">
    <i:Interaction.Behaviors>
        <local:ContinuousPlayMediaBehavior/>
    </i:Interaction.Behaviors>
</MediaElement>

In the next episode, we will continue to work on the UI by adding various screens including one to control the volume of the background music we just added.

Showcase

September 26, 2009 in Showcase

Cluttered Cube - Hidden Object Game 

Cluttered Cube – Hidden Object Game (Work in progess)

Hidden Object: Episode 7 – Use an Action to Toggle the Magnifier Behavior

September 26, 2009 in Hidden Object Game, Silverlight

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

In the last few posts we have been working on the magnifier feature of the game which we will finish in this episode.

Please review the following posts:

First we will add in the following files into the ClutteredCube project:

The first thing we want to do is to change MagnifierOverBehavior so that it inherits from our custom behavior base class thus inheriting the IsEnabled property:

public class MagnifierOverBehavior : BaseBehavior<FrameworkElement>

The only other change we need to make this behavior is not set the Effect property on MouseEnter but instead in MouseMove based on the value of the IsEnabled property:

private void AssociatedObject_MouseEnter( object sender, MouseEventArgs e )
{
  this.AssociatedObject.MouseMove += new MouseEventHandler( AssociatedObject_MouseMove );
  //this.AssociatedObject.Effect = this.magnifier;
}

private void AssociatedObject_MouseMove( object sender, MouseEventArgs e )
{
  if (IsEnabled)
  {
    if (this.AssociatedObject.Effect != this.magnifier)
    {
      this.AssociatedObject.Effect = this.magnifier;
    }
    ...
  }
}

In Blend, open the MainPage and select the MagnifierOverBehavior in the Object tree under magnifierCanvas. Name the behavior magnifierBehavior and set IsEnabled to false:

From the Assets tab drag two instances of SetInteractionPropertyAction onto the CheckBox magnifier control:

On the first one, set the properties as follows:

  • EventName: Checked
  • TargetName: magnifierCanvas
  • ObjectName: magnifierBehavior
  • PropertyName: IsEnabled
  • Value: true

Set the second the same as the first, except:

  • EventName: Unchecked
  • Value: false

Make sure that the CheckBox control IsChecked property is false to match the InEnabled value of false for magniferBehavior.

Run the game and verify that magnification is only turned on when the CheckBox is checked.

Let’s do one more thing to finish this episode. Notice that magnifier.png has a white background behind the glass part of the magnifying glass. The image should be on top of something white like a notepad. Import the notepad image and drop it at the bottom of LayoutRoot.

notepad

Position it how you want it and move the magnifier CheckBox on top of it.

Source Code

Demo

There are many features to add so I will keep the next episode contents a surprise.

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.

Business Cards Observer

September 23, 2009 in Design Inspiration

Check out CardObserver.com.  How does your business card compare?

Here is one of many that I like:

abrakadabra2

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.