Silverlight DependencyProperty Relay

UPDATE: Silverlight 3 (beta 1 at time of this post) supports ElementName and RelativeSource binding :)  See here.

Similar to the Silverlight relay base class I posted, here’s an alternative built around DependencyProperties:

/// <summary>
/// IDependencyRelay interface
/// </summary>
public interface IDependencyRelay
{
    void OnDependencyPropertyChanged( string name, DependencyPropertyChangedEventArgs args );
}

/// <summary>
/// DependencyRelay class
/// </summary>
public class DependencyRelay : FrameworkElement, IDependencyRelay, INotifyPropertyChanged
{
    /// <summary>
    /// Registers a new DependencyProperty.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="propertyType">Type of the property.</param>
    /// <param name="ownerType">Type of the owner.</param>
    /// <returns></returns>
    public static DependencyProperty RegisterProperty( string name, Type propertyType, Type ownerType )
    {
        return DependencyProperty.Register(
            name,
            propertyType,
            ownerType,
            new PropertyMetadata( MakeHandler( name ) ) );
    }

    /// <summary>
    /// Registers a new DependencyProperty.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="propertyType">Type of the property.</param>
    /// <param name="ownerType">Type of the owner.</param>
    /// <param name="defaultValue">The default value.</param>
    /// <returns></returns>
    public static DependencyProperty RegisterProperty( string name, Type propertyType, Type ownerType, object defaultValue )
    {
        return DependencyProperty.Register(
            name,
            propertyType,
            ownerType,
            new PropertyMetadata( defaultValue, MakeHandler( name ) ) );
    }

    /// <summary>
    /// Makes a PropertyChangedCallback handler.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <returns></returns>
    private static PropertyChangedCallback MakeHandler( string name )
    {
        return delegate( DependencyObject obj, DependencyPropertyChangedEventArgs args )
        {
            ( obj as IDependencyRelay ).OnDependencyPropertyChanged( name, args );
        };
    }

    /// <summary>
    /// Called when a DependencyProperty has changed.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
    protected virtual void OnDependencyPropertyChanged( string name, DependencyPropertyChangedEventArgs args )
    {
        OnPropertyChanged( name );
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises the PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    protected void OnPropertyChanged( string propertyName )
    {
        if ( PropertyChanged != null ) PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
    }

    #endregion

    #region IDependencyRelay Members

    /// <summary>
    /// Called when a DependencyProperty has changed.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
    void IDependencyRelay.OnDependencyPropertyChanged( string name, DependencyPropertyChangedEventArgs args )
    {
        OnDependencyPropertyChanged( name, args );
    }

    #endregion
}

The idea is you either derive your relay class from DependencyRelay or implement the IDependencyRelay interface on your own class, then use DependencyRelay’s RegisterProperty method [instead of DependencyProperty.Register].  The result is the same, but it also raises INotifyPropertyChanged.PropertyChanged events when a DependencyProperty changes.

Here’s an example:

public class PlannerBinder : DependencyRelay
{
    public static readonly DependencyProperty ChartProperty = DependencyRelay.RegisterProperty(
        "Chart",
        typeof( Chart ),
        typeof( PlannerBinder ) );

    public Chart Chart
    {
        get { return (Chart)GetValue( ChartProperty ); }
        set { SetValue( ChartProperty, value ); }
    }

    public static readonly DependencyProperty TransformProperty = DependencyRelay.RegisterProperty(
        "Transform",
        typeof( Transform ),
        typeof( PlannerBinder ) );

    public Transform Transform
    {
        get { return (Transform)GetValue( TransformProperty ); }
        set { SetValue( TransformProperty, value ); }
    }

    public static readonly DependencyProperty AgeRangeProperty = DependencyRelay.RegisterProperty(
        "AgeRange",
        typeof( ValueRange ),
        typeof( PlannerBinder ) );

    public ValueRange AgeRange
    {
        get { return (ValueRange)GetValue( AgeRangeProperty ); }
        set { SetValue( AgeRangeProperty, value ); }
    }

    public static readonly DependencyProperty RetirementAgeProperty = DependencyRelay.RegisterProperty(
        "RetirementAge",
        typeof( double ),
        typeof( PlannerBinder ) );

    public double RetirementAge
    {
        get { return (double)GetValue( RetirementAgeProperty ); }
        set { SetValue( RetirementAgeProperty, value ); }
    }

    public static readonly DependencyProperty SliderMarginProperty = DependencyRelay.RegisterProperty(
        "SliderMargin",
        typeof( Thickness ),
        typeof( PlannerBinder ) );

    public Thickness SliderMargin
    {
        get { return (Thickness)GetValue( SliderMarginProperty ); }
        set { SetValue( SliderMarginProperty, value ); }
    }

    protected override void OnDependencyPropertyChanged( string name, DependencyPropertyChangedEventArgs args )
    {
        base.OnDependencyPropertyChanged( name, args );

        if ( args.Property == ChartProperty )
        {
            SetBinding( RetirementAgeProperty, new Binding( "RetirementAge" ) { Source = Chart } );
            SetBinding( AgeRangeProperty, new Binding( "ValueRange" ) { Source = Chart.Axes[ 0 ] } );
        }

        if ( args.Property != SliderMarginProperty && Transform != null && AgeRange.Range > 0 )
        {
            var pos = Transform.Transform( new Point( ( RetirementAge - AgeRange.Minimum ) / AgeRange.Range, 0 ) );

            SliderMargin = new Thickness( pos.X - 5, -5, -5, -5 );
        }
    }
}

You can override OnDependencyPropertyChanged if you need to add some extra handling (calculating dependent values etc).  It’s not a huge time- or code-saver, but maybe you’ll find a use for it somewhere 🙂

One Comment

Leave a comment