Coming back from a long period of silence, I'm posting a little piece of code that I used in a project of mine. Usually, I start by the problem that I was facing and them I move to the solution. This time, I'll try another path.
TrackableValue<T> - a very short description
TrackableValue is a generic class that encapsulates a "value" and provides an event that will fire every time this "value" is changed.
Inspiration
When I came with the basic idea of TrackableValue, I was learning WPF. More precisely, I was trying to get a grip on Dependency Properties. This is a very complex concept to get used to straightaway. To be short, they are at the base of several of the main advantages of WPF. Stuff like data biding, animations, themes, styles, property inheritance through WPF trees, and others are only possible because of dependency properies.
At a time, I saw the advantage of creating a lighter version of these super properties. There were 3 requirements that I was seeking to fulfill. One, it must not be dependant on the WPF assemblies or any other UI framework. Two, it must be trackable for changes. Three, it must use some kind of weak events (more on this later). Obviously, the code that I'm posting fulfills these requirements.
Motivation
One advantage of a value being trackable is that you can make a one-direction data binding. In a Model-view-controller-based application, for example, binding between a model and a view is almost imperative. UI and binding are too often seen working together. Most UI frameworks already have support to binding and I don't intend to substitute that. I didn't want my component to be dependent on any UI. Also, it shouldn't be directly associated to binding. Data binding is just one possible usage of it.
My purpose was simply to facilitate the task of creating solid value-changed events. Putting code on the setter method of a regular CLR property is a simple and very straightforward way to track changes on a property. So, what is the problem with this approach? The problem is that it does not favor composition. I'll explain.
When you want to track changes from outside the class that owns such property, things start to get complicated. Generally, it's possible to make a subclass that overrides the property, but it's not a good policy to let it be the only way of extending your application. Essentially, I'm repeating the mantra "favor object composition over class inheritance". I think TrackableValue is a fine attempt accomplish this. Also, it has the advantage of using weak events, which I'll explain later.
Class "interface"
I was never very fond of UML, so I prefer to show a class structure in pseudo-code than using a class diagram. Here's an interface-like description of TrackableValue:
public sealed class TrackableValue<T>
{
/// <summary>Constructor</summary>
/// <param name="owner">The object that 'owns' the value, if any</param>
public TrackableValue(object owner);
/// <summary>This is the actual value</summary>
public T Value { get; set; }
/// <summary>This is the event that will be raised every time a new value is set</summary>
public event EventHandler<ValueChangedEventArgs<T>> ValueChanged { add; remove; }
/// <summary>This property is only used to set the 'sender' parameter to the event handler</summary>
public object Owner { get; }
}
Now, an example. The code below is a simple class with a trackable property using a TrackableValue.
public class ClassWithTrackablePropery
{
private readonly TrackableValue<double?> innerValue;
public ClassWithTrackablePropery()
{
innerValue = new TrackableValue<double?>(this);
}
public double? Value
{
get { return innerValue.Value; }
set { innerValue.Value = value; }
}
public event EventHandler<ValueChangedEventArgs<double?>> ValueChanged
{
add { innerValue.ValueChanged += value; }
remove { innerValue.ValueChanged -= value; }
}
}
There isn't much to explain about it, but I would like to make a few remarks. First, observe that I've set the innerValue member field to readonly. That's because the actual instance of TrackableValue should not change for the lifetime of the instance of ClassWithTrackablePropery. Second, when instantiating the TrackableValue, I'm setting its owner to this. This way, the sender parameter sent to event handlers will be the ClassWithTrackablePropery object that owns the value that changed. This might be useful in a code that uses this class.
Weak Events
Last but not least, I would like to introduce the notion of weak events. Right now, you might think that my code is too simple and didn't even deserve to be posted here. I wouldn't blame you to have this opinion. I tell you that the really sexy part of TrackableValue is the use of weak events. Before showing what they are (if you don't know already), I'll bring out a situation.
Suppose you have three components in an application: MyView, MyBackEndService, and MyController. MyController controls the application. It manages intances of MyBackEndService and MyView. MyBackEndService communicates with some external application and may change its state asynchronously. MyView is an windows forms window that shows the state of a MyBackEndService and lets the user execute some commands on it.
Now, let's say that MyView signs up for an event on MyBackEndService in order to display state changes in the UI. This will create a strong reference in MyBackEndService to MyView. If the user closes the MyView window, it should dispose itself and be garbage collected later. Suppose MyView doesn't detach from the event on MyBackEndService, the reference from MyBackEndService to MyView would still exists. This would make MyView never to be garbage collected until MyBackEndService is disposed! How to solve this problem? Who will save us? Weak Events!
Basically, the difference between a regular event and an weak event is that the former keeps strong references to the event handler delegates, while the latter uses WeakReference. This solves the problem that I've described beautifully.
WPF uses WeakEvent Patterns for most of its event system. As I said, I didn't want to make TrackableValue dependable on WPF. Fortunately, there is an excellent alternative. I used Weak Events from Daniel Grunwald. I strongly suggest reading his article if you are interested in the subject. I wouldn't be able to explain it better, so I won't try.
Conclusion
Although the code that I'm sharing here seems pretty neat to me, I have some questions about it. For example, should it really be a class, or should it be a struct instead? Also, is there a better way to control the sender parameter in the event handler (instead of using the Owner property)? What about a PreviewValueChanged event where some user code would be able to validate the change and, possibly, cancel it? If you have an opinion on these questions, I would be very happy to hear it.
Finally, here is the download link for the source code (v1.1 @ 2009.08.03). The zip file contains the WeakEvents source and, as usual, a demo project.
Have a nice day!
Updated at 2008.03.03: Source code link updated.