简体   繁体   中英

WPF Binding to a DependencyProperty problem with an custom User Control inside a Window

I need some help. I created a custom User Control, and inserted it into the Main Window. However, Im not able to bind a Property in the Window to a DependencyProperty in the User Control.

Here's the User Control code. XAML:

<UserControl x:Class="SomeExample.UCFullName"
         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:local="clr-namespace:SomeExample"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Margin="0,0,0,0" Orientation="Vertical" >
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
        <StackPanel Orientation="Vertical">
            <Label BorderBrush="White" BorderThickness="1" Content="First Name :" FontSize="14" FontWeight="SemiBold" Foreground="White" Height="30" HorizontalAlignment="Left" Margin="0,2,0,0" VerticalAlignment="Top" Width="100"/>
        </StackPanel>
        <Grid>
            <TextBox Name="FirstName" BorderBrush="Black" BorderThickness="1" FontSize="14" FontWeight="SemiBold" Height="30" HorizontalAlignment="Left" Margin="2,2,0,0" MaxLength="20" VerticalAlignment="Top" Width="100" TextChanged="TxtBlock_TextChanged"/>
        </Grid>
    </StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
        <StackPanel Orientation="Vertical">
            <Label BorderBrush="White" BorderThickness="1" Content="Last Name :" FontSize="14" FontWeight="SemiBold" Foreground="White" Height="30" HorizontalAlignment="Left" Margin="0,2,0,0" VerticalAlignment="Top" Width="100"/>
        </StackPanel>
        <Grid>
            <TextBox Name="LastName" BorderBrush="Black" BorderThickness="1" FontSize="14" FontWeight="SemiBold" Height="30" HorizontalAlignment="Left" Margin="2,2,0,0" MaxLength="20" VerticalAlignment="Top" Width="100" TextChanged="TxtBlock_TextChanged"/>
        </Grid>
    </StackPanel>
</StackPanel>

And here's the code behind

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

namespace SomeExample
{
    public partial class UCFullName : UserControl, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;

        protected void Notify(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion INotifyPropertyChanged implementation


        public string ValueFullName
        {
            get { return (string)GetValue(ValueFullNameProperty); }
            set
            {
                SetValue(ValueFullNameProperty, value);
                Notify("ValueFullName");
            }
        }

        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValueFullNameProperty =
            DependencyProperty.Register("ValueFullName", typeof(string), typeof(UCFullName), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));


        public UCFullName()
        {
            InitializeComponent();
        }

        private void TxtBlock_TextChanged(object sender, TextChangedEventArgs e)
        {
            ValueFullName = FirstName.Text + " " + LastName.Text;
        }
    }
}

This is how it looks:

用户控制

And here's the code of the Main Window:

<Window x:Class="SomeExample.MainWindow"
    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:local="clr-namespace:SomeExample"
    mc:Ignorable="d"
    Title="Some Example" Height="200" Width="400">
<StackPanel Margin="0,0,0,0" Orientation="Vertical" Name="SpManual" Background="Black">
    <GroupBox Header="Name" Foreground="White" FontSize="14" Name="groupBoxCoordinateStart" >
        <local:UCFullName ValueFullName="{Binding Path = PropertyFullName, Mode = TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"></local:UCFullName>
    </GroupBox>
    <StackPanel Name="SpBtnInsert" Orientation="Horizontal" HorizontalAlignment="Center" Visibility="Visible">
        <Button Name="btnShowFullName" BorderBrush="White" BorderThickness="1" FontSize="14" FontWeight="SemiBold" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2,2,0,0" Background="Transparent" Content="Show Name" Foreground="White" Width="98" Click="BtnShowFullName_Click"></Button>
    </StackPanel>
</StackPanel>

And the code behind:

using System.Windows;

namespace SomeExample
{
    /// <summary>
    /// Lógica de interacción para MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public string PropertyFullName { get; set; }
        public MainWindow()
        {
            InitializeComponent();
        }

        private void BtnShowFullName_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Current full name :" + PropertyFullName);
        }
    }
}

主视窗

And of course, I expected that when I pressed the button, I got a message with the full name entered by the user. However, I got nothing.

应用

Edit: Here's the solution to the problem, for people who visit this page with a similar problem.

<local:UCFullName ValueFullName="{Binding Path = PropertyFullName, RelativeSource={RelativeSource AncestorType=Window}}"></local:UCFullName>

You are binding to the wrong AncestorType . Instead of UserControl the type must be Window . Window extends Control but not UserControl .

<local:UCFullName ValueFullName="{Binding Path=PropertyFullName, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=Window}}" />

Also because you set the Binding.Mode to TwoWay , the binding source PropertyFullName must be able to notify the binding target ValueFullName about value changes. To achieve this, you need to implement PropertyFullName as a DependencyProperty to enable two way binding.

As aa side note: The following code can be problematic

public string ValueFullName
{
    get { return (string)GetValue(ValueFullNameProperty); }
    set
    {
        SetValue(ValueFullNameProperty, value);
        Notify("ValueFullName"); // This line might never get called
    }
}

This is just a CLR wrapper for the actual DependencyProperty and will never be invoked by the framework. When using the binding on this property the wrapper will never get called and therefore the event will never get raised.

As BionicCode has pointed out, you can change the AncestorType . Another option is to set DataContext of the Window.

You can either do it in the constructor

public MainWindow()
{
     InitializeComponent();
     DataContext = this;
}

or in the XAML.

<Window DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}">

This way you don't have to specify source in your bindings (as long as you bind to code-behind properties).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM