Skip to content

ViewModel

Niclas Kristek edited this page Aug 21, 2019 · 6 revisions

The ViewModel abstract class provides a base implementation for a viewmodel in the MVVM architecture. It implements the INotifyPropertyChanging, INotifyPropertyChanged and INotifyDataErrorInfo interfaces by inheriting from ValidatingBindable and has some additional features.

IsDirty

ViewModel implements an IsDirty property which is initially false. If the PropertyChanged event handler is raised and the IsDirtyIgnoredAttribute is not defined on the property (or on notified properties via PropertySourceAttribute), IsDirty will be set to true.

private bool _myProperty;

[IsDirtyIgnored]
public bool MyProperty
{
    get => _myProperty;
    set => SetProperty(ref _myProperty, value);
}

In this example, when MyProperty changes, IsDirty will not be automatically set to true.

Also, if the property implements INotifyCollectionChanged and no IsDirtyIgnoredAttribute exists, the ViewModel will attach itself to the CollectionChanged event handler and set IsDirty to true every time it's raised. When the collection property has a setter, please use the SetProperty<T> method to properly detach and attach from the event handler.

Parent

ViewModel implements a Parent property which internally uses a WeakReference<T> to prevent reference cycles.

IsReadOnly

ViewModel implements an IsReadOnly property. If set to true, the SetProperty<T> methods will no longer set any properties or raise events on the PropertyChanging and PropertyChanged event handlers.

An exception is the IsReadOnly property itself or any property with the IsReadOnlyAttribute.

IsUpdating

ViewModel implements an IsUpdating property. This property can be used to indicate if the instance is currently being updated.

IsValid

The IsValid property is a convenient way to, for example, bind to the IsEnabled state of a button without the use of a converter to invert the value of HasErrors.

PropertySourceAttribute

The PropertySourceAttribute on a property indicates that the property depends on other properties and that they should be reevaluated, if any of the named properties change. Thus, the NotifyPropertyChanging and NotifyPropertyChanged methods will automatically raise additional events when an event is raised for one of named properties.

Attributes when a property is overriden

When a property is overriden in a subclass, attributes are not inherited by default. As the overriden property most probably has different dependecies, you should redeclare the PropertySourceAttribute with the new dependencies.

public class Base
{
    [IsDirtyIgnored]
    public bool TestProperty { get; }

    [PropertySource(nameof(TestProperty))]
    public virtual bool AnotherTestProperty => TestProperty;
}

public class Derived : Base
{
    public override bool AnotherTestProperty => true;
}

In that example, when using Derived, only the IsDirtyIgnoredAttribute from TestProperty is processed since this property is not overridden. No event on PropertyChanged will be raised for AnotherTestProperty when TestProperty changes.

If you access the base implementation in the override, you should define a PropertySourceAttribute with the InheritAttributes option set to true. In that case you can also define additional sources.

public class Base
{
    public bool TestProperty { get; }

    [PropertySource(nameof(TestProperty))]
    public virtual bool AnotherTestProperty => TestProperty;
}

class Derived : Base
{
    public bool NewTestProperty { get; }

    [PropertySource(nameof(NewTestProperty), InheritAttributes = true)]
    public override bool AnotherTestProperty => base.AnotherTestProperty && NewTestProperty;
}