依赖性 属性 数据上下文

Dependency Property Datacontext

我有一个用户控件,并为其设置了一个数据上下文。此用户控件还包含一个 Dependency-属性。现在,我只想绑定到这个 属性。

我认为问题与错误的数据上下文有关。

我的用户控件(称为 TimePicker)中的依赖项-属性 如下所示:

public TimeSpan Time
    {
        get { return (TimeSpan)GetValue(TimeProperty); }
        set
        {
            SetValue(TimeProperty, value);
            OnPropertyChanged();
        }
    }

    public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof (TimeSpan), typeof (TimePicker));

我尝试这样使用它:

<upDownControlDevelopement:TimePicker Grid.Row="1" Time="{Binding Path=TimeValue}" />

当我这样做时,出现以下绑定错误:

System.Windows.Data Error: 40 : BindingExpression path error: 'TimeValue' property not found on 'object' ''TimePicker' (Name='TimePickerControl')'. BindingExpression:Path=TimeValue; DataItem='TimePicker' (Name='TimePickerControl'); target element is 'TimePicker' (Name='TimePickerControl'); target property is 'Time' (type 'TimeSpan')

任何帮助将不胜感激

问候迈克尔

PS:您可以在 here

下载代码

您的错误指出 'TimeValue' 属性 not found on 'object' 'TimePicker',这意味着 WPF 框架是查看 'TimePicker' 对象以解析 'TimeValue' 属性 值。您必须以某种方式将包含 'TimePicker' 对象的 WindowUserControlDataContext 设置为 'TimePicker' 对象的实例。

相反,它应该设置为声明 'TimeValue' 属性 的 class 的一个实例。如果您使用的是视图模型,则应将其设置为该视图模型的实例:

DataContext = new YourViewModel();

如果 'TimeValue' 属性 在 WindowUserControl 中声明,那么您可以将 DataContext 设置为它自己(尽管通常不推荐) :

DataContext = this;

请注意,当数据绑定到 'Time' 属性 从您的 TimePicker 控件 内部时,您应该使用 RelativeSource Binding:

<TextBlock Text="{Binding Time, RelativeSource={RelativeSource 
    AncestorType={x:Type YourLocalPrefix:TimePicker}}}" ... />

您不需要覆盖用户控件的数据上下文。您可以使用 RelativeSource 将您的绑定源 属性 即 TimeValue 指向您喜欢的任何其他来源。 例如。如果您的 windows class 中有来源 属性。您可以简单地将绑定目标指向 window 数据上下文中的源,如下所示:

 {Binding Path=DataContext.TimeValue, RelativeSource={ RelativeSource AncestorType={x:Type Window}}}

"Apologies for formatting . Typed from iPhone"

通常我们不设置数据上下文directly.If你想设置数据上下文创建一个你的用户控件的实例并为每个单独设置数据上下文。

虽然这个问题现在已经解决了,但在我看来,DataContext 的使用似乎有些不当。

开发自定义可重用控件时,根本不应该设置 DataContextDataContext 将是什么,这是由控件的 用户 决定的,而不是由开发人员决定的。考虑以下常见的代码模式:

<Grid DataContext="{Binding Data}">
    <TextBox Text="{Binding TextValue1}" />
    <!-- Some more controls -->
</Grid>

请注意,您在这里使用的是 Grid 控件。控件的开发人员(在本例中为 WPF 团队)根本没有触及 DataContext - 这取决于您。作为控件开发人员,这对您意味着什么?您的 DependencyProperty 定义没问题,但您不应该触及 DataContext。然后,您将如何将控件内的内容绑定到 DependencyProperty 值?一个好方法是使用模板(省略命名空间):

<MyTimePicker>
    <MyTimePicker.Template>
        <ControlTemplate TargetType="MyTimePicker">
            <!-- Stuff in your control -->
            <TextBlock Text="{TemplateBinding Time}" />
            <TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
        </ControlTemplate>
    <MyTimePicker.Template>
</MyTimePicker>

请注意 TemplateBinding 始终只是单向的,因此如果您需要任何编辑,则需要使用普通绑定(如示例中的 TextBox 所示) .

这仅意味着您控件中的 TextBlock/Box 将从您的自定义控件本身获取其 Time 值,忽略您可能已设置的任何 DataContext

然后,当你使用控件时,你这样做(添加到我的第一个例子):

<Grid DataContext="{Binding Data}">
    <TextBox Text="{Binding TextValue1}" />
    <!-- Some more controls -->
    <MyTimePicker Time="{Binding TimeValue}" />
</Grid>

这里刚刚发生的是 MyTimePicker 根本没有在任何地方设置 DataContext - 它是从父控件(Grid)获取的。所以值是这样的:Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text

最重要的是,避免在自定义控件的构造函数中执行此操作:

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

这将覆盖 XAML 中设置的任何 DataContext,这将使绑定变得非常痛苦(因为您必须始终手动设置 Source)。前面的例子不行,这个也不行:

<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />

你会认为这没问题,但 DataContext 将在 InitializeComponent() 调用中解析,因此该值将立即被覆盖。因此绑定到 TimeValue 将在控件中查找它(当然会失败)。

只是在开发控件时不要碰 DataContext 就可以了。