27

The task: implement the simplest Dependency Property ever, which can be used in xaml like that:

<uc:MyUserControl1 MyTextProperty="{Binding Text}"/>

I think that this answer is quite close. For better readability i copy all my code here (mostly from that answer above).

<UserControl x:Class="Test.UserControls.MyUserControl1"
             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" 
             DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <!-- Text is being bound to outward representative property;
             Note the DataContext of the UserControl -->
        <TextBox Text="{Binding MyTextProperty}"/>
    </Grid>
</UserControl>

and

public partial class MyUserControl1 : UserControl
{
    // The dependency property which will be accessible on the UserControl
    public static readonly DependencyProperty MyTextPropertyProperty =
        DependencyProperty.Register("MyTextProperty", typeof(string), typeof(MyUserControl1), new UIPropertyMetadata(String.Empty));
    public string MyTextProperty
    {
        get { return (string)GetValue(MyTextPropertyProperty); }
        set { SetValue(MyTextPropertyProperty, value); }
    }

    public MyUserControl1()
    {
        InitializeComponent();
    }
}

And this is my MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:uc="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Orientation="Vertical">
        <uc:MyUserControl1 MyTextProperty="my text goes here"/>
        <Button Click="ButtonBase_OnClick" Content="click"/>
    </StackPanel>
</Window>

So far, everything works. However, i find this quite not usefull. What i'd need is

<uc:MyUserControl1 MyTextProperty="{Binding Text}"/>

and being able to change this by setting a DataContext (as you usually do in MVVM)

So i replace the line as above and add my code behind as follows:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        Text = "Initial Text";
        DataContext = this;
    }
    private string _Text;
    public string Text
    {
        get { return _Text; }
        set
        {
            if (value != _Text)
            {
                _Text = value;
                NotifyPropertyChanged("Text");
            }
        }
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        Text = "clicked";
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

Neither the "initial Text" nor the "clicked" is displayed... ever. So my question is how to implement a dept. property correctly to be used with

<uc:MyUserControl1 MyTextProperty="{Binding Text}"/>
GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Martin Booka Weser
  • 3,192
  • 5
  • 28
  • 41

1 Answers1

33

The Text property is located on the DataContext of the MainWindow not of the UserControl.

So change this line <uc:MyUserControl1 MyTextProperty="{Binding Text}"/> into this:

<uc:MyUserControl1 MyTextProperty="{Binding Text, ElementName=MyMainWindow}"/>

Which will tell the Binding that you're talking about the Text element located in you MainWindow. Of course, since in this example I used ElementName, you're going to want to name your window MyMainWindow...

So add this to your MainWindow:

<Window  Name="MyMainWindow" ..... />

If you rather not name your window, you can use the RelativeSource FindAncestor binding like this:

<wpfApplication6:MyUserControl1 MyTextProperty="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>

In both ways, you are asking to find the property named 'Text' in the DataContext of the window.

Blachshma
  • 17,097
  • 4
  • 58
  • 72
  • OK, thanks a lot! Usually i use a ViewModel, which i set in code behind like 'DataContext = new MyViewModel() {Text = "foo"}'. Can you elaborate how the binding can be set then? – Martin Booka Weser Dec 19 '12 at 16:48
  • 1
    Updated my answer, and remember, MyViewModel should implement `INotifyPropertyChanged` if `Text` is not a DependencyProperty. – Blachshma Dec 19 '12 at 16:51
  • thanks again. your update searches up the visual tree for the Window. But how to do the binding to the DataContext which my not be a control but an 'ordinary' class that implements INotifyPropertyChanged and is used as DataContext for the MainWindow? 'AncestorType=MyViewModel' obiously won't work... – Martin Booka Weser Dec 19 '12 at 16:54
  • I'm not sure I understand the scenario.. You have a Window, and you set the DataContext of it to some regular class that doesn't implement INotifyPropertyChanged? Or are you saying you're using this control not in a window? – Blachshma Dec 19 '12 at 16:58
  • i implement the 'Text' Property in MyViewModel which is a regular class and which implements INotifyPropertyChanged. In the constructor of MainWindow i do 'this.DataContext = new MyViewModel()'. When i then use my Control i the MainWindow as stated in my initial question, i want to use the MyViewModel.Text property for binding rather than the MainWindow.Text – Martin Booka Weser Dec 19 '12 at 17:04
  • Well, sorry. I found it: MyTextProperty="{Binding Path=DataContext.Text, ElementName=MyMainWindow}" Thanks a lot anyways!! You deserved your upvote :) – Martin Booka Weser Dec 19 '12 at 17:14