当依赖属性具有 RelativeSource 绑定时,GetTemplateChild 返回 null

Tor*_*lin 1 c# wpf xaml dependency-properties relativesource

我创建了一个自定义控件,MyTextBoxTextBox. 它有一个与之关联的样式,其中包含一个命名控件:

<Style x:Key="{x:Type MyTextBox}" TargetType="{x:Type MyTextBox}">
    <!-- ... -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MyTextBox}">
                <!-- ... -->
                    <SomeControl x:Name="PART_SomeControl" />
                <!-- ... -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)

MyTextBox有一个依赖属性,当设置时,将其值传播到SomeControl

public class MyTextBox : TextBox
{
    // ...

    public static new readonly DependencyProperty MyParameterProperty =
        DependencyProperty.Register(
            "MyParameter",
            typeof(object),
            typeof(MyTextBox),
            new PropertyMetadata(default(object), MyParameterChanged));

    private static void MyParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var me = (MyTextBox)d;
        var someControl = (SomeControl)me.GetTemplateChild("PART_SomeControl");
        someControl.SetValue(SomeControl.MyParameterProperty, e.NewValue);
    }
}
Run Code Online (Sandbox Code Playgroud)

这在进行简单绑定时工作正常,如下所示:

<MyTextBox MyParameter="{Binding}" />
Run Code Online (Sandbox Code Playgroud)

但是当我使用 RelativeSource 使用更花哨的绑定时,如下所示:

<MyTextBox MyParameter="{Binding DataContext, RelativeSource={RelativeSource
    FindAncestor, AncestorType=ParentView}}"
Run Code Online (Sandbox Code Playgroud)

该方法me.GetTemplateChild()返回null。也就是说,SomeControl找不到。

为什么?

我所做的一个观察是,当它有 a 时RelativeSourceMyParameter依赖属性首先设置为所有依赖属性。也就是说,如果我做这样的事情:

<MyTextBox
    OtherParameter="{Binding}"
    MyParameter="{Binding DataContext, RelativeSource={RelativeSource
        FindAncestor, AncestorType=ParentView}}"
Run Code Online (Sandbox Code Playgroud)

MyParameter属性(奇怪地)设置在 之前OtherParameter。使用简单绑定,它们按照声明的顺序设置,正如预期的那样。

(如您所见,我的代码已从不相关的内容中删除。希望我已经包含了所有重要的内容。)

nmc*_*ean 5

它很可能是在应用模板之前设置的。有几种方法可以解决这个问题:

  • 之前调用ApplyTemplateGetTemplateChild以强制加载模板。

  • 使用BeginInvoke withDispatcherPriority.Loaded将操作延迟到以后。

  • MyParameterChanged如果没有模板,则允许失败,并重复OnApplyTemplate 中的逻辑(无论如何您都应该这样做,以防在加载后模板被替换(如在 Windows 主题更改中)。

看起来您只是将值传递给子元素。您是否考虑过使用带有值继承的附加属性?

至于为什么RelativeSource FindAncestor绑定失败,而不是原始 DataContext 绑定,我认为这归结为它DataContext本身是一个继承属性的事实。假设,假设操作顺序是这样的:

  1. 父级接收 DataContext 属性
  2. 父母添加孩子
  3. 孩子评估 MyParameter
  4. 孩子申请模板
  5. 子级从父级继承 DataContext

在第一种情况 ( MyParameter="{Binding}") 中,第 3 步无法更新,MyParameter因为它还没有要绑定到的 DataContext,因此MyParameterChanged没有被调用,也没有例外。在第 5 步之后,当孩子的 DataContext 更新时,它会重新评估MyParameter,到那时模板已经存在,因此属性更改处理程序可以工作。

在第二种情况下,你是专门找了父母,其中DataContext属性确实存在,所以MyParameterChanged 称为在第3步,因为尚未应用模板失败。