OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


This Seeker Behaves

Live example: http://www.adefwebserver.com/Richard/TheSeekerBehaviorv1Site/

Ok, Silverlight is starting to freak me out a little bit. I thought it would take a couple of days to figure out how to translate my Seeker behaviors into Silverlight behaviors. After going down several paths that had too many details about things I wasn’t interested in right now, I found Falling Snow in Silverlight on Kirupa.com and the scales were lifted from my eyes in about 15 minutes. I was afraid behavior would be restricted to some short-term action-reaction thingie but not at all. After reading Kirupa’s description of a behavior’s parasitic behavior and seeing the FallingSnowBehavior literally take over a Canvas control I became hopeful  I could write a Hunt behavior that could take over my Seeker control, or at least its reaction to a SightingEvent.

image

I modified Seeker Version 1 to try this out. First I modified the Seeker so the reaction to a SightingEvent is a call to the React method on an iSighting interface.

private iSighting _sightingLogic;

        public void HandleSightingEvent(object sender, SightingEventArgs e)
        {
            if (_sightingLogic != null)
                _sightingLogic.React(this, e.Position);
        }

 

This is old stuff from ooNaLife and Seeker 3, the Rise of ooBer. What’s totally cool is how easy it is to do this hookup through a behavior. It’s amazing to me that I can write classes to support different implementations of a particular interface and hook them up through a behavior in Blend, in XAML, or in code-behind. Just like any other behavior you can pass in parameters that allow the behavior to be customized. To demonstrate this, If you drag the rectangle and drop it, you’ll see that the three seekers all approach at different speeds. In this case, I’m using a single class, Sighting that I can customize through the speed variable, but there’s nothing to stop me from writing some completely different class – as long as it implements the iSighting interface, I can pass it to The Seeker by modifying this behavior to optionally use the new class or write a new behavior altogether – whichever makes sense under the circumstances.

I’ll tell you exactly how to write the behavior, but first I want to show you how easy it is to hook up the resulting behavior in Blend, XAML, and code-behind.

When I was writing the behavior, mostly lifted from M Kirupa, I wasn’t even thinking about Blend, but it was there right off, complete with speed parameter.

image

Here it is in XAML

<The_Seeker:Seeker x:Name="seeker1" 
Canvas.Left="24"
Canvas.Top="408" 
Caption="Seeker 1">
	<i:Interaction.Behaviors>
		<local1:Hunt/>
	</i:Interaction.Behaviors>
</The_Seeker:Seeker>

And in code-behind:

            Seeker seeker3 = new Seeker();
            seeker3.Caption = "Seeker 3";
            new Hunt(Hunt.speed.slow).Attach(seeker3);

 

Here’s the amazing part, the behavior itself.

namespace The_Seeker 
{
 
// Note that Behavior is specific to Seeker 
    public class Hunt : Behavior 
    {
 
// I added the speed enum and property here and they showed up in Blend with no further effort 
        public enum speed{normal, fast, slow}; 
 
        private speed _speed; 
 
        public speed Speed 
        { 
            get { return _speed; } 
            set { _speed = value; } 
        }
 
// This reference will be instantiated and attached to the Seeker 
        iSighting _sighting = null; 
 
// Default constructor sets speed to normal
 
        public Hunt() 
        { 
            Speed = speed.normal; 
            InstantiateSighting(); 
        } 
 
// This constructor takes a speed variable
 
        public Hunt(speed huntSpeed) 
        { 
            Speed = huntSpeed; 
            InstantiateSighting(); 
        } 
 
// Put an implementation of of iSighting behind the pointer that will be attached to Seeker
 
        private void InstantiateSighting() 
        { 
            switch (Speed) 
            { 
                case speed.slow: 
                    _sighting = new Sighting(30); 
                    break; 
                case speed.normal: 
                    _sighting = new Sighting(60); 
                    break; 
                case speed.fast: 
                    _sighting = new Sighting(100); 
                    break; 
            } 
        } 
 
        protected override void OnAttached() 
        { 
            base.OnAttached();
 
// You could probably handle everything here, but I prefer to wait until everything is loaded 
            this.AssociatedObject.Loaded+=new System.Windows.RoutedEventHandler(AssociatedObject_Loaded); 
        } 
 
        protected override void OnDetaching() 
        { 
            this.AssociatedObject.Loaded -= AssociatedObject_Loaded; 
            Seeker seeker = AssociatedObject as Seeker; 
            if (seeker == null) 
                return; 
            seeker.SightingLogic = null; 
            base.OnDetaching(); 
        }
 
        private void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e) 
        { 
            Seeker seeker = AssociatedObject as Seeker; 
            if (seeker == null) 
                return;
 
// Attach customized sighting logic to the Seeker instance attached to this behavior instance 
            seeker.SightingLogic = _sighting; 
        } 
    } 
}
 
 

It almost seems too easy, but there it is!!!

Download code:

http://www.adefwebserver.com/Richard/SeekerBehaviorv1.0.zip





Comments are closed.