WPF绑定内部控件与父级数据上下文

4

我创建了一个用户控件

<UserControl x:Class="MyApp.MyControl"
         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"  x:Name="uc">
<Grid Width="Auto" Height="Auto">
    <TextBlock Text="{Binding Path=DataContext.TextContent, ElementName=uc}"/>
    <TextBlock Text="{Binding Path=DataContext.TextContent2, ElementName=uc}"/>
</Grid>

我希望在定义的控件(uc)中,子控件能够绑定到uc.DataContext的属性。我使用定义的控件如下:

<Window x:Class="Tms.TMSClient.Views.MainWindow" Name="window"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:control="clr-namespace:MyApp"
    xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary">      

    <control:MyControl DataContext="{Binding Path=MyControlVM}"/>

窗口所分配的DataContext结构为:WindowVM.MyControlVM.TextContent。

给定的代码无法工作,因为文本框的DataContext绑定到了WindowVM而不是绑定到MyControlVM。我认为问题可能是因为内部文本框的绑定会比定义的控件(uc)先执行,因此uc的绑定DataContext尚未生效。

我想要实现的是:自定义控件(MyControl)将绑定到其对应的视图模型(MyControlVM),并且MyControl的内部元素将绑定到MyControlVM的属性。

您有解决这个问题的任何方案吗?


你是否偶然在 MyApp.MyControl 中执行类似于 DataContext = this 的操作?此外,如果您不更改控件中的 DataContext,则 {Binding TextContent} 应该就足够了。 - dkozl
1
你将 MyControlVM.DataContext 设置为 MyControlVM,由于它是通过可视树继承的,因此默认情况下,直到你更改它,里面的所有内容都将具有相同的 DataContext。在你的情况下,TextBlockGridUserControl 将从外部设置为 MyControlVM,并具有相同的 DataContext - dkozl
3个回答

6

如果我理解你的意思正确,你想从你的MyControl视图模型数据绑定到一个MyControl UserControl内部的TextBox.Text属性。如果是这样,那么您可以使用RelativeSource Binding或者你已经在使用的ElementName语法。

首先,请确保您的视图模型被设置为UserControlDataContext

public MyControl()
{
    DataContext = new YourControlViewModel();
}

在 WPF 中,由于子控件会自动继承其父级的 DataContext 对象,因此您可以通过 UserControl 的 XAML 使用 MyControl.DataContext 属性从 TextBox 引用此视图模型:

<TextBlock Text="{Binding DataContext.TextContent, 
    RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

这就是您所需要的全部内容。


4

默认情况下,每个控件都会从其父控件继承其数据上下文。因此,没有必要明确绑定它。

实际上,当您想要将控件的数据上下文绑定到嵌套属性时,您必须指定这一点:

<control:MyControl DataContext="{Binding Path=TextContent}"/>

定义的控件可能有许多内部元素,因此直接绑定并不好。我想要的是:自定义控件(MyControl)将绑定到其相应的视图模型(MyControlVM),而MyControl的内部元素将绑定到MyControlVM的属性。 - Nghia Le
哇,@ThomasLee,你真的没有听任何人的话,是吗?DataContext默认情况下会被所有子控件自动继承。我认为你的代码没有问题,所以一定有其他因素干扰了它。 - Sheridan

4
<TextBlock Text="{Binding Path=TextContent}"/>

在我的测试应用程序中,它可以正常工作。

MainWindow.xaml

<Window x:Class="DataContextTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:DataContextTest"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <my:MyOuterDataContext />
</Window.DataContext>
<Grid>
    <my:MyControl DataContext="{Binding Path=MyInnerDataContext}" />
</Grid>

MyControl.xaml

<UserControl x:Class="DataContextTest.MyControl"
         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="300" d:DesignWidth="300">
<Grid>
    <TextBlock Text="{Binding Path=TextContent}" />
</Grid>

数据上下文:

public class MyOuterDataContext
{
    public MyInnerDataContext MyInnerDataContext { get; set; }

    public MyOuterDataContext()
    {
        MyInnerDataContext = new MyInnerDataContext();
    }
}

public class MyInnerDataContext
{
    public string TextContent { get { return "foo"; } }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接