WPF UserControl 不继承父 DataContext
WPF UserControl doesn't inherit parent DataContext
我正在尝试开发可重用的 UserControl,但 运行遇到了绑定问题。我已经创建了一个较小的应用程序来测试它,但无法解决它,或者至少理解为什么它没有按我预期的那样工作。
代码如下。我期望的是我放在 MainWindow.xaml 上的 TestUserControl 的实例将在那里继承 DataContext,就像它下面的 TextBlock 一样。相反,它的 DataContext 似乎为空。 DataContext 没有传递下去有什么原因吗?我必须自动设置它吗?
MainWindow.xaml
<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
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
<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
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:
UserControl 实际上是从其父元素继承了 DataContext。但是,该 DataContext 中没有 TextFromParent
属性(因为它是 MainWindow 实例)。
UserControl 的 XAML 中的绑定应该绑定到 UserControl 本身的 属性,而不是当前 DataContext 之一。因此它必须使用 UserControl 实例作为源对象:
<TextBlock Text="{Binding TextFromParent,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
将 UserControl 的 DataContext 设置为其自身不是一个选项,因为它会阻止从控件的父元素继承 DataContext 值。
但是,您可以在 UserControl 的 XAML 中设置根元素的 DataContext,以避免在可能的许多绑定上设置 RelativeSource:
<UserControl ...>
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock Text="{Binding TextFromParent}" />
</Grid>
</UserControl>
试试这个,你不需要在绑定中使用任何 RelativeSource:
<Grid Name="GrdContent">
<TextBlock Name="TheTextBlock" Text="{Binding TextFromParent}" />
</Grid>
...
public MainWindow()
{
GrdContent.DataContext = this;
InitializeComponent();
SomeText = "New Text!";
}
我已尝试实施上述解决方案(使用相同的代码)但无法正常工作。
主窗口XAML和代码隐藏:
<Grid>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Name="TestTextBlock1" Text="{Binding SomeText}" />
<local:TestUserControl DataContext="{Binding SomeText}" />
<TextBlock Name="TestTextBlock3" Text="{Binding SomeText}" />
</StackPanel>
</StackPanel>
</Grid>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _someText;
public MainWindow()
{
DataContext = this;
InitializeComponent();
SomeText = "Hello World!";
}
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));
}
}
我的用户控件
<UserControl x:Class="ControlDataContext.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:ControlDataContext"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock Text="{Binding TextFromParent}" />
</Grid>
用户控件背后的代码
public partial class TestUserControl : UserControl
{
public TestUserControl()
{
InitializeComponent();
}
public string TextFromParent
{
get => (string)GetValue(TextFromParentProperty);
set => SetValue(TextFromParentProperty, value);
}
public static readonly DependencyProperty TextFromParentProperty = DependencyProperty.Register(
"TextFromParent", typeof(string), typeof(TestUserControl), new PropertyMetadata());
}
图片以黄色高亮显示缺失的行
我正在尝试开发可重用的 UserControl,但 运行遇到了绑定问题。我已经创建了一个较小的应用程序来测试它,但无法解决它,或者至少理解为什么它没有按我预期的那样工作。
代码如下。我期望的是我放在 MainWindow.xaml 上的 TestUserControl 的实例将在那里继承 DataContext,就像它下面的 TextBlock 一样。相反,它的 DataContext 似乎为空。 DataContext 没有传递下去有什么原因吗?我必须自动设置它吗?
MainWindow.xaml
<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
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
<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
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:
UserControl 实际上是从其父元素继承了 DataContext。但是,该 DataContext 中没有 TextFromParent
属性(因为它是 MainWindow 实例)。
UserControl 的 XAML 中的绑定应该绑定到 UserControl 本身的 属性,而不是当前 DataContext 之一。因此它必须使用 UserControl 实例作为源对象:
<TextBlock Text="{Binding TextFromParent,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
将 UserControl 的 DataContext 设置为其自身不是一个选项,因为它会阻止从控件的父元素继承 DataContext 值。
但是,您可以在 UserControl 的 XAML 中设置根元素的 DataContext,以避免在可能的许多绑定上设置 RelativeSource:
<UserControl ...>
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock Text="{Binding TextFromParent}" />
</Grid>
</UserControl>
试试这个,你不需要在绑定中使用任何 RelativeSource:
<Grid Name="GrdContent">
<TextBlock Name="TheTextBlock" Text="{Binding TextFromParent}" />
</Grid>
...
public MainWindow()
{
GrdContent.DataContext = this;
InitializeComponent();
SomeText = "New Text!";
}
我已尝试实施上述解决方案(使用相同的代码)但无法正常工作。
主窗口XAML和代码隐藏:
<Grid>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Name="TestTextBlock1" Text="{Binding SomeText}" />
<local:TestUserControl DataContext="{Binding SomeText}" />
<TextBlock Name="TestTextBlock3" Text="{Binding SomeText}" />
</StackPanel>
</StackPanel>
</Grid>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _someText;
public MainWindow()
{
DataContext = this;
InitializeComponent();
SomeText = "Hello World!";
}
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));
}
}
我的用户控件
<UserControl x:Class="ControlDataContext.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:ControlDataContext"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock Text="{Binding TextFromParent}" />
</Grid>
用户控件背后的代码
public partial class TestUserControl : UserControl
{
public TestUserControl()
{
InitializeComponent();
}
public string TextFromParent
{
get => (string)GetValue(TextFromParentProperty);
set => SetValue(TextFromParentProperty, value);
}
public static readonly DependencyProperty TextFromParentProperty = DependencyProperty.Register(
"TextFromParent", typeof(string), typeof(TestUserControl), new PropertyMetadata());
}