GetTemplateChild返回空当依赖属性已绑定的RelativeSource

问题描述 投票:0回答:4

我创建了一个自定义的控制,MyTextBox,从TextBox继承。它有一个与之关联的风格,包含namned控制:

<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>

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);
    }
}

做一个简单的绑定时,这样也能正常工作:

<MyTextBox MyParameter="{Binding}" />

但是,当我用更花哨使用的RelativeSource,这样的结合:

<MyTextBox MyParameter="{Binding DataContext, RelativeSource={RelativeSource
    FindAncestor, AncestorType=ParentView}}"

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

为什么?

一个观察我已经是当它有一个RelativeSource,该MyParameter依赖属性首先依赖属性的设置。也就是说,如果我做这样的事情:

<MyTextBox
    OtherParameter="{Binding}"
    MyParameter="{Binding DataContext, RelativeSource={RelativeSource
        FindAncestor, AncestorType=ParentView}}"

MyParameter属性是(奇怪)OtherParameter之前设置。使用简单的结合,他们都在同一顺序设置为声明,只是符合市场预期。

(正如你所看到的,我的代码已经从unrelevant东西剥离。我希望,我已经包括一切是非常重要的。)

c# wpf xaml dependency-properties relativesource
4个回答
4
投票

最有可能在应用模板之前,它被设置。还有,你可以解决这几个方面:

  • 呼叫ApplyTemplate GetTemplateChild之前迫使模板加载。
  • 使用BeginInvokeDispatcherPriority.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步,因为尚未应用模板失败。


1
投票

覆盖OnApplyTemplate方法,并调用GetTempladeChild里面。我通过阅读这篇解决了这个问题:http://www.codeproject.com/Articles/179105/How-to-access-Control-Template-parts-from-Code-Beh


0
投票

请注意,这样的:

<MyTextBox MyParameter="{Binding}" />

只有与此相同:

<MyTextBox MyParameter="{Binding DataContext, RelativeSource={RelativeSource
FindAncestor, AncestorType=ParentView}}" />

如果MyTextBox控制是在一个视图中命名ParentView有其DataContext属性集。如果是这样的话,那么它真的应该不会造成像你任何问题描述。因此,我只能假设你正在试图通过SomeControl访问MyParameterProperty对象的UI已经被初始化之前。

您可以通过添加一个处理程序LoadedInitialized事件进行测试。摆在那里一个破发点,并附加一个在你MyParameterChanged处理程序,并看到他们得到中提出顺序。值得一提的是,Dependencypropertys可以从Styles或内联XAML中设置的对象在UI初始化之前。


0
投票

调用ApplyTemplate(); InitializeComponent();之后(通常是在构造函数)对我的作品。重写OnApplyTemplate()方法和这里所说的GetTemplateChild

例如:

        private TextBox PART_TextBox;
        private RepeatButton PART_UpButton;
        private RepeatButton PART_DownButton;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            PART_TextBox = GetTemplateChild("PART_TextBox") as TextBox;
            PART_UpButton = GetTemplateChild("PART_UpButton") as RepeatButton;
            PART_DownButton = GetTemplateChild("PART_DownButton") as RepeatButton;
        }

这样,OnApplyTemplate()应该设置任何依赖属性之前调用。

© www.soinside.com 2019 - 2024. All rights reserved.