如何从UserControl访问父级的DataContext

11

我需要从一个我在WPF中创建的UserControl(包含文本框和列表框的网格:我需要向此列表框中插入项目)访问容器的DataContext:哪种方法最好?

我考虑将DataContext作为参数传递给用户控件,但认为有一种更清晰的方法来实现它。

5个回答

15
通常情况下,DataContext会被继承,只需不在UserControl上明确设置它,它就会从其父级获取。如果您必须设置它,仍然可以使用Parent属性获取父级,然后可以将其安全地转换为FrameworkElement,如果不为null,则可以获取其DataContext

如果目标控件嵌套在一个或多个其他控件中,则目标控件的父级可能会更改DataContext,因此您可能无法在目标控件中看到预期的对象。我认为应该有一个DataContext堆栈来访问。 - Kind Contributor
@Todd:嗯,你可以编写一个方法,遍历整个树并获取所有唯一的“DataContexts”,我认为我之前甚至做过类似的事情。 - H.B.

8
请将此BindingProxy类添加到您的项目中:
using System.Windows;

namespace YourNameSpace
{
    /// <summary>
    /// Add Proxy <ut:BindingProxy x:Key="Proxy" Data="{Binding}" /> to Resources
    /// Bind like <Element Property="{Binding Data.MyValue, Source={StaticResource Proxy}}" />   
    /// </summary>
    public class BindingProxy : Freezable
    {
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy));
    }
}
  1. 将BindingProxy添加到您的UserControl资源中。
  2. 将BindingProxy的“Data”属性设置为所需内容,例如搜索父级窗口。 Data =“{Binding RelativeSource = {RelativeSource FindAncestor,AncestorType = {x:Type Window}},Path = DataContext}” 如果您需要更复杂的内容,可以使用自定义转换器。

现在,您可以访问该父级的DataContext:{Binding Data.MyCommand,Source ={StaticResource BindingProxy}}

<UserControl 
         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:common="clr-namespace:YourNameSpace;assembly=YourAssembly"
         mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <common:BindingProxy x:Key="BindingProxy" Data="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext}" />
    </UserControl.Resources>
    <Border>
        <Button Command="{Binding Data.MyCommand, Source={StaticResource BindingProxy}}">Execute My Command</Button>
        <!-- some visual stuff -->
    </Border>
</UserControl>

最优雅的解决方案在这里! - cdie
好的解决方案。 - Max

8

我有时会有嵌套的用户控件,而孙子级别的用户控件有时需要祖父级视图的数据上下文。目前我发现的最简单的方法(我有点新手)是使用以下内容:

<Shared:GranchildControl DataContext="{Binding RelativeSource={RelativeSource 
                    FindAncestor, AncestorType={x:Type GrandparentView}},
                    Path=DataContext.GrandparentViewModel}" />

如果您需要更具体的信息,我在我的博客上写了一个更详细的例子,可以点击这里查看。


1

H.B.回答了你标题中的问题。

然而,这段文字提出了一个不同的设计问题。我建议您重新考虑您的设计。

只要没有其他人明确地覆盖,控件就会继承其祖先的DataContext属性。
如果用户控件需要数据,则应从其数据源(用户控件的视图模型)获取数据。因此,在这种情况下,用户控件可以从SomeViewModel实例上公开的ListItemsForDisplay属性获取所需的数据。无需获取父级并进行转换.. 更加简洁。

<ContainerType DataSource={Binding SomeViewModel}>
  <YourUserControl>
    <ListBox ItemsSource={Binding ListItemsForDisplay}"/>
...

0
在这种情况下,UserControl 将获取 DataContext 窗口。
<Window>
    <local:MyUserControl DataContext="{Binding}"/>
</Window>

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