Skip to content

ViewModel: Observing classes

Raphael Winkler edited this page May 29, 2022 · 1 revision

If you need to observe classes or collections for changes, you can use the Tasty.ViewModel.Observables namespace! The Observer system detects if properties in classes change and also if all values have been changed back to their original value, and sets a boolean flag accordingly. This system can be especially useful if you're working on an editor and need to show the user that they have unsaved changes.

Implementation

To implement the observer system, extend your class from either "ObservableClass" or from "IObservableClass". The advantage of extending from "ObservableClass" is that everything is setup for you and you can immediately begin hooking up observers, but if required you can do it yourself quite easily with "IObservableClass".

First lets set up the class we wish to observe. If you extend from "ObservableClass" you can skip this step.

class SomeClass : IObservableClass
{
        // You can also set the access modifier to private
        protected ObserverManager observerManager = new ObserverManager();
        protected string guid;

        public ObserverManager ObserverManager => observerManager;

        public bool UnsavedChanges => observerManager.UnsavedChanges;

        public string Guid => guid;

        public virtual IObservableClass Copy()
        {
            return new ObservableClass(guid);
        }

        // Generate a new object of this type with a unique GUID. ObserverManager needs this GUID in order to differentiate between multiple items of the same type in a collection for example
        public SomeClass() : this(System.Guid.NewGuid().ToString())
        {
        }

        public SomeClass(string guid)
        {
            this.guid = guid;
        }
}

Now we only need to call the method "ObserveProperty(...)" inside our properties and we're good to go!

Keep in mind that the first time the method "ObserveProperty(...)" is called, the observer for the property is created. This means that changes only get reported after the Observer is registered!

Note that the example below assumes you're using the "ObservableClass" to extend from, but the steps for the "IObservableClass" interface are exactly the same

class SomeClass : ObservableClass
{
    private string name;
    private int ticketNumber;

    public string Name
    {
        get => name;
        set
        {
            // Call ObserverveProperty(...) before changing the value inside this class
            observerManager.ObserveProperty(value);
            name = value;
        }
    }

    public int TicketNumber
    {
        get => ticketNumber;
        set
        {
            // Call ObserverveProperty(...) before changing the value inside this class
            observerManager.ObserveProperty(value);
            ticketNumber = value;
        }
    }
}

Every time a property inside the class changes, (and is hooked with the method described above) the UnsavedChanges flag is set accordingly. If all properties return to their initial value, the flag is set back to false.

But what should we do if we save our data, this would mean the changed values are now our original values. This is where you would call "ResetObservers()".

class SomeClass : ObservableClass
{
    ...

    public void Save()
    {
        // Do stuff here
        observerManager.ResetObservers();
    }
}

Alternatively you can reset a specific observer by selecting it from the "ChangeObservers" property inside "ObserverManager":

class SomeClass : ObservableClass
{
    ...

    public void ResetTicketNumberObserver()
    {
        // Using LINQ, get the first observer whose PropertyName is "TicketNumber" and call "Reset()". If null returned, do nothing
        observerManager.ChangeObservers.FirstOrDefault(x => x.PropertyName == "TicketNumber")?.Reset();
    }
}

Next up: Bindable collections & observing changes