0
votes

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 !

2
Are you getting an error with the code above? What exact problem are you having with trying to bind these properties.Michael
Your UserControl should inherit the DataContext of the MainWindow, so why not bind the TextBox's Text property directly to the InputText property?user2697817

2 Answers

4
votes

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();
        }
    }
}
1
votes

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}" />