Freigeben über


ChangeableObject and ValueConverter: databinding conveniences

A couple more bits of code that others might find useful...  Whenever I do databinding-intensive apps (both Silverlight & WPF), I find myself writing a fair amount of boilerplate code.  If you want change notifications on your class, you need to inherit from INotifyPropertyChanged, define a PropertyChanged event, and write a couple lines of code to fire it:

    if (this.PropertyChanged!= null) {
        var args = new PropertyChangedEventArgs("SomeProperty");
        PropertyChanged(this, args);
     }

Not rocket science, but it gets old after the 10th time.  How about a ChangeableObject base class?:

    public class ChangeableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null) {
                var args = new PropertyChangedEventArgs(info);
                PropertyChanged(this, args);
            }
        }
    }

Now you can inherit the PropertyChanged event, and simplify firing it to a single line, e.g.:

    public class Elevator : ChangeableObject
    {
        private double position = 0;
        public double Position
        {
            get { return position; }
            set { position = value; NotifyPropertyChanged("Position") ; }
        }
    }

Similarly, it never ceases to annoy me that whenever I write a value converter, I'm forced to define a a ConvertBack method even though I almost never use it.  Plus, IntelliSense for implementing interfaces isn't as strong as overriding virtual methods...  So here's a trivial base class:

    public abstract class ValueConverter : IValueConverter
    {
        public abstract object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture);

        public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new Exception("unsupported");
        }
    }

Which can be used like this:

    public class BoolToVisibilityConverter : ValueConverter {
        public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var v = (bool)value;
            var result = (v) ? Visibility.Visible : Visibility.Collapsed;
            return result;
        }
    }

Comments

  • Anonymous
    February 03, 2009
    Wouldn't the first example be a good candidate for an extension method? Just imagine if your ValueConverter base class for some reason also had to implement INotifyPropertyChanged... static class NotifyPropertyChangedExtensions {    public static void NotifyPropertyChanged(this INotifyPropertyChanged prop, String info)    {        if (prop.PropertyChanged != null) {            var args = new PropertyChangedEventArgs(info);            prop.PropertyChanged(prop, args);        }    } } Of course, you'd still have to implement the event...

  • Anonymous
    February 03, 2009
    @David: Can't invoke the event at that place only from inside the class. And a safer way is: var propertyChanged = PropertyChanged; if (propertyChanged != null) {    propertyChanged(this, new PropertyChangedEventArgs(info)); }

  • Anonymous
    February 03, 2009
    Just to add my 2-cents worth....  Sometimes I find that in more complex notification objects that updating one property may have the effect of updating other property values as well.  Instead of calling this for each property that changed from within the CLR property's "set" method, I use a version that can take take several property names: protected void NotifyPropertyChanges(params string[] names) {   var propertyChanged = PropertyChanged;   if (propertyChanged != null)   {      foreach (string name in names)      {         propertyChanged(this, new PropertyChangedEventArgs(name));      }   } }

  • Anonymous
    February 03, 2009
    You can find a more comprehensive implementation of ValueConverter, which also implements IMultiValueConverter and a MarkupExtension at http://codeplex.com/wpfcontrib/.

  • Anonymous
    February 04, 2009
    Right, to the multiple property point. Most of the time I raise this event with NULL - this indicates that all properties changed. If you have a large object of 20 or so properties and you are updating all of them it generates a lot of event traffic - especially for busy real-time apps.  A single "SynchronizeViews" type method works better and is more controlled.  It also lets you use the automatic property getters/setters.

  • Anonymous
    February 12, 2009
    The comment has been removed

  • Anonymous
    February 28, 2009
    I think these helpers are only half-way through ;-) For all of the value converters I usually employ a single non-abstract class that takes a conversion delegate in its ctor. A new type of converter could be created like this: ValueConverter.Create((byte n) => Color.FromArgb(0xFF, n, n, n)); No class declarations needed to have one more converter defined. The object we create could be planted into the ResourceDictionary for further use from XAML markup thru a standard resource reference.

  • Anonymous
    February 28, 2009
    Now, the properties with notifications. The first alternative approach is to derive your object (directly or indirectly) from the DependencyObject class. This is possible for codebehind as well as for the UI classes. This enables us to declare our properties as Dependency Properties, which also guarantees that WPF will see any changes to them without our firing any special events. The pattern to declare a dependency property (a public static field and a CLR getter/setter) could easily be encoded as a R# Live Template / VS Code Snippet. The second alternative approach is to use a wrapper class to declare each of the properties, say, Property<TValue>. Inside it stores the actual value, exposes it through a CLR property (say, Value), and fires the appropriate events whenver the value is changed through a setter. This probably is the quickest way of writing the databindable code because it takes just one member, requires no special base classes, no interface implementations, no event declarations, no event firings, no nothing. The code to access the property gets longer, though (obj.Age.Value instead of obj.Age, for instance; {Binding Age.Value} in XAML), but in many cases I rule this to be a lesser inconvenience compared to setting up the rig for event notifications in each data object class.

  • Anonymous
    March 09, 2009
    Re: Halfway Through - 2 template approach Iteresting approach Serge.  Perhaps I'm missing something but isn't it awkward to handle the property name and appropriate parent sender object (Nick's original base class doesn't give the parent either) when firing the event? EI the code: var args = new PropertyChangedEventArgs(PropertyName); // what name? propertyChanged(this, args); // what sender? Is there a more clever way than adding the overhead of the property name and owning object as members?