none
Handling INotifyPropertyChanged without hardcoded strings RRS feed

  • Question

  • I have a proper MVVM implementation and to avoid connections between the Model and ViewModel I created a third tier called common which defines a large set of interfaces. The Model implements the interfaces. The ViewModel is then able to consume the Model without directly referencing the Model (in fact, there is absolutely no direct reference to the binaries at all).

    Within the model, I've declared constants for the property names so that, when raising a property changed event, I have a reasonable alternative to hard-coded strings. (Yes, they are still hardcoded, but the string is defined exactly once and is placed directly next to the property in code making it much easier to update when the property name changes).

    The issue is handling property changed events. Like I said, the ViewModel does not reference the Model - only the Common assembly which defines the interfaces. Interfaces do not allow members, which include constants. So, when handling the property changed events, I seem to have no other option than to hardcode the strings.

    I've considered building sealed classes, but multiple inheritance is in issue for my interfaces. Any thoughts?

    I've used the LINQ solution in the past, but it works very badly when dealing with unusual properties or property types. I can't remember all of the edge cases off the top of my head, but I believe that parameterized and generic properties were an issue.

    Thanks

    Thursday, May 3, 2012 7:07 PM

All replies

  • I have used following base class for my view models to get rid of the string in some project, but in simple cases I still use strings. I think this has been in some Prism 4 example that I extracted to my own ViewModelBase class. Your previous Linq solution might be something like this, but this works with generic properties. I don't recall have I used it with indexers, but I think it might not work.

        class Program
        {
            static void Main(string[] args)
            {
                var model = new GenericModel<int>() { Value = 10 };
    
                model.PropertyChanged += (s, e) => Console.WriteLine(e.PropertyName + ": " + model.Value);
    
                for (int i = 1; i < 10; i++)
                {
                    model.Value = (i * 10);
                }
    
                Console.WriteLine();
    
                var dmodel = new DoubleModel() { Value = 10D };
    
                dmodel.PropertyChanged += (s, e) => Console.WriteLine(e.PropertyName + ": " + dmodel.Value);
    
                for (int i = 1; i < 10; i++)
                {
                    dmodel.Value = (i * 10);
                }
    
                Console.WriteLine("Done...");
                Console.ReadKey();
            }
        }
    
        public abstract class ViewModelBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
            {
    
                var propertyName = ExtractPropertyName(propertyExpression);
    
                this.OnPropertyChanged(propertyName);
    
            }
    
            protected static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
            {
                var memberExpression = propertyExpression.Body as MemberExpression;
    
                return memberExpression.Member.Name;
    
            }
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        public class GenericModel<T> : ViewModelBase
        {
            private T value;
    
            public T Value
            {
                get { return this.value; }
                set
                {
                    this.value = value;
                    RaisePropertyChanged(() => this.Value);
                }
            }
    
    
        }
    
        public class DoubleModel : ViewModelBase
        {
            private double value;
    
            public double Value
            {
                get { return this.value; }
                set
                {
                    this.value = value;
                    RaisePropertyChanged(() => this.Value);
                }
            }
        }


    • Edited by MasaSam Friday, May 4, 2012 5:45 AM
    Friday, May 4, 2012 5:44 AM
  • I appreciate the code example - a lot of effort. Unfortunately, this was the LINQ solution I was talking about. It works great for very simple, plain properties and types, but there were edge cases where simply extracting the Member expression was not sufficient. As I said, I cannot remember the exact scenario. Maybe I'll try to hunt the problem down and provide a more thorough explanation.

    Thanks

    Sunday, May 6, 2012 12:40 PM
  • Other solution might be use some 3rd party tools like Simon Cropp's Notify Property Weaver. This tool will create property notifications at compile time so you don't have to write them. If someone decompiles your code he will see the common INotifyPropertyChanged implementation.

    Don't know if this can help you. I haven't tested it with very complex properties just similar with my previous post, but I don't think you can use any other way than hardcoded strings with very complex properties.

    Sunday, May 6, 2012 2:10 PM
  • I am usually content to use constants, but when the class and/or it's members are not visible outside the assembly then handling the notify property changed outside the assembly requires hard-coded, inline strings. I hate to do that, but I haven't found a pattern which allows me to get around that requirement (aside from one property changed event per property, like Windows Forms).

    Thanks though.

    Sunday, May 6, 2012 7:48 PM
  • Can you make a specific example for your issue?


    Ghost,
    Call me ghost for short, Thanks
    To get the better answer, it should be a better question.

    Tuesday, May 8, 2012 3:11 PM