Introduction
There are times when it makes sense to bind to a Singleton in a WPF MVVM design. Binding to a Singleton can be done at that same time as Binding
through a Control
DataContext
. One of the factors in using the concept presented in this article is if the values in the Singleton could change, and these changes would need to be propagated.
One reason to use a Singleton is because of the size of the data, and this data could be shared. If the data is very large, it may make sense to use a weak reference, and this article shows a simple way to implement a weak reference for a singleton.
The Simple Binding Singleton
The following the code from the sample for the Singleton class
:
public class Lookups1 : INotifyPropertyChanged
{
private static Lookups1 _instance;
private string _staticString1;
private string _staticString2;
public static Lookups1 Instance => _instance ?? (_instance = new Lookups1());
public string StaticString1
{
get => _staticString1;
set
{
_staticString1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StaticString1)));
}
}
public string StaticString2
{
get => _staticString2;
set
{
_staticString2 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StaticString2)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The static
part of this code is the is the Instance
property which returns an instance of the class
. The instance for the class
is stored in the field _instance
. When the Instance
property is first accessed, the _instance
field will be null
, but this will cause the "??
" operator to initialize the _instance
field with a new instance of Lookups
.
There are two non-static properties in the Lookups
class. They are identical simple string
properties, but are used with different UpdateSourceTrigger
values. They both have a backing field, and they both fire the PropertyChanged
event
when changed. Both demonstrate how to implement a property in the instance class
that will tell anything bound to them that there has been a changed, and to update their values, which is exactly what could be implemented for a property in a ViewModel
that would be in a Control
DataContext
and needs to indicate to the View
that the property has been changed. The implementation is the simplest that can be implemented but is not the recommended way to implement a property since no checking is done as to whether the property has actually been changed, and a local instance of the event is not created, and used--It could be between when the OnPropertyChanged
is checked and used it could reset.
Binding
To use this Singleton, a sample of Binding
the XAML is as follows:
<TextBox Width="400"
Margin="2,0,2,2"
Text="{Binding Source={x:Static local:Lookups1.Instance},
Path=StaticString2,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Width="400"
Margin="2"
Text="{Binding Source={x:Static local:Lookups1.Instance},
Path=StaticString2}" />
I have two examples here, taken from the sample, one is on the Text
property on a TextBox
, which will be two-ways, and the other is one the Text
property of a TextBlock
, which will be OneWay
. The Binding
Source
argument points the Binding
to the static
part of the class
Lookups1
, and its property Instance
. The Path
argument points to the property on the instance of the Lookups1 class. The Binding
to TextBox
uses the UpdateSourceTrigger
value set to PropertyChanged
so that every change in the value of the Text
causes an update in the value of in the items bound to it. Normally the update will only occur when focus leaves the Control
.
WeakReference Static
In this second tab of the sample different bindings to a static with only a getter that in this case is an items source. In addition, a WeakReference
is used.
I have never used weak references, and since I have pretty much only worked front end, there is little need to actually use weak references, but as I was working on this article, I thought that it might be a good place to use a weak reference (but obviously for this sample the size of the class with only a small collection of strings does not make sense). I could have just use a direct Binding, but adding the WeakReference
really make much difference, and the WeakReference
could easily be replaced with a simple IEnumerable<string>
and a small amount of complexity would be removed.
I am using the same collection for all three types of static
Binding
I am using in the XAML:
public class Lookups2 : INotifyPropertyChanged
{
private static Lookups2 _instance;
private static WeakReference<ItemsClass> _namesWeakReference;
public static Lookups2 Instance => _instance ?? (_instance = new Lookups2());
public event PropertyChangedEventHandler PropertyChanged;
public static IEnumerable<string> Names
{
get
{
if ((_namesWeakReference == null)
|| !_namesWeakReference.TryGetTarget(out var itemsClass))
{
itemsClass = new ItemsClass();
_namesWeakReference = new WeakReference<ItemsClass>(itemsClass);
}
return itemsClass.Items;
}
}
public class ItemsClass
{
private readonly IEnumerable<string> _names;
public ItemsClass()
{
_names = new[]
{
"One",
"Two",
"Three",
"Four",
"Five"
};
}
public IEnumerable<string> Items => _names;
}
}
Because of the use of a WeakReference
, a class
(ItemClass
) had to be created and used as the Generic in the Generic WeakReference
. All that the ItemsClass
does is create and maintain an instance of an Array
of string
. In the property used for Binding
, the existence of the WeakReference
class
, and the existance of the Target
property of the WeakReference
is checked, and if either does not exist, then a new WeakReference
and a new ItemsClass
is created and returned.
The XAML
The following is the code using this class in static
bindings which are all on the second tab in the sample.
The first ComboBox
uses an x:Static
to bind the ItemsSource
to a static
property in the Lookup2
class
:
<ComboBox Name="ComboBox1"
Grid.Row="0"
Grid.Column="1"
Margin="2"
ItemsSource="{x:Static local:Lookups2.Names}" />
The second ComboBox
uses a standard Binding
with only a Source
using x:Static
to bind the ItemsSource
to a static
property in the Lookup2
class
:
<ComboBox Name="ComboBox2"
Grid.Row="0"
Grid.Column="1"
Margin="2"
ItemsSource="{Binding Source={x:Static local:Lookups2.Names}}" />
The third ComboBox
uses a standard Binding
x:Static
to bind the ItemsSource
to a static
Intance
property with a Path
specifying the Names
property if an instance of the class Lookup2
in the Lookup2
class
:
<ComboBox Name="ComboBox3"
Grid.Row="0"
Grid.Column="1"
Margin="2"
ItemsSource="{Binding Source={x:Static local:Lookups2.Instance},
Path=Names}" />
Dynamic Update
The last tab shows an implementation of a Singleton ItemsSource
that is dynamically updates. In this case the update is or language, which is changed with another Singleton ItemsSource
that is a selection from one of three languages: German, English, and French. The selection of language is provided by another Singleton. When the language is changed, then each of the records for the bound ItemsSource
properties is updated, in this case for the selected language. This is another excellent reason for using a Singleton.
The following is the class
for the static
ItemsSource
properties in the last tab:
public class Lookups3 : INotifyPropertyChanged
{
private int _language;
private static Lookups3 _instance;
public static Lookups3 Instance => _instance ?? (_instance = new Lookups3());
public Lookups3()
{
Names = new[]
{
new DictionaryAdapter<int, string>(1, "First", "Ersten", "Première"),
new DictionaryAdapter<int, string>(2, "Second", "Zweite","Deuxième"),
new DictionaryAdapter<int, string>(3, "Third","Dritten","Troisième"),
new DictionaryAdapter<int, string>(4, "Forth", "Her", "Suite"),
new DictionaryAdapter<int, string>(5, "Fifth", "Fünfte", "Cinquième"),
};
Langauges = new[]
{
new DictionaryAdapter<int, string>(0, "English", "Englisch", "Anglais"),
new DictionaryAdapter<int, string>(1, "German", "Deutsch", "Allemand"),
new DictionaryAdapter<int, string>(2, "French", "Französisch", "Français"),
};
}
public IEnumerable<DictionaryAdapter<int, string>> Names { get; }
public IEnumerable<DictionaryAdapter<int, string>> Langauges { get; }
public int Language
{
get => _language;
set
{
_language = value;
OnPropertyChanged();
Names.ToList().ForEach(i => i.UpdateLanguage(_language));
Langauges.ToList().ForEach(i => i.UpdateLanguage(_language));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Basically, it provides the properties for the ItemsSource
for the ComboBox
to make the selection and to select the language. There is also a property of Type
int
for selecting the language. When the language is changed, each of the records in both collections is updated so that the Value
property for each item is updated for the correct language.
History
- 04/25/2018: Initial version