与相对来源绑定

时间:2021-06-10 13:07:09

标签: c# wpf xaml data-binding

我正在尝试了解 RelativeSource 的工作原理。

通过下面的设置,我希望在表单上看到“I am the MainViewModel”文本,但是我在调​​试器中看到一个错误,MainWindow 上没有任何文本:

<块引用>

无法找到引用“RelativeSource FindAncestor, AncestorType='UnderstandingBindings.ViewModels.MainViewModel', AncestorLevel='1''的绑定源。 BindingExpression:Path=SomeProperty;数据项=空;目标元素是 'TextBlock' (Name='myText');目标属性是“文本”(类型“字符串”)

我有一个这样的 ViewModel:

class MainViewModel
{
    public string SomeProperty { get => "I am the MainViewModel"; }
    private readonly ChildViewModel _child = new ChildViewModel();
    public ChildViewModel Child => _child;
}

class ChildViewModel
{
    public string SomeProperty { get => "I am the ChildViewModel"; }
}

MainWindow XAML 看起来像:

<Window x:Class="UnderstandingBindings.Views.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:UnderstandingBindings.Views"
        xmlns:vm="clr-namespace:UnderstandingBindings.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel x:Name="pnlMain">
        <TextBlock x:Name="myText" Text="{Binding SomeProperty, RelativeSource={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"/>
    </StackPanel>
</Window>

数据上下文是这样分配的:

public partial class MainWindow : Window
{
    private readonly MainViewModel _viewModel = new MainViewModel();
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = _viewModel.Child;
    }
}

2 个答案:

答案 0 :(得分:0)

wpf 查找声明绑定的 xaml 元素的祖先。您可以将其视为走上可视化树。

您将使用它绑定到该祖先上的属性,或者必须通过它的 DataContext 属性绑定到视图模型。例如:

<TextBlock x:Name="myText" Text="{Binding DataContext.Child.SomeProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>  

这仅在您的示例中将 DataContext 设置为 MainViewModel 时才有效。如果您希望 Binding 朝着您预期的方向发展,那么您需要在 Child 上添加对 Parent Viewmodel 的引用。

答案 1 :(得分:0)

relative source within a binding 的声明执行以下操作。

<块引用>

通过指定绑定源相对于绑定目标位置的位置来获取或设置绑定源。

这意味着它将绑定的 Source 属性设置为可视化树中的一个元素。这可以是当前元素 (Self) 或祖先(例如,StackPanel 是它包含的 TextBlock 的祖先)或在控件模板的情况下模板化的父元素。这取决于您设置的 Mode。相对源允许您在绑定中指定该元素的属性路径,例如其 DataContextTag 或任何其他属性。

您得到的错误转化为:我在从 MainViewModel 开始的可视化树中搜索了类型为 TextBlock 的实例。然后我检查了下一个祖先 StackPanel,它不是 MainViewModel。然后我检查了下一个祖先 Window,它也不是 MainViewModel。没有其他祖先,所以我找不到任何东西。

您在这里滥用了相对来源。视图模型不是可视化树的一部分,而是充当元素的数据上下文。对于您使用数据上下文的示例,正​​确的方法就足够了。一旦您在 MainWindow 上设置了数据上下文,它就会在所有子控件中继承,如果没有另外指定,例如在元素上显式分配不同的数据上下文。因此,作为 TextBlock 的子代的 MainWindow 将获得与您分配给 DataContextMainWindow 属性相同的数据上下文。

示例中的数据上下文是 ChildViewModel 的一个实例,因此为了绑定到它的 SomeProperty,您不需要相对源绑定,只需要使用自动解析的属性路径对应控件的DataContext(设置为绑定源)。

<TextBlock x:Name="myText" Text="{Binding SomeProperty}"/>

这将导致以下文本:我是 ChildViewModel

如果您想绑定到 SomePropertyMainViewModel,您应该相应地设置 DataContext

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

TextBlock 中的绑定和上面一样,如果你想显示 SomePropertyMainViewModel

<TextBlock x:Name="myText" Text="{Binding SomeProperty}"/>

如果您想绑定 SomePropertyChildViewModel,您可以更改路径。

<TextBlock x:Name="myText" Text="{Binding Child.SomeProperty}"/>

在这两个示例中,它将导致以下文本:I am the MainViewModel