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 🙂
[…] Silverlight DependencyProperty Relay and Silverlight Asynchronous Loader Helper (Chris Cavanagh) […]