none
Silverlight UserControl Custom Property Binding

    Question

  • What is the proper way to implement Custom Properties in Silverlight UserControls? 

    Every "Page" in Silverlight is technically a UserControl (they are derived from the UserControl class).  When I say UserControl here, I mean a Custom UserControl that will be used inside many different pages in many different scenarios (similar to an ASP.NET UserControl). 

    I would like the Custom UserControl to support Binding and not rely on the Name of the Property it is binding to, to always be the same.  Instead, I would like the UserControl itself to have a property that the Controls inside the UserControl bind to, and the ViewModels outside the UserControl also bind to.  (please see the example below) 

    Binding within the UserControl works, Binding within the MainPage works, The Binding I set up between the MainPage and the UserControl does not work.  This is the binding between the Custom UserControl Property (which is a DependencyProperty) and the Property being bound to the UserControl from the MainPage (which will usually be a Property of a ViewModel - but it is a Property of the MainPage here for simplification).  Specifically this line: 

        <myUserControls:MyCustomUserControl x:Name="MyCustomControl2" SelectedText="{Binding MainPageSelectedText, Mode=TwoWay}" Width="200" Height="50" />  
     

    example output: 

    MainPage.xaml

        <UserControl x:Class="SilverlightCustomUserControl.MainPage"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:myUserControls="clr-namespace:SilverlightCustomUserControl"
            mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
            DataContext="{Binding RelativeSource={RelativeSource Self}}">
          <Canvas x:Name="LayoutRoot">
            <StackPanel Orientation="Vertical">
              <TextBlock Text="UserControl Binding:" Width="200"></TextBlock>
              <myUserControls:MyCustomUserControl x:Name="MyCustomControl2" SelectedText="{Binding MainPageSelectedText, Mode=TwoWay}" Width="200" Height="50" />
              <TextBlock Text="MainPage Binding:" Width="200"></TextBlock>
              <TextBox Text="{Binding MainPageSelectedText, Mode=TwoWay}" Width="200"></TextBox>
              <Border BorderBrush="Black" BorderThickness="1">
                <TextBlock Text="{Binding MainPageSelectedText}" Width="200" Height="24"></TextBlock>
              </Border>
            </StackPanel>
          </Canvas>
        </UserControl>
    
    
     

     

    MainPage.xaml.cs

        namespace SilverlightCustomUserControl
        {
         public partial class MainPage : UserControl, INotifyPropertyChanged
         {
          //NOTE: would probably be in a ViewModel
          public string MainPageSelectedText
          {
           get { return _MainPageSelectedText; }
           set
           {
            string myValue = value ?? String.Empty;
            if (_MainPageSelectedText != myValue)
            {
             _MainPageSelectedText = value;
             OnPropertyChanged("MainPageSelectedText");
            }
           }
          }
          private string _MainPageSelectedText;
        
        
          public MainPage()
          {
           InitializeComponent();
          }
        
        
          #region INotifyPropertyChanged Members
        
          public event PropertyChangedEventHandler PropertyChanged;
        
          protected virtual void OnPropertyChanged(string name)
          {
           PropertyChangedEventHandler ph = this.PropertyChanged;
        
           if (ph != null)
            ph(this, new PropertyChangedEventArgs(name));
          }
        
          #endregion
         }
        }
    
    
     

    MyCustomUserControl.xaml

        <UserControl
           x:Class="SilverlightCustomUserControl.MyCustomUserControl" 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           DataContext="{Binding RelativeSource={RelativeSource Self}}">
          <Grid>
            <StackPanel>
              <TextBox Text="{Binding SelectedText, Mode=TwoWay}" />
              <Border BorderBrush="Black" BorderThickness="1">
                <TextBlock Text="{Binding SelectedText}" Height="24"></TextBlock>
              </Border>
            </StackPanel>
          </Grid>
        </UserControl>
     


    MyCustomUserControl.xaml.cs

        namespace SilverlightCustomUserControl
        {
         public partial class MyCustomUserControl : UserControl
         {
          public string SelectedText
          {
           get { return (string)GetValue(SelectedTextProperty); }
           set { SetValue(SelectedTextProperty, value); }
          }
        
          public static readonly DependencyProperty SelectedTextProperty =
            DependencyProperty.Register("SelectedText", typeof(string), typeof(MyCustomUserControl), new PropertyMetadata("", SelectedText_PropertyChangedCallback));
        
        
          public MyCustomUserControl()
          {
           InitializeComponent();
          }
        
          private static void SelectedText_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
           //empty
          }
         }
        }
    
    
     


    References (how I got this far): 

    Amazing article on Silverlight DataBinding in general  http://www.simple-talk.com/dotnet/.net-framework/data-and-silverlight-2-data-binding/

    use DependencyPropertys:
    http://geekswithblogs.net/thibbard/archive/2008/04/22/wpf-custom-control-dependency-property-gotcha.aspx

    use DependencyPropertys, add x:Name to your UserControl - add Binding with ElementName, set Custom property again in the PropertyChangedCallback method:
    http://stackoverflow.com/questions/845564/setting-custom-properties-in-usercontrol-via-databinding


    don't use custom properties, rely on underlying datacontext names (I do not like this solution):
    http://stackoverflow.com/questions/1145435/wpf-trouble-using-dependency-properties-in-a-usercontrol

     

    Tuesday, October 06, 2009 2:26 PM

