RegisterProperty with magic strings

Topics: Feature requests
Nov 28, 2012 at 6:24 PM

Hi Geert,

it would be nice if Catel could offer two additional RegisterProperty functions to enable easier refactoring etc.. (while preserving backward compatiblity):

        public static PropertyData RegisterProperty<TModel, TValue>(
            Expression<Func<TModel, TValue>> propertyExpression, 
            TValue defaultValue = default(TValue), 
            EventHandler<AdvancedPropertyChangedEventArgs> propertyChangedEventHandler = null, 
            bool includeInSerialization = true, 
            bool includeInBackup = true)
        {
            Argument.IsNotNull("propertyExpression", propertyExpression);

            var memberExpression = (MemberExpression)propertyExpression.Body;
            var propertyName = memberExpression.Member.Name;

            return Catel.Data.ModelBase.RegisterProperty(
                propertyName, 
                typeof(TValue), 
                defaultValue, 
                propertyChangedEventHandler, 
                includeInSerialization, 
                includeInBackup);
        }

        public static PropertyData RegisterProperty<TModel, TValue>(
            Expression<Func<TModel, TValue>> propertyExpression, 
            Func<TValue> createDefaultValue, 
            EventHandler<AdvancedPropertyChangedEventArgs> propertyChangedEventHandler = null, 
            bool includeInSerialization = true, 
            bool includeInBackup = true)
        {
            Argument.IsNotNull("propertyExpression", propertyExpression);

            var memberExpression = (MemberExpression)propertyExpression.Body;
            var propertyName = memberExpression.Member.Name;

            return Catel.Data.ModelBase.RegisterProperty(
                propertyName, 
                typeof(TValue), 
                createDefaultValue, 
                propertyChangedEventHandler, 
                includeInSerialization, 
                includeInBackup);
        }

Usage:

public class Entity : ModelBase
{
   public static readonly PropertyData IdProperty = RegisterProperty<Entity, int>(x => x.Id, -1);

   public int Id
   {
       get
       {
           return this.GetValue<TId>(IdProperty);
       }

       set
       {
           this.SetValue(IdProperty, value);
       }
   }
}

Many Thanks!

Coordinator
Nov 28, 2012 at 7:08 PM

Hi Facos,

Thanks for your contributions, I really appreciate them. Why don't you create a pull request, then I only have to accept. Saves me some time, and your changes are accepted faster.

You probably already have the source code cloned anyway. If you create a pull request, I can simply accept it and continue.

Developer
Nov 29, 2012 at 6:57 PM
Edited Nov 29, 2012 at 7:50 PM

Hi Facos,

What do you think about this?

1) Why the first generic parameter is required?  It is actually "ignored", so it could be written as:

 

Expression<Func<object, TValue>> propertyExpression

 

The property will look like this:

public static readonly PropertyData IdProperty = RegisterProperty(x => ((Entity)x).Id, -1)

2) A small improvement to check the type of the property expression.

 

Argument.IsOfType("propertyExpression.Body", propertyExpression.Body, typeof(MemberExpression));


Nov 29, 2012 at 7:47 PM

@geert:
You are right, but i have to admit that i have no experience with codeplex forks and pull requests.
For the next thing i will try to send you pull requests if i get them right :)...
The above code is copied straight out of my application library... but i thought it suits better to the
catel library...

@alexfdezsauco:
Thanks for your feedback!

1) That's a matter of taste. My intention with the additional TModel generic parameter was to avoid the
cast in the property member expression.

2) Thanks for the addition. Did not think of this casting check...

Developer
Nov 29, 2012 at 8:18 PM
Edited Nov 29, 2012 at 8:35 PM

1) I'm also thinking about the simplification of the expression for CatelR# and keep the same style when it generates the property change notification method ;)

 
public static readonly PropertyData IdProperty = RegisterProperty("Id", typeof(int), default(int), (o, e) => ((Entity)o).OnIdChanged());

But probably you are right,  this syntax is actually looks more clear

public static readonly PropertyData IdProperty = RegisterProperty<Entity, int>(o => o.Id, typeof(int), default(int), (o, e) => o.OnIdChanged());
Nov 30, 2012 at 12:33 PM

@alexfdezsauco:

Thank you for your hint. I did not think of the PropertyChangedEventHandler casting.
The workaround below has the drawback of adding one indirection when calling the
PropertyChangedEventHandler but i can live with that.
One last thing still remains: the AdvancedPropertyChangedEventArgs. They must be
always casted...

 

public static PropertyData RegisterProperty<TModel, TValue>(
            Expression<Func<TModel, TValue>> propertyExpression, 
            TValue defaultValue = default(TValue), 
            Action<TModel, AdvancedPropertyChangedEventArgs> propertyChangedCallback = null, 
            bool includeInSerialization = true, 
            bool includeInBackup = true)
        {
            Argument.IsNotNull("propertyExpression", propertyExpression);
            Argument.IsOfType("propertyExpression.Body", propertyExpression.Body, typeof(MemberExpression));

            var memberExpression = (MemberExpression)propertyExpression.Body;
            var propertyName = memberExpression.Member.Name;

            EventHandler<AdvancedPropertyChangedEventArgs> propertyChangedEventHandler = null;

            if (propertyChangedCallback != null)
            {
                propertyChangedEventHandler = (sender, args) => propertyChangedCallback((TModel)sender, args);
            }

            return Catel.Data.ModelBase.RegisterProperty(
                propertyName, 
                typeof(TValue), 
                defaultValue, 
                propertyChangedEventHandler, 
                includeInSerialization, 
                includeInBackup);
        }


        public static PropertyData RegisterProperty<TModel, TValue>(
            Expression<Func<TModel, TValue>> propertyExpression, 
            Func<TValue> createDefaultValue, 
            Action<TModel, AdvancedPropertyChangedEventArgs> propertyChangedCallback = null, 
            bool includeInSerialization = true, 
            bool includeInBackup = true)
        {
            Argument.IsNotNull("propertyExpression", propertyExpression);
            Argument.IsOfType("propertyExpression.Body", propertyExpression.Body, typeof(MemberExpression));

            var memberExpression = (MemberExpression)propertyExpression.Body;
            var propertyName = memberExpression.Member.Name;

            EventHandler<AdvancedPropertyChangedEventArgs> propertyChangedEventHandler = null;

            if (propertyChangedCallback != null)
            {
                propertyChangedEventHandler = (sender, args) => propertyChangedCallback((TModel)sender, args);
            }

            return Catel.Data.ModelBase.RegisterProperty(
                propertyName, 
                typeof(TValue), 
                createDefaultValue, 
                propertyChangedEventHandler, 
                includeInSerialization, 
                includeInBackup);
        }

Now it's possible to write:

 

public static readonly PropertyData IdProperty = RegisterProperty<Entity, int>(o => o.Id, -1, (o, e) => o.OnIdChanged((int)e.OldValue));

Coordinator
Dec 17, 2012 at 8:26 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.