【问题标题】:Why does binding to a DepenencyProperty on a user control use the user control's Data Context?为什么绑定到用户控件上的 DepenencyProperty 使用用户控件的数据上下文?
【发布时间】:2013-02-07 20:12:31
【问题描述】:

假设我有一个 WPF 应用程序 (exe),它在 MainWindow.xaml 中有这个:

<Grid>
    <extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}"/>
    <extraControls:MyUserControl MyDependencyProperty="{Binding Something}" />
</Grid>

我的 MainWindow.xaml.cs 看起来像这样:

public MainWindow()
{
    DataContext = new MainWindowVM();
    InitializeComponent();
}

我的 MainWindowVM.cs 有一个属性设置为 Something 通知属性更改。

用户控件是在单独的 dll 中制作的。您可能猜到了,MyMVVMUserControl 将 DataContext 设置为视图模型。

public MyMVVMUserControl()
{
    DataContext = new MyMVVMUserControlVM();
    InitializeComponent();
}

MyUserControl 在后面的代码中没有设置 DataContext。

所以有趣的是,它们的MyDependencyProperty 设置完全相同。

但是MVVM版本不行。

深入研究后,我发现 MainWindow.xaml 中的{Binding Something} 使用 MyMVVMUserControl 的 View Model 设置作为 DataContext(而不是 MainWindow.cs 中设置的 DataContext (设置为 MainWindowVM))。

我的问题是为什么?

为什么 WPF 会查看用户控件内部并将其 DataContext 用于实际应用程序中的绑定?

(注意:我知道我可以通过在绑定中设置源来解决这个问题,但我希望其他人能够使用我的用户控件。但是对于这个问题,我现在有一个内置的“陷阱”任何我想使用我的用户控件的人。)

【问题讨论】:

  • 我强烈推荐 Snoop 检查正在运行的应用程序并查看每个元素的 DataContext 是什么。
  • 另外,InitalizeComponent() 应该先行。
  • 如果 InitalizeComponent 是第一个,那么任何需要 CataContext 的控件都不会拥有它...对吗?
  • @HighCore - 感谢 Snoop 的指针。我会看看它。但在这种情况下,我在绑定中添加了 PresentationTraceSources.TraceLevel=High。这清楚地告诉我 MainWindow.xaml 中绑定的 DataContext 正在使用用户控件的 DataContext。

标签: c# .net wpf binding


【解决方案1】:

我想我理解你的问题,我会给出一个适合我的解决方案(我以前遇到过这个问题)。想法是您在后面的代码中为您设置DataContext MyMVVMUserControl 的接缝,然后它从中获取绑定。

我为此找到的解决方案是在代码中设置数据上下文,而不是在用户控件中。为UserControl 的子项设置数据上下文。例如,假设这是您的 UserControl 的 Xaml:

<UserControl ... x:Name="userControl">
    <Grid x:Name="rootContainer">
         ...
    </Grid>
</UserControl>

然后在后面的代码中设置rootContainer的数据上下文,这样所有的视觉孩子都可以访问控件数据上下文,并且用户控件数据上下文为空。

...
rootContainer.DataContext = new UserControlViewModel();
...

希望这可以帮助您解决问题...

【讨论】:

    【解决方案2】:

    您真的不应该从UserControl 内部设置UserControl 的DataContext。这样做可以防止任何其他 DataContext 传递给 UserControl,这破坏了 WPF 拥有独立 UI 和数据层的最大优势之一。

    如果 DataContext 未设置为其他任何值,WPF 对象仅从父对象继承其 DataContext。在创建MyMVVMUserControl 时,您将DataContext 设置为新的MyMVVMUserControlVM,从而防止DataContext 从MainWindow 继承。

    因此,您的 MVVMUserControl 将其 DataContext 设置为您的 MyMVVMUserControlVM 是正常的,因为您在 UserControl 的构造函数中明确设置了它。

    这是设计使然。 WPF/MVVM 中的 UI 对象只是数据层的可视化表示,因此设置数据层然后尝试将您的属性绑定到不在数据层上的东西没有多大意义。

    以这行代码为例:

     <UserControl DataContext="{Binding ClassA}" Content="{Binding Name}" />
    

    这会将Content 属性绑定到UserControl.DataContext.Name,即ClassA.Name。如果这会导致绑定到UserControl.Parent.DataContext.Name,那将没有多大意义,因为绑定应该引用当前对象的DataContext,而不是父对象的DataContext。

    所以我唯一一次从UserControl 本身内部设置UserControl 的DataContext 是如果UserControl 是它自己的独立对象,它永远不会与来自其余部分的数据交互应用。迄今为止从未有过:)

    通常我的UserControls 几乎总是以下两种情况之一:

    • ViewModel(或Model)的可视化表示,例如CustomerUserControl 用于CustomerViewModel,在这种情况下,我将DataContext 传递给他们使用时需要的DataContext

      例如,

      <local:CustomerUserControl DataContext="{Binding SelectedCustomer}" />
      

      或

      <DataTemplate DataType="{x:Type local:CustomerModel}">
          <local:CustomerUserControl />
      </DataTemplate>
      
    • 或者一个自我维持的 UI 对象,它通过自定义 DependencyProperties 接收它需要的任何外部数据,并在控件的代码隐藏中执行任何其他逻辑,例如具有 SelectedDate 依赖项的 DatePicker 控件属性,或具有Equation 和Value 的依赖属性的CalculatorUserControl

      <local:DatePickerUserControl SelectedDate="{Binding SomeDate}" />
      
      <local:CalculatorUserControl Equation="{Binding SomeString}" 
                                   Value="{Binding SomeDouble}" />
      

    在您的情况下,听起来您应该使用第一种情况,并且应该将 ViewModel 传递到包含所需数据的 UserControl 中。

    <extraControls:MyMVVMUserControl DataContext="{Binding MyMVVMUserControlVM}"
                                     MyDependencyProperty="{Binding Something}">
    

    或

    <extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}">
        <extraControls:MyMVVMUserControl.DataContext>
            <viewModels:MyMVVMUserControlVM />
        </extraControls:MyMVVMUserControl.DataContext>
    <extraControls:MyMVVMUserControl />
    

    【讨论】:

    • 嗯,我一定没有明白这一点。我有一个用户控件,它为 UI 建模以输入特定对象的数据。但它很复杂,并不是我需要的一切都在对象中。所以,我试图将对象作为依赖属性传递,然后使用视图模型(在用户控件中)来运行 UI、逻辑和命令......
    • @Vaccano 我已经更新了我的答案。我的用户控件几乎总是两件事之一。它们可以是 ViewModel(或 Model)的可视化表示,例如 CustomerUserControl 对应于 CustomerViewModel,在这种情况下,我将在使用它们时将它们需要的 DataContext 传递给它们。或者,它们可以是通过自定义 DependencyProperties 接收所需数据的自持 UI 对象,例如具有 SelectedDate 依赖属性的 DatePicker 控件。听起来您应该使用第一种情况,并且应该将 ViewModel 传递到包含所需数据的 UserControl 中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-30
    相关资源
    最近更新 更多