Task-It Series
This post is part of a series of blog posts and videos about the Task-It (task management) application that I have been building with Silverlight 4 and Telerik's RadControls for Silverlight 4. For a full index of these resources, please go here. One of the posts listed in the index provides a full source download for the application, and I will be referring to that source code in this post.
Communicating between components
In any Silverlight application there are going to be times when you want to communicate between different components in your app. These could be UserControls, view model classes, whatever. To do this you need a framework for publishing events and subscribing to them. So basically, you fire off an event when something happens and 0 or more other components out there listen for that event, and respond when it is fired. This is known as the Observer Pattern, and is a basic need in any Silverlight application (i.e. if you're not using it you should be).
In an earlier version of Task-It I used Prism’s EventAggregator framework for this sort of event management, but ended up moving to the MVVM Light Toolkit’s implementation because it was simpler, and involved less code.OK, so let’s look at how to publish and subscribe to events using the MVVM Light Toolkit’s Messenger class (it’s actually quite simple). To distinguish one event from another you pass a unique object when it is fired, so let’s say we want to pass a Person object, we could simply fire the event like this (assuming that 'person' is an instance of a Person object):
Messenger.Default.Send(person);
And listen for it like this:
Messenger.Default.Register<
Person
>(this, OnPersonSelected);
It really is that simple.
Notice that the Person object will be passed as an argument to the OnPersonSelected method that was defined as the method I want to call when the subscriber receives the event, so I can then do whatever I want with that Person object in that method.
public void OnPersonSelected(Person person)
{
...
}
Task-It sample (firing the event)
OK, now back to our Task-it example. To implement the RadTreeView example mentioned above I created a class called EntityFilterArgs. I called it that because it is fired when an Entity object (TaskCategory, Project, Context or Contact) is selected in the tree view. It is then used to filter the data in the RadGridView to display only the tasks related to that entity. In hindsight, EntitySelectedArgs might have made more sense since I just mentioned a property grid example that wouldn’t really involve ‘filtering’, so I may do a little refactoring on that one. :-)First let’s take a look at the EntityFilterArgs class. Most of my event args classes live in EventArgs.cs in the TaskIt projects Core directory. I don’t usually put multiple classes in a single .cs file, but in this case the classes are all very small, so I’d rather not have a ton of .cs files.
public class EntityFilterArgs : EntityArgs
{
public long ID { get; set; }
}
public abstract class EntityArgs
{
public EntityType EntityType { get; set; }
}
public Entity SelectedItem
{
get { return _selectedItem; }
set
{
if (!IsEntity(value) || value == _selectedItem)
{
return; // Ignore left mouse clicks on top-level nodes
}
_selectedItem = value;
FireFilterEvent(value, value.GetType());
this.OnPropertyChanged(p => p.SelectedItem);
}
}
private static void FireFilterEvent(Entity entity, Type type)
{
var relationType = GetEntityType(type);
var info = new EntityFilterArgs {EntityType = relationType};
switch (relationType)
{
case EntityType.TaskCategory:
info.ID = ((TaskCategory) entity).ID;
break;
case EntityType.Project:
info.ID = ((Project) entity).ID;
break;
case EntityType.Context:
info.ID = ((Context) entity).ID;
break;
case EntityType.Contact:
info.ID = ((Contact) entity).ID;
break;
}
Messenger.Default.Send(info);
}
Messenger.Default.Register<
EntityFilterArgs
>(this, OnFilterByRelation);
So we can see that when it receives the event it is going to call a method called OnFilterByRelation. I won’t go into all the details of what this method does, but the main thing is that it stores the information that was passed in its SelectedEntityFilterArgs property, and calls LoadTasks. This will result in a call via WCF RIA Services to the database to get the appropriate data.
public void OnFilterByRelation(EntityFilterArgs args)
{
SelectedEntityFilterArgs = args;
IsInitialized = false;
InitializeSelections();
LoadTasks();
...
}