Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

WPF MVVM View Binding to a Singleton and WeakReference

5.00/5 (13 votes)
25 Apr 2018CPOL5 min read 24.3K   283  
Concept to create a Singleton for WPF MVVM Binding, and using Weak reference for the property that is bound to.

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:

C#
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:

XML
                <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.

Image 1

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:

C#
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:

XML
<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:

XML
<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:

XML
<ComboBox Name="ComboBox3"
          Grid.Row="0"
          Grid.Column="1"
          Margin="2"
          ItemsSource="{Binding Source={x:Static local:Lookups2.Instance},
                                Path=Names}" />

Image 2

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.

Image 3

History

  • 04/25/2018: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)