profou
profou

Reputation: 217

Binding a property with another property owned to a user control

I want to realize a binding between several properties. Is it possible ?

I have a main window class named "MainWindow" which owns a property "InputText". This class contains a user control named MyUserControl. MyUserControl has a text box bound to a dependency property "MyTextProperty"

I would like to bind the property "InputText" of my main window with the dependency property "MyTextProperty" of my user control. So, if the user writes a text, I want that the properties "InputText", "MyTextProperty", "MyText" are updated.

User control code:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MyUserControl.xaml
    /// </summary>
    public partial class MyUserControl : UserControl
    {

        public string MyText
        {
            get { return (string)GetValue(MyTextProperty); }
            set { SetValue(MyTextProperty, value); }
        }

        public static readonly DependencyProperty MyTextProperty =
            DependencyProperty.Register("MyText", typeof(string), typeof(MyUserControl), new PropertyMetadata(0));



        public MyUserControl()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string property)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
            }
        }

    }
}

WPF user control code:

<UserControl x:Class="WpfApplication1.MyUserControl"
             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="100"
             Background="Black">
    <Grid>
        <TextBox Height="20" Width="100" Text="{Binding MyText}"></TextBox>
    </Grid>
</UserControl>

Main window code:

using System;
using System.Linq;
using System.Windows;
using System.ComponentModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string inputText;

        public string InputText
        {
            get { return inputText; }
            set 
            { 
                inputText = value;
                NotifyPropertyChanged("InputText");
            }
        }

        public MainWindow()
        {
            this.DataContext = this;

            InitializeComponent();

        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(String property)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}

WPF main window code:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:myNS="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="80" Width="300">
    <Grid>
        <StackPanel Orientation="Vertical">
            <myNS:MyUserControl x:Name="test" MyText="{Binding InputText}"></myNS:MyUserControl>
            <Button Name="cmdValidation" Content="Validation" Height="20"></Button>
        </StackPanel>
    </Grid>
</Window>

Thank you !

Upvotes: 0

Views: 2572

Answers (2)

J.H.
J.H.

Reputation: 4322

If you want your posted code to work with as little changes as possible then:

In MainWindow.xaml, change

MyText="{Binding InputText}"

to

MyText="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.InputText, Mode=TwoWay}"

You need TwoWay if you want the UC to update InputText.

Also, in MyUserControl.xaml.cs, in your DependencyProperty.Register statement, you have the PropertyMetadata default value set to 0 for a string -- change it to something appropriate for a string - like null or string.empty.

public static readonly DependencyProperty MyTextProperty =
    DependencyProperty.Register("MyText", typeof(string), typeof(MyUserControl), new PropertyMetadata(null));

If you want to change the code up a bit, you could make this more complex in the user control but simpler when you use it by:

Making the dependency property, MyText, bind two way by default

Stop setting the DataContext in the user control

Change the UC xaml text binding to use a relative source to the UC

I always find code easier to understand, so here are modified versions of your files: 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:myNS="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="180" Width="300">
    <Grid>
        <StackPanel Orientation="Vertical">
            <TextBlock>
                <Run Text="MainWindow.InputText: " />
                <Run Text="{Binding InputText}" />
            </TextBlock>
            <TextBlock>
                <Run Text="MyUserControl.MyText: " />
                <Run Text="{Binding ElementName=test, Path=MyText}" />
            </TextBlock>
            <myNS:MyUserControl x:Name="test" MyText="{Binding InputText}"></myNS:MyUserControl>
            <Button Name="cmdValidation" Content="Validation" Height="20"></Button>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Windows;
using System.ComponentModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string inputText = "Initial Value";

        public string InputText
        {
            get { return inputText; }
            set
            {
                inputText = value;
                NotifyPropertyChanged("InputText");
            }
        }

        public MainWindow()
        {
            this.DataContext = this;

            InitializeComponent();

        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(String property)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}

MyUserControl.xaml

<UserControl x:Class="WpfApplication1.MyUserControl"
             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="100"
             Background="Black">
    <Grid>
        <TextBox Height="20" Width="100" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=MyText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
    </Grid>
</UserControl>

MyUserControl.xaml.cs

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MyUserControl.xaml
    /// </summary>
    public partial class MyUserControl : UserControl
    {

        public string MyText
        {
            get { return (string)GetValue(MyTextProperty); }
            set { SetValue(MyTextProperty, value); }
        }

        public static readonly DependencyProperty MyTextProperty =
            DependencyProperty.Register("MyText", typeof(string), typeof(MyUserControl), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });

        public MyUserControl()
        {
            InitializeComponent();
        }
    }
}

Upvotes: 4

Mike Eason
Mike Eason

Reputation: 9713

Firstly,

this.DataContext = this;

No. Just, no. You are overriding the DataContext of the UserControl set by it's parent window.

For your UserControl, give it an x:Name, and bind directly to the dependency property.

<UserControl
    ...
    x:Name="usr">

    <TextBox Text="{Binding MyText, ElementName=usr}" ... />

After you've done that, you can then simply bind your MyText property to the DataContext of the MainWindow.

<myNS:MyUserControl x:Name="test" MyText="{Binding InputText}" />

Upvotes: 1

Related Questions