ModelBase and inheritance

May 31, 2011 at 9:26 AM

Is there a way to use the ModelBase class on model classes which derive from other model base classes? I have something like this:


abstract class AbstractMember : ModelBase<AbstractMember>
{
  string _name;
  public string Name
  {
    get { return _name; }
    set
    {
      _name = value;
      NotifyPropertyChanged(m => m.Name);
    }
  }
}

class Group : AbstractMember
{
  IPAddress _multicastAddress;
  public IPAddress MulticastAddress
  {
    get { return _multicastAddress; }
    set
    {
      _multicastAddress = value;
      NotifyPropertyChanged(m => m.MulticastAddress);
    }
  }
}

The call to NotifyPropertyChanged from Group.set_MulticastAddress cannot be compiled.

What's the recommended way to solve this?

Coordinator
May 31, 2011 at 3:50 PM

There is a class in the SimpleMvvmToolkit called BindingHelper, that exposes a static NotifyPropertyChanged method. Unfortunately, the class is internal in the current release of the toolkit (I will mark it as public in a future release).  Here is an implementation of that class with NotifyPropertyChanged implemented as an extension method, which makes it easier to use.

using System;
using SimpleMvvmToolkit;
using System.Linq.Expressions;
using System.ComponentModel;

namespace SimpleMvvmExtensions
{
    internal static class BindingHelper
    {
        // Defined as virtual so you can override if you wish
        public static void NotifyPropertyChanged<TModel, TResult>
            (this TModel model, Expression<Func<TModel, TResult>> property,
            PropertyChangedEventHandler propertyChanged)
        {
            // Convert expression to a property name
            string propertyName = ((MemberExpression)property.Body).Member.Name;

            // Fire notify property changed event
            InternalNotifyPropertyChanged(propertyName, model, propertyChanged);
        }

        public static void InternalNotifyPropertyChanged(string propertyName,
            object sender, PropertyChangedEventHandler propertyChanged)
        {
            if (propertyChanged != null)
            {
                // Always fire the event on the UI thread
                if (UIDispatcher.Current.CheckAccess())
                {
                    propertyChanged(sender, new PropertyChangedEventArgs(propertyName));

                }
                else
                {
                    Action action = () => propertyChanged
                        (sender, new PropertyChangedEventArgs(propertyName));
                    UIDispatcher.Current.BeginInvoke(action);
                }
            }
        }
    }
}

With this class in your project, simply invoke the NotifyPropertyChanged method from your Group class:

class Group : AbstractMember
{
    IPAddress _multicastAddress;
    public IPAddress MulticastAddress
    {
        get { return _multicastAddress; }
        set
        {
            _multicastAddress = value;
            this.NotifyPropertyChanged(m => m.MulticastAddress, propertyChanged);
        }
    }
}

The ModelBase class is usually not needed when consuming a WCF service, because generated classes already implement INotifyPropertyChanged. But it is possible, even with WCF, that you would want to use your own classes, which may be configured in an inheritance hierarchy.  I'll be sure to modify the BindingHelper class in the toolkit to support this scenario.

Cheers,

Tony

May 31, 2011 at 4:39 PM

Hi Tony,

thanks a million for the extension method. Works like a charm. Hope you will include that in the next release and make the BindingHelper class public.

Strange how you have to explicitly specify "this." to help the C# compiler find the extension method.

Regards,

Chris

Coordinator
Jun 3, 2011 at 5:30 PM

I uploaded a new version of the source code that incorporates this extension method: http://simplemvvmtoolkit.codeplex.com/SourceControl/changeset/changes/11329.  It will be part of the next release as well.

Tony

Jan 4, 2015 at 11:15 PM
Hate top bring up this old thread but i cannot get this to work.
    public abstract class Encounter : ModelBase<Encounter> {

        private string _id;
        public string ID {
            get { return _id; }
            set {
                _id = value;
                NotifyPropertyChanged(m => m.ID);
            }
        }

        public Encounter() { }
    }

    public class DialogEncounter : Encounter {

        private ImpObservableCollection<Node> _nodes = new ImpObservableCollection<Node>();
        public ImpObservableCollection<Node> Nodes {
            get { return _nodes; }
            set {
                _nodes = value;
                this.NotifyPropertyChanged(m => m.Nodes, propertyChangedField);
            }
        }

        private ImpObservableCollection<Connection> _connections = new ImpObservableCollection<Connection>();
        public ImpObservableCollection<Connection> Connections {
            get { return _connections; }
            set {
                _connections = value;
                this.NotifyPropertyChanged(m => m.Connections, propertyChangedField);
            }
        }

        public DialogEncounter() {
            ID = "New Dialog";

        }


    }
            this.NotifyPropertyChanged(m => m.Nodes, propertyChangedField);
            this.NotifyPropertyChanged(m => m.Connections, propertyChangedField);
proprtyChanged did not exist, but propertyChangedField did, problem being that it still has issues. Do you have an example using the latest version?
Coordinator
Jan 5, 2015 at 7:44 AM
A third parameter was added to NotifyPropertyChanged in a later release in order to support thread dispatching across all platforms. This syntax should work:
public class DialogEncounter : Encounter
{
    private ObservableCollection<Node> _nodes;
    public ObservableCollection<Node> Nodes
    {
        get { return _nodes; }
        set
        {
            _nodes = value;
            this.NotifyPropertyChanged(m => m.Nodes, propertyChangedField, UIDispatcher.Current);
        }
    }

    private ObservableCollection<Connection> _connections;
    public ObservableCollection<Connection> Connections
    {
        get { return _connections; }
        set
        {
            _connections = value;
            this.NotifyPropertyChanged(m => m.Connections, propertyChangedField, UIDispatcher.Current);
        }
    }
}
Let me know if you need me to share the complete project.

Cheers,
Tony
Jan 6, 2015 at 12:20 AM
Works! Thanks much!