关于DataContext,硬编码值,绑定表达式,模板和嵌套控件的操作顺序

时间:2011-04-25 22:30:41

标签: wpf wpf-controls binding datacontext

这困扰了我一段时间,我已经厌倦了解决这个问题。在WPF中,什么是"操作顺序"说到:

  • 设置DataContext
  • 继承DataContext
  • 评估"硬编码"财产价值
  • 评估{Binding}属性值

所有这些都考虑了嵌套控件和模板(当应用模板时)。

我有很多有问题的情况,但这只是一个例子:

自定义用户控制

<UserControl x:Class="UserControls.TestUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             >
    <StackPanel>
        <Label Content="{Binding Label1}" />
        <Label Content="{Binding Label2}" />
    </StackPanel>
</UserControl>

用户控制代码隐藏

using System;
using System.Windows;
using System.Windows.Controls;

namespace UserControls
{
    public partial class TestUserControl : UserControl
    {
        public static readonly DependencyProperty Label1Property = DependencyProperty.Register("Label1", typeof(String), typeof(TestUserControl), new FrameworkPropertyMetadata(OnLabel1PropertyChanged));
        public String Label1
        {
            get { return (String)GetValue(Label1Property); }
            set { SetValue(Label1Property, value); }
        }

        public static readonly DependencyProperty Label2Property = DependencyProperty.Register("Label2", typeof(String), typeof(TestUserControl), new FrameworkPropertyMetadata(OnLabel2PropertyChanged));
        public String Label2
        {
            get { return (String)GetValue(Label2Property); }
            set { SetValue(Label2Property, value); }
        }

        public TestUserControl()
        {
            DataContext = this;

            InitializeComponent();
        }

        private static void OnLabel1PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            //used for breakpoint
        }

        private static void OnLabel2PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            //used for breakpoint
        }
    }
}

使用用户控件的窗口

<Window x:Class="Windows.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:UC="clr-namespace:UserControls"
        >
    <StackPanel>
        <Label Content="Non user control label" />

        <UC:TestUserControl x:Name="uc" Label1="User control label 1" Label2="{Binding Label2FromWindow}" />
    </StackPanel>
</Window>

窗口的代码隐藏

using System;
using System.Windows;

namespace Windows
{
    public partial class TestWindow : Window
    {
        public String Label2FromWindow
        {
            get { return "User control label 2"; }
        }

        public TestWindow()
        {
            DataContext = this;

            InitializeComponent();
        }
    }
}

所以在这种情况下,为什么没有&#34;标签2&#34;在用户控件中,从&#34; Label2FromWindow&#34;中获取值。从窗口?我觉得它是一个计时问题,用户控件首先评估它的所有表达式,然后窗口稍后评估它的表达式,并且用户控件永远不会被通知&#34;窗口的评估值。

这个例子有望说明一个问题,但我真正的问题是:

关于DataContext,属性上的硬编码值,绑定表达式,模板和嵌套控件的操作顺序是什么?

修改

H.B.帮助我实现了这一目标。当窗口的DataContext设置为自身时,用户控件将继续&#34;继承&#34; DataContext。这使得Binding可以在用户控件的属性上工作,但是在用户控件中,绑定到其本地属性不会起作用。当直接在用户控件上设置DataContext时,窗口对用户控件属性的绑定不再有效,但用户控件可以绑定到其自己的本地属性。以下是有效的更新代码示例。

用户控制:

<UserControl x:Class="UserControls.TestUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Name="uc">
    <StackPanel>
        <Label Content="{Binding ElementName=uc, Path=Label1}" />
        <Label Content="{Binding ElementName=uc, Path=Label2}" />
    </StackPanel>
</UserControl>

用户控制代码隐藏:

using System;
using System.Windows;
using System.Windows.Controls;

namespace UserControls
{
    public partial class TestUserControl : UserControl
    {
        public static readonly DependencyProperty Label1Property = DependencyProperty.Register("Label1", typeof(String), typeof(TestUserControl));
        public String Label1
        {
            get { return (String)GetValue(Label1Property); }
            set { SetValue(Label1Property, value); }
        }

        public static readonly DependencyProperty Label2Property = DependencyProperty.Register("Label2", typeof(String), typeof(TestUserControl));
        public String Label2
        {
            get { return (String)GetValue(Label2Property); }
            set { SetValue(Label2Property, value); }
        }

        public TestUserControl()
        {
            InitializeComponent();
        }
    }
}

测试窗口:

<Window x:Class="Windows.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:UC="clr-namespace:UserControls"
        >
    <StackPanel>
        <Label Content="Non user control label" />

        <UC:TestUserControl Label1="User control label 1" Label2="{Binding Label2FromWindow}" />
    </StackPanel>
</Window>

测试窗口代码隐藏:

using System;
using System.Windows;

namespace Windows
{
    public partial class TestWindow : Window
    {
        public String Label2FromWindow
        {
            get { return "User control label 2"; }
        }

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

2 个答案:

答案 0 :(得分:3)

这不是关于我认为的顺序,而是优先级(也许我在这里分裂)。您明确设置了DataContext的{​​{1}} - 这意味着不会被继承,因此您的绑定会在UserControl中查找属性UserControl。显然,它找不到它。

永远不要设置Label2FromWindowDataContext个实例,你不应该遇到这样的问题。 (为UserControl命名并使用UserControl进行内部绑定)

获取完整优先级列表see MSDN

答案 1 :(得分:0)

另一种运行良好的方法是给UserControl的第一个子项x:Name =“LayoutRoot”。然后在UserControl构造函数中,使用LayoutRoot.DataContext = this。

此方法允许您从周围窗口设置UserControl上定义的DependencyProperties的值,并且仍然使用UserControl标记内的标准Bindings而不使用ElementName或RelativeSource绑定。

Colin Ebererhardt在此解释了这一点:http://www.scottlogic.com/blog/2012/02/06/a-simple-pattern-for-creating-re-useable-usercontrols-in-wpf-silverlight.html