Updated ViewModelBase for my WPF Projects

When I’m working on projects that use WPF, there’s a set of base classes that I usually use for classes that are bindable to the UI. Back in 2016 I shared the code for these classes. In 2018, I shared an updated version of these classes for UWP. I have another updated version of these classes that I’m sharing in part because it is used in some code for a future post.

For those unfamiliar, the WPF derived Windows UI technologies support binding UI elements to code classes so that the UI is automatically updated with a value in the class changes. Classes that are bound to the UI must implement the INotifyPropertyChanged interface. This interface defines a single event of type PropertyChangedEventHandler. This event is raised to indicate that a property on the class has changed. The event data contains the name of the class member that has changed. UI elements bound to that member get updated in response.

The class that I’ve defined, ViewModelBase, eases management of that event. The PropertyChanged event expects the name of the property to be passed as a string. If the name of the property is passed directly as a string in code, there’s opportunity for bugs from mistyping the property name. Or, if a property name is changed, there is the possibility that a string value is not appropriately updated. To reduce the possibility of such bugs occurring the ViewModelBase that I’m using uses Expressions instead of strings. With a bit of reflection, I extract the name of the field/property referenced, which gaurantees that only valid values are used. If something invalid is passed it would result in failure at compile time. Using an expression allows Intellisense to be used which reduces the chance of typos. If a field or property name is changed through the VisualStudio IDE, these expressions are recognized too. While I did this in previous versions of this classes, it was necessary to make some updates to how it works based on changes in .Net.

Additionally, this version of the class can be used in code with multiple threads. It uses a Dispatcher to ensure that the PropertyChanged events are routed to the UI thread before being raised.

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public ViewModelBase()
    {
        this.Dispatcher = Dispatcher.CurrentDispatcher;
    }
    [IgnoreDataMember]
    [JsonIgnore]
    public Dispatcher Dispatcher { get; set; }

    protected void DoOnMain(Action a)
    {
        Dispatcher.Invoke(a);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
        {
            Action a = () => { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); };
            if (Dispatcher != null)
            {
                Dispatcher.Invoke(a);
            }
            else
            {
                a();
            }
        }
    }

    protected void OnPropertyChanged(Expression expression)
    {
        OnPropertyChanged(((MemberExpression)expression).Member.Name);
    }

    protected bool SetValueIfChanged<T>(Expression<Func<T>> propertyExpression, Expression<Func<T>> fieldExpression, object value)
    {
        var property = (PropertyInfo)((MemberExpression)propertyExpression.Body).Member;
        var field = (FieldInfo)((MemberExpression)fieldExpression.Body).Member;
        return SetValueIfChanged(property, field, value);
    }

    protected bool SetValueIfChanged(PropertyInfo pi, FieldInfo fi, object value)
    {
        var currentValue = pi.GetValue(this, new object[] { });
        if ((currentValue == null && value == null) || (currentValue != null && currentValue.Equals(value)))
            return false;
        fi.SetValue(this, value);
        OnPropertyChanged(pi.Name);
        return true;
    }
}

Commands are classes that implement the ICommand interface. ICommand objects are bound to UI elements such as buttons and used for invoking code. This interfaces defines three members. There is an event named CanExecuteChange and two methods, CanExecuteChange() and Execute(). The Execute() method contains the code that is meant to be invoked when the user clicks on the button. CanExecute() returns true or false to indicate whether the command should be presently invokable. You may want a command to not be invokable until the user has satisfied certain conditions. For example, on a UI that uploads a file might want the upload button disabled until the user selected a file, and that file is a valid file. For that scenario, your implementation of CanExecute() would check if a file had been selected and if the file is valid and only return true if both conditions are satisfied. Or, if the command started a long running task for which only one instance is allowed, the CanExecute() function may only return true if there are now instances of the task running.

In this code, I’ve got a DelegateCommand class defined. This class has been around in its current form for longer than I remember. I believe it was originally in something that Microsoft provided, and it hasn’t changed much. To facilitate creating objects that contain the code, the DelegateCommand class acceptions an action (an action is a function that can be easily passed around as an object). The constructor for the class accepts either just an action to execute, or an action and a function used by CanExecute() (if no function is passed for CanExecute() it is assumed that the command can always be execute.

There are two versions of the DelegateCommand class. One is a Generic implementation used when the execute command must also accept a typed parameter. The non-generic implementation receives an Object parameter.

    public class DelegateCommand : ICommand
    {
        public DelegateCommand(Action execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action execute, Func<object, bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute != null)
                return _canExecute(parameter);
            return true;
        }

        public void Execute(object parameter)
        {
            _execute();
        }

        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }

        public event EventHandler CanExecuteChanged;

        private Action _execute;
        private Func<object, bool> _canExecute;
    }


    public class DelegateCommand<T> : ICommand
    {
        public DelegateCommand(Action<T> execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute != null)
            {

                return _canExecute((T)parameter);
            }

            return true;
        }

        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }

        public event EventHandler CanExecuteChanged;

        private Action<T> _execute;
        private Func<T, bool> _canExecute;
    }

By itself, this code might not be meaningful. But I’ll be referencing it in future content, starting with a post scheduled for a couple of weeks after this one.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Mastering Windows Presentation Foundation

3 thoughts on “Updated ViewModelBase for my WPF Projects

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.