Mar182010

How to use strongly-typed name with INotifyPropertyChanged

Published by eric at 8:20 AM under .NET Framework | C# | Windows | WPF

You may already have read my posts about how to use INotifyPropertyChanged in a type-safe way (here and here), but sometime you don’t want to modify all your classes to use this method. All you want is avoid the use of a magic string to define the property. Your code will be refactoring proof.

For example let say you have this property:

public string FirstName
{
    get { return _firstName; }
    set 
    {
        if (_firstName == value)
            return;
        _firstName = value;
        RaisePropertyChanged("FirstName");
    }
}

Some refactoring tool, like Resharper, will be able to change the “FirstName” string but not Visual Studio itself. The solution? Replace this string with a strong type value. How? Let’s assume we can do this:

public string FirstName
{
    get { return _firstName; }
    set 
    {
        if (_firstName == value)
            return;
        _firstName = value;
        RaisePropertyChanged(this.NameOf(p => p.FirstName));
    }
}

Notice that you must specify the “this” keyword to make it work. That is exactly What this extension method let you do:

public static class ObjectExtensions
{
    public static string NameOf<T>(this T target, Expression<Func<T, object>> propertyExpression)
    {
        MemberExpression body = null;
        if (propertyExpression.Body is UnaryExpression)
        {
            var unary = propertyExpression.Body as UnaryExpression;
            if (unary.Operand is MemberExpression)
                body = unary.Operand as MemberExpression;
        }
        else if (propertyExpression.Body is MemberExpression)
        {
            body = propertyExpression.Body as MemberExpression;
        }
        if (body == null)
            throw new ArgumentException("'propertyExpression' should be a member expression");

        // Extract the right part (after "=>")
        var vmExpression = body.Expression as ConstantExpression;

        // Extract the name of the property to raise a change on
        return body.Member.Name;
    }
}

You can event use it in your property changed handler:

private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
    if (args.PropertyName == this.NameOf(p => p.FirstName))
    {
        // ...
    }
}

Enjoy!



[KickIt] [Dzone] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tags: , , ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses

Jul272009

Type-safe INotifyPropertyChanged and derived classes

Published by eric at 7:50 AM under .NET Framework | C# | Windows | WPF

Some of you who read my previous blog post notice that this technique doesn't allow to raise “PropertyChanged” event from a derived class. This is because you can only call method of “PropertyChangedEventHandler” from the class where it is defined. Anywhere else you can only assign (+=) and unassign (-=) event handler.

One way to work around this is to add a method in your base model class that will forward the call to “PropertyChangedEventHandler”.

Here is a modified copy of the class from my previous post:

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type safe raise from base method
            RaisePropertyChanged(() => Data);
        }
    }

    protected void RaisePropertyChanged(Expression<Func<object>> expression)
    {
        PropertyChanged.Raise(expression);   
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

Now you can define a derived class and use the same method to raise a PropertyChanged event.

public class DerivedModel : Model
{
    private string _moreData;

    public string MoreData
    {
        get { return _moreData; }
        set
        {
            if (_moreData == value)
                return;

            _moreData = value;
            RaisePropertyChanged(() => MoreData);
        }
    }
}

Now with very little effort you can build a type-safe and bindable data model.

It is also useful to be able to raise many property at once. For example if have calculated properties that depends on others like in this sample class:

public class User : INotifyPropertyChanged
{
    private string _lastName;

    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (_lastName == value)
                return;

            _lastName = value;
            RaisePropertyChanged(()=>LastName, ()=>FullName);
        }
    }

    private string _firstName;

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName == value)
                return;

            _firstName = value;
            RaisePropertyChanged(() => FirstName, () => FullName);
        }
    }

    public string FullName
    {
        get { return String.Format("{0} {1}", _firstName, _lastName); }
    }

    public void RaisePropertyChanged(params Expression<Func<object>>[] expression)
    {
        PropertyChanged.Raise(expression);
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

Because a change to either “FirstName” or “LastName” should trigger a change to “FullName” you have to raise both changes from both properties. Of course you can call “RaisePropertyChanged” many times but with a simple overload you can do this. All you have to do is add this to your extensions class.

public static void Raise(this PropertyChangedEventHandler handler, params Expression<Func<object>>[] proppertyExpressions)
{
    foreach (var expression in proppertyExpressions)
        Raise(handler, expression);
}

Aside from beeing type safe, this method will give you intellisense support while you type your “RaiePropertyChanged” calls. You still have to type the right property though.



[KickIt] [Dzone] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tags: , , ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 2 Responses

Jul222009

How to use INotifyPropertyChanged, the type-safe way (no magic string)

Published by eric at 10:43 PM under .NET Framework | C# | Windows | WPF

Implementation of the INotifyPropertyChanged interface is quite simple. There is only one event to implement. Take for example the following simple model class:

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type un-safe PropertyChanged raise 
            PropertyChanged(this, new PropertyChangedEventArgs("Data"));
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

This is a pretty standard way to implement a bindable property. The problem here is the “Data” string to specify which property changed. If someone change the name of the property without changing the content of the string, the code will compile fine but won’t work. In a big application with may properties it can be hard to detect and find the problem.

The best solution is to rely on the compiler to warn us. But because the property name is a string it can’t. So let’s change that line with a type-safe one.

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type safe PropertyChanged raise
            PropertyChanged.Raise(() => Data);
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

What is the trick? Raise is an extension method that takes a lambda expression to specify the name of the property in a type safe way. The Raise method resolve this expression to extract the name of the property and pass it to the PropertyChanged event.

public static class PropertyChangedExtensions
{
    public static void Raise(this PropertyChangedEventHandler handler, Expression<Func<object>> propertyExpression)
    {
        if (handler != null)
        {
            // Retreive lambda body
            var body = propertyExpression.Body as MemberExpression;
            if (body == null)
                throw new ArgumentException("'propertyExpression' should be a member expression");

            // Extract the right part (after "=>")
            var vmExpression = body.Expression as ConstantExpression;
            if (vmExpression == null)
                throw new ArgumentException("'propertyExpression' body should be a constant expression");

            // Create a reference to the calling object to pass it as the sender
            LambdaExpression vmlambda = Expression.Lambda(vmExpression);
            Delegate vmFunc = vmlambda.Compile();
            object vm = vmFunc.DynamicInvoke();

            // Extract the name of the property to raise a change on
            string propertyName = body.Member.Name;
            var e = new PropertyChangedEventArgs(propertyName);
            handler(vm, e);
        }
    }
}

All you have to do is to put this extension method in your code and the jib is done. Of course at the end a string will be used to raise the PropertyChanged event but because you don’t have to type it, you don’t have to maintain it.



[KickIt] [Dzone] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tags: , , ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses

Mar082009

Access denied when writing to the event log

Published by germain at 12:33 PM under Windows

If you have an access denied when writing to the event log, the user probably doesn't have or has lost the right to write to the event log. The solution is to give him the right to write to the event log for the authenticated user.   

In regedit navigate to  [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application] for example

Append (A;;0x3;;;AU) to the customSd Key, that will the right to Authenticated User (AU) to write to the chosen event log 

 

 

 

 



[KickIt] [Dzone] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tags:

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses