locked
Binding and Custom Controls RRS feed

  • Question

  • I have a simple usercontrol with a textblock and a textbox.  I want to be able to use this in another window.  I coded up INotifyPropertyChanged fields for a Title (to go in TextBlock) and TextBlockContent (to go in TextBox).  When I drag these controls into the window, I can see these two fields "Title" and "TextBlockContent" in properties page but when I go to bind them, "Apply Data Binding" is grayed out, but "Apply Resouce" is not.

    Thinking that the User Control needed explicit DataContext I set it, but still can't "Apply Data Binding" from properties page.  Is this because I need to explictly define these properties as a dependency property rather than implementing INotifyPropertyChange with "Getter, Setters"

    Tx!


    Javaman, Cowboy Coders Unite!
    Wednesday, January 26, 2011 6:33 PM

Answers

  • Found a bit more detail on this for anyone's review and benefit:
    If you want to expose properties that the property window in Visual Studio can use, you have to code up explicit Dependency Properties if you want them to participate in Binding.
     
    Assume you want a reusable custom control that has a Title and Content field (one is a Textblock and the other is a Text Block) which are  contained within a Horizontal StackPanel. 
     
    The XAML below has an interesting binding that binds to this UserControl, by finding the anscestor of the UserControl named TitleField.  This allow you to put the dependency properties right into the code behind of the UserControl itself which is the best thing to do for this type of architecture.
     
     
     
    <UserControl x:Class="UseCaseCreator.UCs.TitleField"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="25" d:DesignWidth="425" xmlns:my="clr-namespace:UseCaseCreator.UCs">
        <StackPanel Orientation="Horizontal">
            <TextBlock MinWidth="125" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:TitleField,AncestorLevel=1}, Path=Title}"></TextBlock>
            <TextBox MinWidth="300" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:TitleField, AncestorLevel=1}, Path=TextBoxContent}"></TextBox>
        </StackPanel>
    </UserControl>
     
     
     
    Next, the dependency properites will look like this in the code-behind of the XAML above.  Note the first string  in the PropertyMetaData will show up in this UserControl when reused as it represents the default value...
     
     public partial class TitleField : UserControl
        {
            public TitleField()
            {
                InitializeComponent();
            }
            public static readonly DependencyProperty TitleProperty = 
                DependencyProperty.Register(
                    "Title", typeof(string), typeof(TitleField),
                     new PropertyMetadata("Not Bound", new PropertyChangedCallback(TitleChangedCB))
                );
     
            public string Title
            {
                get { return (string)GetValue(TitleProperty); }
                set { SetValue(TitleProperty, value); }
            }
            public static readonly DependencyProperty TextBoxContentProperty = 
                DependencyProperty.Register(
                "TextBoxContent", typeof(string), typeof(TitleField),
                new PropertyMetadata("Not Bound")
                );
            public string TextBoxContent
            {
                get { return (string)GetValue(TextBoxContentProperty); }
                set { SetValue(TextBoxContentProperty, value); }
            }
            private static void TitleChangedCB(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
     
            }
        }
     
     
     
    To reuse this control and bind via another XAML "main window", you will need to do this:
     
    <UserControl x:Class="UseCaseCreator.View.TestEntity"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    
                 xmlns:uc="clr-namespace:UseCaseCreator.UCs"
                 xmlns:vm="clr-namespace:UseCaseCreator.ViewModel"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <UserControl.Resources>
            <uc:TitleField x:Key="xuc"></uc:TitleField>
            <vm:TestEntityVM  x:Key="xvm"></vm:TestEntityVM>
        </UserControl.Resources>
        <StackPanel DataContext="{Binding Source={StaticResource xvm}}">
            <uc:TitleField TextBoxContent="{Binding Path=Instance.Priority}" Title="Priority"></uc:TitleField>
            <uc:TitleField TextBoxContent="{Binding Path=Instance.USECASENAME}" Title="UseCaseName"></uc:TitleField>
        </StackPanel>
    </UserControl>

    Javaman, Cowboy Coders Unite!
    • Marked as answer by Mr. Javaman Thursday, January 27, 2011 10:20 PM
    Thursday, January 27, 2011 9:33 PM