Answers

  • It looks like The Binding Markup Extension's "Source" Property must be a static resource: http://msdn.microsoft.com/en-us/library/cc189022(VS.95).aspx   &  http://msdn.microsoft.com/en-us/library/system.windows.data.binding.source(VS.95).aspx

    Is there any way of referencing the UserControl itself as the Source within xaml alone?  Maybe this is why gauthamshetty86 knew I needed to use ElementName binding - he was just one step ahead of me.

    I was able to get it working in code behind.  You just need to remove the binding expressions from the controls, give them names, and set everything up in the UserControl's Constructor.

    MyCustomUserControl.xaml

     

    <UserControl
       x:Class="SilverlightCustomUserControl.MyCustomUserControl" 
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    	<Grid>
    		<StackPanel>
    			<TextBox x:Name="UserControlTextBox" />
    			<Border BorderBrush="Black" BorderThickness="1">
    				<TextBlock x:Name="UserControlTextBlock" Height="24"></TextBlock>
    			</Border>
    		</StackPanel>
    	</Grid>
    </UserControl>
      

    MyCustomUserControl.xaml.cs

     

    namespace SilverlightCustomUserControl
    {
    	public partial class MyCustomUserControl : UserControl
    	{
    		public string SelectedText
    		{
    			get { return (string)GetValue(SelectedTextProperty); }
    			set { SetValue(SelectedTextProperty, value); }
    		}
    
    		public static readonly DependencyProperty SelectedTextProperty =
    			 DependencyProperty.Register("SelectedText", typeof(string), typeof(MyCustomUserControl), new PropertyMetadata("", SelectedText_PropertyChangedCallback));
    
    
    		public MyCustomUserControl()
    		{
    			InitializeComponent();
    			
    			//SEE HERE!!
    			UserControlTextBox.SetBinding(TextBox.TextProperty, new Binding() { Source = this, Path = new PropertyPath("SelectedText"), Mode = BindingMode.TwoWay });
    			UserControlTextBlock.SetBinding(TextBlock.TextProperty, new Binding() { Source = this, Path = new PropertyPath("SelectedText") });
    			//SEE HERE!!
    		}
    
    		private static void SelectedText_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    		{
    			//empty
    		}
    	}
    }
      

     

     

    Wednesday, October 07, 2009 3:15 PM

