【问题标题】:UserControl's DataContextUserControl 的 DataContext
【发布时间】:2011-07-01 22:23:51
【问题描述】:

我正在创建一个UserControl 我想使用这样的东西:

<controls:ColorWithText Color="Red" Text="Red color" />

到目前为止,我已经实现了类似的控件:

<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
    <StackPanel Orientation="Horizontal" >
        <Border Width="15" Height="15" Background="{Binding Color, ElementName=ThisControl}" />
        <TextBlock Text="{Binding Text, ElementName=ThisControl}" />
    </StackPanel>
</UserControl>

其中ColorText 是代码中定义的控件的依赖属性。这可行,但似乎没有必要每次都指定ElementName

另一个有效的选项是使用

<UserControl x:Class=… DataContext="{Binding ElementName=ThisControl}" Name="ThisControl">

并没有指定ElementNames,但这对我来说似乎也不是一个干净的解决方案。

我有两个问题:

  1. 为什么&lt;UserControl DataContext="{RelativeSource Self}"&gt; 不起作用?
  2. 执行此类操作的最佳方法是什么?

【问题讨论】:

    标签: wpf xaml datacontext


    【解决方案1】:

    我知道这个问题已经得到解答,但没有任何解释能说明DataContext 及其工作原理。这个链接在这方面做得很好。

    EVERYTHING YOU WANTED TO KNOW ABOUT DATABINDING IN WPF, SILVERLIGHT AND WP7 (PART TWO)

    回答您的问题 #1

    为什么&lt;UserControl DataContext="{RelativeSource Self}"&gt; 不起作用?

    这是上述链接的摘要。 DataContext 不应在 UserControl 元素级别设置为 Self。这是因为它破坏了DataContext 的继承。如果您确实将其设置为 self 并且将此控件放置在 Window 或其他控件上,它将不会继承 Windows DataContext

    DataContext 被继承到 XAML 的所有较低元素和 UserControls 的所有 XAML,除非它在某处被覆盖。通过将UserControl DataContext 设置为自身,这将覆盖DataContext 并破坏继承。相反,将其嵌套在 XAML 中的一个元素深处,在您的情况下为 StackPanel。将DataContext绑定放在这里,绑定到UserControl。这会保留继承。

    有关此内容的详细说明,另请参阅下面的此链接。

    A SIMPLE PATTERN FOR CREATING RE-USEABLE USERCONTROLS IN WPF / SILVERLIGHT

    回答您的问题 #2
    做这种事情的最佳方法是什么?

    参见下面的代码示例。

    <UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
        <StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=ThisControl}">
            <Border Width="15" Height="15" Background="{Binding Color" />
            <TextBlock Text="{Binding Text}" />
        </StackPanel>
    </UserControl>
    

    请注意,执行此操作后,您将不需要在每个绑定上使用 ElementName

    【讨论】:

    • 唯一保留 UserControl 外部绑定的优雅解决方案。谢谢。
    • 很好的解释!
    • 这是迄今为止最有帮助的答案,因为它不会破坏数据上下文继承
    【解决方案2】:

    为什么不能使用&lt;UserControl DataContext="{RelativeSource Self}"&gt;

    这就是你将如何使用控件

    <Grid DataContext="{StaticResource ViewModel}">
        <!-- Here we'd expect this control to be bound to -->
        <!-- ColorToUse on our ViewModel resource          -->
        <controls:ColorWithText Color="{Binding ColorToUse}" />
    </Grid>
    

    现在,因为我们在控件中硬编码了数据上下文,它会尝试在 ColorWithText 对象而不是您的 ViewModel 上查找 ColorToUse 属性,这显然会失败。

    这就是你不能在用户控件上设置 DataContext 的原因。感谢Brandur 让我明白这一点。

    做这种事情的最佳方法是什么?

    相反,您应该在控件的第一个子 UI 元素中设置 DataContext。

    在你的情况下,你想要

    <StackPanel 
      DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
      Orientation="Horizontal" >
    

    现在您有了一个引用控件的 DataContext,因此您可以使用相对绑定访问该控件的任何属性。

    【讨论】:

    • 这绝对是最好的解决方案!它保留了控件绑定并且不需要任何特定的元素命名。
    【解决方案3】:

    您应该使用

    {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=Color}
    

    对于数据绑定相关的疑问,请始终参考此表。
    http://www.nbdtech.com/Blog/archive/2009/02/02/wpf-xaml-data-binding-cheat-sheet.aspx

    【讨论】:

    • 我应该每次都写这个吗?我不想绑定到这个控件中的任何其他东西,我认为重复代码是不好的。
    【解决方案4】:

    第一个,试试:

    <UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
    

    对于第二个问题,我认为使用ElementNameAncestorBinding 是绑定UserControl 属性的最佳方式。

    【讨论】:

    • 问题。如果你这样设置RelativeSource,它怎么知道这个控件的VM是什么?你是怎么设置的?
    • 我知道这是一篇旧帖子,但对于其他来她的人...您不会为个人控件设置 VM。您在控件上设置属性,这些属性应该足以使其“工作”。如果控件依赖于某个虚拟机或紧密耦合/依赖于放置到特定上下文中工作,那么它不是“控件”。你违反了关注点分离原则。
    • 据我所知,这会破坏任何 UserControlDependencyProperty。请参阅nikolalukovic.com/programming/… 和/或@jdawiz 的答案。
    【解决方案5】:

    您可以在构造函数本身中将 datacontext 设置为 self。

    public ColorWithText()
    {
     InitializeComponent();
     DataContext = this;
    }
    

    现在你可以简单地说

    <UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
        <StackPanel Orientation="Horizontal" >
            <Border Width="15" Height="15" Background="{Binding Color}" />
            <TextBlock Text="{Binding Text}" />
        </StackPanel>
    </UserControl>
    

    【讨论】:

    • 甚至在加载的事件中 this.Loaded += (sender, e) => { this.DataContext = this; };
    • 那是非常简单和优雅的。我喜欢它。
    猜你喜欢
    • 2015-02-16
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 2016-04-21
    • 2018-03-12
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多