All replies

  • Tests show that if a person creates a user control, and they want to expose those fields in XAML (for reuse in another control like a window) you must include DependencyProperty for each field you want to be able to Bind.  The "Apply Data Binding is only shown when you have this construct in code behind of custom control...

        public static readonly DependencyProperty TitleProperty =
          DependencyProperty.Register(
            "Title", typeof(string), typeof(TitleField),
             new PropertyMetadata("Set Property in Designer",new PropertyChangedCallback(TitleChangedCB))
          );
         
        public string Title
        {
          get { return (string)GetValue(TitleProperty); }
          set { SetValue(TitleProperty, value); }
        }
    

    The getter setter is then the GetValue and SetValue of the DependencyProperty, this is slightly different than getter and setters for INotifyPropertyChanged interface implementation. 

    Still one problem I'm not seeing that the changes I make in Properties page are showing in Designer...


    Javaman, Cowboy Coders Unite!
    Wednesday, January 26, 2011 7:22 PM
  • Correct.  You can only databind to DependencyProperties.  Not sure what is causing your other problem.  Are you saying that you set a databinding in the properties panel but the XAML is not being updated?

    Wednesday, January 26, 2011 10:10 PM
  • Yes, with the Dependency Properties I can see the Property filed name via an imported user control but when I set either a binding or the property value itself, it doesn't relfect in the XAML of main window that hosts the control.

    Suggestions?

     


    Javaman, Cowboy Coders Unite!
    Thursday, January 27, 2011 12:23 AM
  • not sure about that one.  It works fine for me.  When the Data binding editor is open, did you hit enter to apply the value?  I believe a double click will work as well.

    Thursday, January 27, 2011 1:18 AM
  • Found a bit more detail on this for anyone's review and benefit:
    If you want to expose properties that the property window in Visual Studio can use, you have to code up explicit Dependency Properties if you want them to participate in Binding.
     
    Assume you want a reusable custom control that has a Title and Content field (one is a Textblock and the other is a Text Block) which are  contained within a Horizontal StackPanel. 
     
    The XAML below has an interesting binding that binds to this UserControl, by finding the anscestor of the UserControl named TitleField.  This allow you to put the dependency properties right into the code behind of the UserControl itself which is the best thing to do for this type of architecture.
     
     
     
    <UserControl x:Class="UseCaseCreator.UCs.TitleField"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="25" d:DesignWidth="425" xmlns:my="clr-namespace:UseCaseCreator.UCs">
        <StackPanel Orientation="Horizontal">
            <TextBlock MinWidth="125" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:TitleField,AncestorLevel=1}, Path=Title}"></TextBlock>
            <TextBox MinWidth="300" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:TitleField, AncestorLevel=1}, Path=TextBoxContent}"></TextBox>
        </StackPanel>
    </UserControl>
     
     
     
    Next, the dependency properites will look like this in the code-behind of the XAML above.  Note the first string  in the PropertyMetaData will show up in this UserControl when reused as it represents the default value...
     
     public partial class TitleField : UserControl
        {
            public TitleField()
            {
                InitializeComponent();
            }
            public static readonly DependencyProperty TitleProperty = 
                DependencyProperty.Register(
                    "Title", typeof(string), typeof(TitleField),
                     new PropertyMetadata("Not Bound", new PropertyChangedCallback(TitleChangedCB))
                );
     
            public string Title
            {
                get { return (string)GetValue(TitleProperty); }
                set { SetValue(TitleProperty, value); }
            }
            public static readonly DependencyProperty TextBoxContentProperty = 
                DependencyProperty.Register(
                "TextBoxContent", typeof(string), typeof(TitleField),
                new PropertyMetadata("Not Bound")
                );
            public string TextBoxContent
            {
                get { return (string)GetValue(TextBoxContentProperty); }
                set { SetValue(TextBoxContentProperty, value); }
            }
            private static void TitleChangedCB(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
     
            }
        }
     
     
     
    To reuse this control and bind via another XAML "main window", you will need to do this:
     
    <UserControl x:Class="UseCaseCreator.View.TestEntity"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    
                 xmlns:uc="clr-namespace:UseCaseCreator.UCs"
                 xmlns:vm="clr-namespace:UseCaseCreator.ViewModel"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <UserControl.Resources>
            <uc:TitleField x:Key="xuc"></uc:TitleField>
            <vm:TestEntityVM  x:Key="xvm"></vm:TestEntityVM>
        </UserControl.Resources>
        <StackPanel DataContext="{Binding Source={StaticResource xvm}}">
            <uc:TitleField TextBoxContent="{Binding Path=Instance.Priority}" Title="Priority"></uc:TitleField>
            <uc:TitleField TextBoxContent="{Binding Path=Instance.USECASENAME}" Title="UseCaseName"></uc:TitleField>
        </StackPanel>
    </UserControl>

    Javaman, Cowboy Coders Unite!
    • Marked as answer by Mr. Javaman Thursday, January 27, 2011 10:20 PM
    Thursday, January 27, 2011 9:33 PM