All replies

  • Hello, Go through this link: http://forums.silverlight.net/forums/t/14659.aspx http://www.codeproject.com/Articles/36501/ElementName-binding-in-Silverlight-via-Attached-Be.aspx http://www.thejoyofcode.com/Workaround_for_missing_ElementName_in_Silverlight_2_0_Binding.aspx http://www.simple-talk.com/dotnet/.net-framework/data-and-silverlight-2-data-binding/ If it is Useful please mark As Answer
    Wednesday, October 07, 2009 1:14 AM
  • I don't see anything immediately wrong. What you want to do is not a problem at all. I would remove your DataContext lines in both controls.

    DataContext="{Binding RelativeSource={RelativeSource Self}}"

    Have you checked for binding errors? This can be seen in the immediate window at runtime.

     
    Wednesday, October 07, 2009 6:02 AM
  • gauthamshetty86,  Thanks for the help.  But it doesn't answer my question.  I'd like to explain the links a little more for future readers. 

    Link 1: http://forums.silverlight.net/forums/t/14659.aspx
    Template Binding - This covers Custom Control (but not UserControl) binding within the Custom Control itself.  The binding within my UserControl (from the TextBox to the Custom Property) is working fine. 

    Link 2: http://www.codeproject.com/Articles/36501/ElementName-binding-in-Silverlight-via-Attached-Be.aspx
    Element to Element Binding in Silverlight 2.0 - This is closer to my issue, but not exactly.  This covers Element to Element binding within a single UserControl, not between two UserControls.  I do not want to rely on the Properties my UserControl is binding to, to always have the same name.  That is why I have an intermediary Property in the UserControl itself (SelectedText in my example).  Also, I will usually not be binding between two Elements, but more similar to this:  UserControl's Child Control >> UserControl Custom Property >> ViewModel Property.  The second link is the broken one. 

    Link 3: http://www.thejoyofcode.com/Workaround_for_missing_ElementName_in_Silverlight_2_0_Binding.aspx
    Element to Element Binding in Silverlight 2.0 - Interesting Hack for Element to Element binding in 2.0.  but not what I need. 

    Link 4: http://www.simple-talk.com/dotnet/.net-framework/data-and-silverlight-2-data-binding/
    Amazing article on Silverlight 2.0 DataBinding in general (still relevant to 3.0), but, as I am already following all of his advice, it doesn’t help me.  And he does not have an example of UserControl to UserControl binding.  There is an excellent explanation of the binding I am doing within each UserControl however.

     

    Wednesday, October 07, 2009 12:26 PM
  • You are Right!  There is a Binding Error!

    System.Windows.Data Error: BindingExpression path error: 'MainPageSelectedText' property not found on 'SilverlightCustomUserControl.MyCustomUserControl' 'SilverlightCustomUserControl.MyCustomUserControl' (HashCode=8628710). BindingExpression: Path='MainPageSelectedText' DataItem='SilverlightCustomUserControl.MyCustomUserControl' (HashCode=8628710); target element is 'SilverlightCustomUserControl.MyCustomUserControl' (Name='MyCustomControl2'); target property is 'SelectedText' (type 'System.String')..

    It is looking for the "MainPageSelectedText" in the context of the UserControl itself because I set the DataContext of the UserControl to be itself (I do that so that I can easily bind to the Custom Properties from the Controls within the UserControl).

    So does the DataContext of the UserControl have to be the same as the DataContext of the "Parent" UserControl to Bind back "Up" like what I am trying to do, or is there another way?  I would like to keep the DataContext of the UserControl to be itself, and only bind to those local values, to help modularize the code.

    Wednesday, October 07, 2009 12:53 PM
  • You should be able to fix this by setting Source in your binding expression.

    Wednesday, October 07, 2009 1:33 PM
  • It looks like The Binding Markup Extension's "Source" Property must be a static resource: http://msdn.microsoft.com/en-us/library/cc189022(VS.95).aspx   &  http://msdn.microsoft.com/en-us/library/system.windows.data.binding.source(VS.95).aspx

    Is there any way of referencing the UserControl itself as the Source within xaml alone?  Maybe this is why gauthamshetty86 knew I needed to use ElementName binding - he was just one step ahead of me.

    I was able to get it working in code behind.  You just need to remove the binding expressions from the controls, give them names, and set everything up in the UserControl's Constructor.

    MyCustomUserControl.xaml

     

    <UserControl
       x:Class="SilverlightCustomUserControl.MyCustomUserControl" 
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    	<Grid>
    		<StackPanel>
    			<TextBox x:Name="UserControlTextBox" />
    			<Border BorderBrush="Black" BorderThickness="1">
    				<TextBlock x:Name="UserControlTextBlock" Height="24"></TextBlock>
    			</Border>
    		</StackPanel>
    	</Grid>
    </UserControl>
      

    MyCustomUserControl.xaml.cs

     

    namespace SilverlightCustomUserControl
    {
    	public partial class MyCustomUserControl : UserControl
    	{
    		public string SelectedText
    		{
    			get { return (string)GetValue(SelectedTextProperty); }
    			set { SetValue(SelectedTextProperty, value); }
    		}
    
    		public static readonly DependencyProperty SelectedTextProperty =
    			 DependencyProperty.Register("SelectedText", typeof(string), typeof(MyCustomUserControl), new PropertyMetadata("", SelectedText_PropertyChangedCallback));
    
    
    		public MyCustomUserControl()
    		{
    			InitializeComponent();
    			
    			//SEE HERE!!
    			UserControlTextBox.SetBinding(TextBox.TextProperty, new Binding() { Source = this, Path = new PropertyPath("SelectedText"), Mode = BindingMode.TwoWay });
    			UserControlTextBlock.SetBinding(TextBlock.TextProperty, new Binding() { Source = this, Path = new PropertyPath("SelectedText") });
    			//SEE HERE!!
    		}
    
    		private static void SelectedText_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    		{
    			//empty
    		}
    	}
    }
      

     

     

    Wednesday, October 07, 2009 3:15 PM
  • Hello, You, Have to inherit :Control not UserControl *)Here I am considering the example of binding IsEnabledProperty dependency property of checkbox by FridayCheckBoxEnabledProperty, Let's start.. ( Visual Studio 2008 ->File ->New project ->Silverlight ->SilverLightClassLibrary -> Add ->NewItem TemplatedControl1.cs) You will get generic.Xaml file and TemplatedControl1.cs file in generic.Xaml file you add required user control in TemplatedControl1.cs you add your dependency property. Say, If you add an userControl in Xaml -> and dependency control in .cs file public static readonly DependencyProperty FridayCheckBoxEnabledProperty = DependencyProperty.Register( "FridayCheckBoxEnabled", typeof(bool), typeof(DateControl), null); *) Add an overide class in .cs file public override void OnApplyTemplate() { base.OnApplyTemplate() CheckBox checkBox = GetTemplateChild(controlName) as CheckBox; Binding valueBinding = new Binding(uxCheckbox); valueBinding.Mode = BindingMode.TwoWay; valueBinding.Source = this; checkBox.SetBinding(CheckBox.IsEnabledProperty, valueBinding); } Please mark as answer If it is useful.
    Thursday, October 08, 2009 12:42 AM
  • Aaron you are the man. I have been beating my head against the wall trying to figure this out and your solution was the answer. Many, many thanks and if you're ever in Texas, I'll give you a cookie! -Adam
    Tuesday, January 05, 2010 10:27 AM
  • In my blog I described the BoundToSelfControl, which binds itself to itself and in the same time it saves what was passed through DataContext in special field. Take a look at http://dotnetfollower.com/wordpress/2011/06/silverlight-for-windows-phone-7-how-to-bound-usercontrol-to-itself/


    Thanks!

    .NET Follower (http://dotnetfollower.com)

    Thursday, June 09, 2011 3:34 PM