我创建了一个自定义的控制,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东西剥离。我希望,我已经包括一切是非常重要的。)
最有可能在应用模板之前,它被设置。还有,你可以解决这几个方面:
GetTemplateChild
之前迫使模板加载。DispatcherPriority.Loaded
延迟操作,直到后来。MyParameterChanged
失败,如果没有模板,并重复OnApplyTemplate逻辑(你本来也应该这样做,万一模板负荷后更换(如在Windows主题的变化)。它看起来像你只是传递价值的子元素。你有没有使用与值继承附加属性考虑?
至于为什么它不能为您RelativeSource FindAncestor
结合,而不是原始的DataContext绑定,我认为这归结为一个事实,即DataContext
本身是一种遗传特性。可以想像,假设操作顺序是这样的:
在第一种情况下,(MyParameter="{Binding}"
),第3步未能更新MyParameter
,因为它目前还没有一个DataContext绑定到,所以MyParameterChanged
不叫,也没有例外。步骤5中,当孩子的DataContext的更新后,它会重新评估MyParameter
,并通过该点的模板存在这样的属性更改处理工作。
在第二种情况下,你是专门找了父母,这确实存在的DataContext属性,所以MyParameterChanged
被称为在第3步,因为尚未应用模板失败。
覆盖OnApplyTemplate方法,并调用GetTempladeChild里面。我通过阅读这篇解决了这个问题:http://www.codeproject.com/Articles/179105/How-to-access-Control-Template-parts-from-Code-Beh
请注意,这样的:
<MyTextBox MyParameter="{Binding}" />
只有与此相同:
<MyTextBox MyParameter="{Binding DataContext, RelativeSource={RelativeSource
FindAncestor, AncestorType=ParentView}}" />
如果MyTextBox
控制是在一个视图中命名ParentView
有其DataContext
属性集。如果是这样的话,那么它真的应该不会造成像你任何问题描述。因此,我只能假设你正在试图通过SomeControl
访问MyParameterProperty
对象的UI已经被初始化之前。
您可以通过添加一个处理程序Loaded
或Initialized
事件进行测试。摆在那里一个破发点,并附加一个在你MyParameterChanged
处理程序,并看到他们得到中提出顺序。值得一提的是,Dependencyproperty
s可以从Style
s或内联XAML中设置的对象在UI初始化之前。
调用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()
应该设置任何依赖属性之前调用。