是否可以在一个用户控件中有两个不同的数据上下文

本文关键字:两个 上下文 数据 用户 一个 控件 是否 | 更新日期: 2023-09-27 18:32:01

是否可以在一个用户控件中有两个不同的DataContext?我想访问我的一个Model对象,以及 MainViewModel 中的属性。例子:

类数据DataContext(模型中定义的对象):

<DataGrid x:Name="PropertiesControl1" Height="auto" ItemsSource="{Binding ClassDatas}" HeadersVisibility="None" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Fields}" Header="" Width="*" IsReadOnly="True"/>
            </DataGrid.Columns>
</DataGrid>

主视图模型DataContext

<TextBox x:Name="txtFields" Text="{Binding FieldsTextProperty, UpdateSourceTrigger=PropertyChanged}" Height="23" TextWrapping="NoWrap" Background="#FFCBEECD" AcceptsReturn="False" >.....</TextBox>

进一步,如何将它们结合起来?

<TextBox x:Name="txtFields" Text="{Binding FieldsTextProperty, UpdateSourceTrigger=PropertyChanged}" Height="23" TextWrapping="NoWrap" Background="#FFCBEECD" AcceptsReturn="False" >
                <i:Interaction.Triggers>
                    <iex:KeyTrigger Key="Enter">              
                        <cmd:EventToCommand Command="{Binding DataContext.AddFieldCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" PassEventArgsToCommand="True"/>
                    </iex:KeyTrigger>
                </i:Interaction.Triggers>
</TextBox>

FieldsTextProperty是来自MainViewModelDataContextEventToCommand包含来自我的对象的DataContext

是否可以在一个用户控件中有两个不同的数据上下文

不,控件只能将单个对象设置为其DataContext属性

但是,绑定不必绑定到它的DataContext。可以使用其他绑定属性为绑定指定不同的源。用于更改绑定源的常用属性包括 SourceRelativeSourceElementName

  • 可以仅更改特定绑定的绑定源,例如在Command绑定中所做的那样

  • 或者,可以通过设置控件的 DataContext 属性或将控件的 属性绑定到其他内容来更改整个控件的它

实际上,我看到WPF初学者对DataContext感到困惑,以至于我写了一篇关于它的博客文章。您可能有兴趣阅读它:您所说的"数据上下文"是什么?

这是我用来帮助演示DataContext的代码块:

假设我们将一个窗口绑定到一个名为 ClassA 的对象。 ClassA有一个名为ClassB的属性,ClassAClassB都有一个名为Name的属性。

下面是一个 XAML 块,它说明了 DataContext 的工作原理。它还包括一个示例,说明控件如何引用不在自己的 DataContext 中的属性。

public partial class MyWindow: Window
{
    public MyWindow()
    {
       InitializeComponent();
       this.DataContext = new ClassA();
    }
}
public class ClassA
{
    public string Name { get; set; }
    public ClassB ClassB { get; set; }
}
public class ClassB
{
    public string Name { get; set; }
}
<!-- DataContext set to ClassA in initialization code -->
<Window x:Name="MyWindow"> 
 
    <!-- DataContext here is not specified, so it's inherited 
         from its parent's DataContext, which is ClassA -->
    <StackPanel> 
 
        <!-- DataContext inherited from parent, which is 
             ClassA, so this will display ClassA.Name -->
        <Label Content="{Binding Name}" />
 
         <!-- DataContext is still ClassA, however we are 
              setting it to ClassA.ClassB with a binding -->
        <StackPanel DataContext="{Binding ClassB}">
 
            <!-- DataContext inherited from parent, which is 
                 ClassB, so this will display ClassB.Name -->
            <Label Content="{Binding Name}" />
 
            <!-- DataContext is still ClassB, but we are 
                 binding to the Window's DataContext.Name, 
                 which is ClassA.Name -->
            <Label Content="{Binding 
                       ElementName=MyWindow, 
                       Path=DataContext.Name}" /> 
        </StackPanel>
 
        <!-- We've left the StackPanel with its DataContext 
             bound to ClassB, so this Label's DataContext 
             is ClassA (inherited from parent StackPanel), 
             and we are binding to ClassA.ClassB.Name -->
        <Label Content="{Binding ClassB.Name}" />
    </StackPanel>
</Window>

是的,您的用户控件可以具有 DataContext,并且该用户控件中的任何控件也可以具有不同的 DataContext,但在您的方案中,这不是您应该执行的操作。

您的 TextBox 应使用绑定到 UserControl 的基础 ViewModel 的简单命令,并且在执行该命令时,应使用消息总线从 MainViewModel 或任何其他 ViewModel 获取所需的任何内容。消息总线是 ViewModels 应该在 MVVM 中通信和交换信息的方式。