views:

62

answers:

1

Hello,

I'm reading the source code from the latest Prism 4 drop and am interested in solving this problem. There is a base class for the ViewModels that implements INotifyPropertyChanged and INotifyDataErrorInfo and provides some refactoring friendly change notification.

protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpresssion)
{
    var propertyName = ExtractPropertyName(propertyExpresssion);
    this.RaisePropertyChanged(propertyName);
}



private string ExtractPropertyName<T>(Expression<Func<T>> propertyExpresssion)
{
    if (propertyExpresssion == null)
    {
        throw new ArgumentNullException("propertyExpression");
    }

    var memberExpression = propertyExpresssion.Body as MemberExpression;
    if (memberExpression == null)
    {
        throw new ArgumentException("The expression is not a member access expression.", "propertyExpression");
    }

    var property = memberExpression.Member as PropertyInfo;
    if (property == null)
    {
        throw new ArgumentException("The member access expression does not access  property.","propertyExpression");
    }

    if (!property.DeclaringType.IsAssignableFrom(this.GetType()))
    {
       throw new ArgumentException("The referenced property belongs to a different type.", "propertyExpression");
    }

    var getMethod = property.GetGetMethod(true);
    if (getMethod == null)
    {
        // this shouldn't happen - the expression would reject the property before reaching this far
        throw new ArgumentException("The referenced property does not have a get method.", "propertyExpression");
    }

    if (getMethod.IsStatic)
    {
        throw new ArgumentException("The referenced property is a static property.", "propertyExpression");
    }

    return memberExpression.Member.Name;
    }

and as an example of it's usage

private void RetrieveNewQuestionnaire()
{
    this.Questions.Clear();

    var template = this.questionnaireService.GetQuestionnaireTemplate();
    this.questionnaire = new Questionnaire(template);

    foreach (var question in this.questionnaire.Questions)
    {
      this.Questions.Add(this.CreateQuestionViewModel(question));
    }

    this.RaisePropertyChanged(() => this.Name);
    this.RaisePropertyChanged(() => this.UnansweredQuestions);
    this.RaisePropertyChanged(() => this.TotalQuestions);
    this.RaisePropertyChanged(() => this.CanSubmit);
}

My question is this. What would it take to pass an array of the property names to an overloaded method (RaisePropertyChanged) and condense this last bit of code from 4 lines to 1?

Thank you, Stephen

+2  A: 

Have you considered changing :

protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpresssion)
{
    var propertyName = ExtractPropertyName(propertyExpresssion);
    this.RaisePropertyChanged(propertyName);
}

to :

protected void RaisePropertyChanged<T>(params Expression<Func<T>>[] propertyExpresssion)
{
    foreach (var propertyName in 
         propertyExpresssion.Select(ExtractPropertyName))
    {
        this.RaisePropertyChanged(propertyName);
    }
}

and the usage is:

private void RetrieveNewQuestionnaire()
{
      //yada yada yada
      this.RaisePropertyChanged(() => this.Name, 
                                () => this.UnansweredQuestions, 
                                () => this.TotalQuestions);
}

Maybe someone consider this a bad practice, but at least do the trick. Good luck.

Markust
+1. Beat me to it. ;)
jrista
I cannot get this to compile.
Stephen Patten
That compiles and works. I use it. Post the error.
Markust
protected void RaisePropertyChanged<T>(params Expression<Func<T>>[] propertyExpresssion){ foreach (var propertyName in propertyExpresssion.Select(ExtractPropertyName)) { this.RaisePropertyChanged(propertyName); }}The type arguments for method 'System.Linq.Enumerable.Select<TSource,TResult>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,TResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Stephen Patten
This is also in a base class
Stephen Patten
The only way that this has worked for me during my trial and error testing is that if you're lucky enough to have the SAME data types for all the properties that your passing to the RaisePropertyChanged method. In your example above, you are passing a string and 2 ints and I highly doubt that this is working, or shall I say I'm still unable to get it to work verbatim.Just to make sure everyone is on the same page, there may be something inferred from the other comments that I'd need to code up and am missing. I am just trying to grok this stuff and it's way beyond my mental capabilities.
Stephen Patten