关于c#:WPF UserControl不继承父DataContext

WPF UserControl doesn't inherit parent DataContext

我正在尝试开发可重用的UserControl,但是遇到绑定问题。 我创建了一个较小的应用程序来对其进行测试,但无法对其进行整理,或者至少了解了为什么它无法按我的预期工作。

代码如下。 我所期望的是我放在MainWindow.xaml上的TestUserControl实例将继承那里的DataContext,就像它的TextBlock一样。 相反,它的DataContext似乎为null。 DataContext没有传递下去是有原因的吗? 是否需要自动设置?

MainWindow.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Window x:Class="WpfTestApp.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:WpfTestApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Orientation="Vertical">
        <local:TestUserControl TextFromParent="{Binding SomeText}" />
        <TextBlock Name="TestTextBlock" Text="{Binding SomeText}" />

    </StackPanel>
</Window>

MainWindow.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;

namespace WpfTestApp
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _someText;

        public MainWindow()
        {
            DataContext = this;

            InitializeComponent();

            SomeText ="New Text!";
        }

        public string SomeText
        {
            get { return _someText; }
            set
            {
                _someText = value;
                NotifyOnPropertyChanged();
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyOnPropertyChanged([CallerMemberName] string propertyName ="")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

TestUserControl.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
<UserControl x:Class="WpfTestApp.TestUserControl"
             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:WpfTestApp"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock Name="TheTextBlock" Text="{Binding TextFromParent}" />

    </Grid>
</UserControl>

TestUserControl.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System.Windows;
using System.Windows.Controls;

namespace WpfTestApp
{
    public partial class TestUserControl : UserControl
    {
        public TestUserControl()
        {
            InitializeComponent();
        }

        public string TextFromParent
        {
            get { return (string)GetValue(TextFromParentProperty); }
            set { SetValue(TextFromParentProperty, value); }
        }

        public static readonly DependencyProperty TextFromParentProperty = DependencyProperty.Register(
           "TextFromParent", typeof(string), typeof(TestUserControl), new PropertyMetadata());
    }
}

程序在运行时如下所示,第一个文本为空白,然后是具有有效绑定的TextBlock:
enter image description here


UserControl实际上是从其父元素继承DataContext的。 但是,该DataContext中没有TextFromParent属性(因为它是MainWindow实例)。

UserControl的XAML中的Binding应该绑定到UserControl本身的属性,而不是当前DataContext之一。 因此,它必须使用UserControl实例作为源对象:

1
2
<TextBlock Text="{Binding TextFromParent,
                  RelativeSource={RelativeSource AncestorType=UserControl}}"
/>

不能将UserControl的DataContext设置为其自身,因为这样可以防止从控件的父元素继承DataContext值。

但是,您可以在UserControl的XAML中设置根元素的DataContext,以避免在可能的许多绑定上设置RelativeSource:

1
2
3
4
5
<UserControl ...>
    <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
        <TextBlock Text="{Binding TextFromParent}" />
    </Grid>
</UserControl>

试试看,您不需要在绑定中使用任何RelativeSource:

1
2
3
4
<Grid Name="GrdContent">
    <TextBlock Name="TheTextBlock" Text="{Binding TextFromParent}" />

</Grid>

...

1
2
3
4
5
6
7
8
public MainWindow()
        {
            GrdContent.DataContext = this;

            InitializeComponent();

            SomeText ="New Text!";
        }