依赖属性数据上下文

Mic*_*egg 2 c# wpf dependency-properties

我有一个用户控件,并为它设置了一个 Datacontext。这个用户控件还包含一个依赖属性。现在,我只想绑定到这个属性。

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

我的用户控件(称为 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));
Run Code Online (Sandbox Code Playgroud)

我尝试像这样使用它:

<upDownControlDevelopement:TimePicker Grid.Row="1" Time="{Binding Path=TimeValue}" />
Run Code Online (Sandbox Code Playgroud)

当我这样做时,我收到以下绑定错误:

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')
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激

问候迈克尔

PS:你可以在这里下载代码

ves*_*san 5

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

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

<Grid DataContext="{Binding Data}">
    <TextBox Text="{Binding TextValue1}" />
    <!-- Some more controls -->
</Grid>
Run Code Online (Sandbox Code Playgroud)

请注意,在这里,您正在使用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>
Run Code Online (Sandbox Code Playgroud)

请注意,这TemplateBinding始终是单向的,因此如果您需要进行任何编辑,则需要使用普通绑定(如您TextBox在示例中看到的)。

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

然后,当您使用控件时,您可以这样做(添加到我的第一个示例中):

<Grid DataContext="{Binding Data}">
    <TextBox Text="{Binding TextValue1}" />
    <!-- Some more controls -->
    <MyTimePicker Time="{Binding TimeValue}" />
</Grid>
Run Code Online (Sandbox Code Playgroud)

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

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

public MyTimePicker()
{
    InitializeComponent();
    DataContext = this;
}
Run Code Online (Sandbox Code Playgroud)

这将覆盖DataContextXAML 中的任何设置,这将使绑定变得非常痛苦(因为您必须始终Source手动设置)。前面的示例不起作用,这也不起作用:

<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />
Run Code Online (Sandbox Code Playgroud)

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

只是DataContext在开发控件时不要触摸,你会没事的。