I want to thank Michael for giving me the opportunity to share my experiences as I learn Silverlight.
When I’m trying to learn any new language, framework, or the like, I often find myself frustrated because blogs and tutorials obscure the simple concepts I’m trying to understand with too much detail. My goal is to start very simply so that the programming concepts are laid-out in small, easy-to-understand chunks of code. Then with each version to expand the use of those concepts and add in some very simple examples of a few new ones.
I never get to do it on the job, but my favorite kind of programming is to create intelligent objects that interact onscreen. My first Silverlight project in that line is The Seeker. The seeker reacts to an onscreen event by moving towards it. In this case the event is the drop part of a drag and drop operation.
The important concepts are:
- Publishing and consuming events
- Programmatically adding and positioning controls on screen
- Drag and drop
- Animation / Storyboard
- Internal Logic
- Calculating distance onscreen
The images below show the starting positions, the seekers on the way after the prey has been dragged and dropped, then all three seekers after reaching their destination. You can drag again while they’re en route and they will veer to follow.


I'll step through the different concepts roughly in the order they come into play as the program runs. Then I'll go into a little more detail on the boilerplate you have to provide to wire-up events.
Events:
I designed the seeker to becomes aware of changes in its environment through events. Every seeker subscribes to SightingEvent, so when one is raised, every seeker becomes aware of it with no-bookkeeping involved. Just wire up the initial event, make the event visible everywhere it needs to be available for subscription, and make one call whenever you need to raise the event. In The Seeker, every seeker is subscribed to the SightingEvent when the seeker is created. Actually, seeker1 is created in XAML. In MainPage_Loaded, you can see seeker1 being subscribed to SightedEvent, followed by creation of seeker2 and seeker3 as they are created, subscribed, and added to the screen.
Programmatically adding and positioning controls on screen
Very simple, you just have to know the steps.
1. Create the object which must derive from UserControl
SeekerClass.Seeker seeker3 = new SeekerClass.Seeker();
2. Add the new object to the controls
this.LayoutRoot.Children.Add(seeker3);
3. Set the position (note the coordinate argument is a double – very important)
seeker1.SetValue(Canvas.LeftProperty, 300.0);
seeker1.SetValue(Canvas.TopProperty, 400.0);
Drag and drop
To get things moving, drag and drop the green rectangle. I lifted the drag-and-drop code straight from MDSN.It appears right after MainPage_Loaded in MainPage.xaml.cs. It's pretty straightforward. All I'm adding is a real-world example of the code in action. Feel free to lift it from me, along with all the other code.
Animation / Storyboard
Now that the rectangle has been dropped, each seeker moves towards the drop point, which it received with the SightingEvent notification. To do make the move it uses a storyboard and animation. Again, not much explanation is required because the code in the Seeker.MoveTo() method is so straightforward. I feel free to say that because I didn't write it. It's a slightly modified version of the method 'Move', posted by sladapter at http://forums.silverlight.net/forums/t/21985.aspx. I found it when I was trying to figure out why SetValue(Canvas.LeftProperty) and the like weren't working. Turned out I was passing an integer when I needed to be passing a double. Good thing too, otherwise I wouldn't have stumbled on this little self-contained gem of a method to elegantly move an object from one point on the screen to another.
The reason I was trying to use SetValue(Canvas.LeftProperty), etc. was that I was going to do everything procedurally, calculating each step. Imagine everything involved in that and then look at the simplicity of the MoveTo method. And in sladpter's version, 'Move', you can pass in any FrameworkElement object and move it.
Internal Logic / UserControl
Notice that the decision to move towards the event and the resulting animation are internal to each seeker. It determines its own location and uses that to configure the resulting animation. Potentially each seeker could decide to do something different based on its own situation, such as distance to target. The UserControl class is ideal because it's a container for the graphic elements of the object along with the internal logic. So it has a virtual body and a ‘brain’ it can use to examine its own current environment and act accordingly.
Calculating distance onscreen
Actually there already is one decision that seeker makes based on distance to target. To move at a predictable speed it needs to calculate distance to target; distance is then used to determine the duration required to move at the desired speed.
To calculate the distance ... maybe you remember from geometry that the length of the hypotenuse of a triangle is equal to the square root of the sum of the square of the opposite two sides, so we can determine the straight line distance by taking the square root of the sum of the vertical distance squared and the horizontal distance squared, and maybe you don't. If you look at the code, you'll get the idea. This calculation and the code to calculate duration are pretty heavily commented in the HandleSightingEvent method in Seeker.xaml.cs if you're interested. You should be able to use it even if you don't understand it at first. I don't guarantee it's the most efficient, or even totally accurate - but it gives me a consistent way to control the speed and I can fine-tune it later.
Event Details / Boilerplate:
Publishing events is really pretty easy. It's the syntax that makes it difficult to keep things straight. I'm just going to describe what the various components do without getting into syntax too much.
- Define a custom EventArgs class if you want to pass details along with the event, see SightinEvent.cs
public class SightingEventArgs : EventArgs
The following elements of the event are typically coded in the sender of the event, in this case MainPage.
- Define the signature of a method required to subscribe to the event.
public delegate void SightingEventHandler(object sender, SightingEventArgs e)
- Define an event 'broadcaster' that sends events by making calls to methods that match the SightEventHandler signature.
public event SightingEventHandler SightingEvent;
- Add a subscriber to the event.
SightingEvent += this.seeker1.HandleSightingEvent;
- Broadcast the event to all subscribers, 'this' is the event sender, 'e' is an instance of SightingEventArgs. I'd prefer 'args', but 'e' is more standard.
SightingEvent(this, e);
If you don't get hung up on the semantics of the 'event' keyword it all pretty much makes sense; I think the event keyword would more logically be eventBroadcaster.
To consume the event, define a method on the subscriber that matches the signature defined by the SightingEventHandler delegate:
public void HandleSightingEvent(object sender, SightingEventArgs e)
That's it! When you want to broadcast a SightingEvent, create a SightingEventArgs object and call SightingEvent to send it out to all subscribers.
You can get the source here:
http://www.adefwebserver.com/Richard/The_Seeker.zip