There are several reasons why you might want to use the singleton pattern in WPF. You often need an object that is globally accessible and you only need one instance of that object. A
static
class might do the job, but if you want to bind to the object and use it as a
StaticResource
, you need an instance.
There are several ways to bind to a singleton. The simplest solution is to use the
x:Static
syntax:
<TextBlock Text="{Binding SomeProperty, Source={x:Static local:MySingleton.Instance}}" />
This works fine, but there are 2 disadvantages. First, the syntax is too long and it looks ugly. Second, the reference to
MySingleton.Instance
appears in every single binding expression and if you decide to change your class later (or just rename it...), you will have a lot of work to do.
The solution is to store your singleton instance as a
StaticResource
. This will add one level of indirection, so if your class changes for some reason, you will only have to change the way the
StaticResource
is created without breaking all the bindings.
Now I'm getting to the point of this article. How can you use a singleton as a
StaticResource
? Singleton does not have any
public
constructor, so you can't do it the easy way. A common solution is to use
ObjectDataProvider
:
<Application.Resources>
<ObjectDataProvider x:Key="MyData" ObjectType="{x:Type local:MySingleton}" MethodName="GetInstance" />
</Application.Resources>
This works really fine. Binding to our singleton is now as simple as this:
<TextBlock Text="{Binding SomeProperty, Source={StaticResource MyData}}" />
It's fine, but it's still not perfect. The problem is that
ObjectDataProvider
is only able to access
public
methods, not properties, so we had to change the
static public Instance
property to a
static public GetInstance()
method. It's not a big problem, but I liked the property-based singleton better, because it's easier and more readable... What if there is an easy solution to use a property-based singleton instance as a
StaticResource
? Yes!
The
ObjectDataProvider
class is derived from
DataSourceProvider
. Let's derive a custom class from
DataSourceProvider
to enable the functionality we need. It's as simple as this:
public class PropertyDataProvider : DataSourceProvider {
private Type _objectType;
private string _propertyName;
public Type ObjectType {
get { return _objectType; }
set {
if (value == _objectType) return;
_objectType = value;
OnPropertyChanged(new PropertyChangedEventArgs("ObjectType"));
if (!base.IsRefreshDeferred) base.Refresh();
}
}
public string PropertyName {
get { return _propertyName; }
set {
if (value == _propertyName) return;
_propertyName = value;
OnPropertyChanged(new PropertyChangedEventArgs("PropertyName"));
if (!base.IsRefreshDeferred) base.Refresh();
}
}
protected override void BeginQuery() {
Exception error = null;
object result = null;
if (_objectType == null) {
error = new InvalidOperationException("ObjectType is not set.");
} else if (String.IsNullOrEmpty(_propertyName)) {
error = new InvalidOperationException("PropertyName is not set.");
} else {
PropertyInfo prop = _objectType.GetProperty(_propertyName, BindingFlags.Static | BindingFlags.Public);
if (prop == null) {
error = new MissingMemberException(_objectType.FullName, _propertyName);
} else {
try {
result = prop.GetValue(null, null);
} catch (MethodAccessException e) {
error = e;
} catch (TargetInvocationException e) {
error = e;
}
}
}
base.OnQueryFinished(result, error, null, null);
}
}
The class only has 2 properties and it overrides the
BeginQuery()
method. This method is a bit messy because it needs to handle exceptions in a specific way... It would look like the following without the exception-handling trash:
protected override void BeginQuery() {
object result = null;
PropertyInfo prop = _objectType.GetProperty(_propertyName, BindingFlags.Static | BindingFlags.Public);
result = prop.GetValue(null, null);
base.OnQueryFinished(result, null, null, null);
}
(Note: The code above is more readable, but it's NOT correct!)
You can now create a
StaticResource
from a singleton the following way:
<Application.Resources>
<local:PropertyDataProvider x:Key="MyData" PropertyName="Instance" ObjectType="{x:Type local:MySingleton}" />
</Application.Resources>
To sum this up, the class I introduced in this article might seem pretty useless, but if you want to bind to a singleton and you prefer the
Instance
property syntax over the
GetInstance()
method syntax, now you know there is a way to do it... It's just a syntactic sugar, but it's so easy to use that it's worth the effort.