我已经为用户创建了一个AddressInput控件来输入地址。控件将根据使用位置的不同而具有不同的外观,因此我提供了一个名为AddressTemplate的DataTemplate属性。
默认样式如下:
<Style TargetType="{x:Type addressUI:AddressInput}">
<Setter Property="AddressTemplate"
Value="{StaticResource DefaultAddressTemplate}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type addressUI:AddressInput}">
<GroupBox Header="Address">
<ContentPresenter ContentTemplate="{Binding Path=AddressTemplate, RelativeSource={RelativeSource TemplatedParent}}"
Content="{Binding Path=Address, RelativeSource={RelativeSource TemplatedParent}}"
x:Name="PART_AddressPresenter" />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我的所有地址数据模板将包含一个用于选择国家的组合框(名为"part_countryeslist")。我需要一些在选择更改时触发的代码隐藏操作,这意味着我需要挂钩SelectionChanged事件。在我的AddressInput中,我需要在AddressTemplate中找到part_countryeslist。
我可以得到"PART_AddressPresenter" ContentPresenter像这样:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var addressPresenter = Template.FindName("PART_AddressPresenter", this) as ContentPresenter;
}
现在我如何得到"part_countryeslist"包含在AddressTemplate?
我试过了:
var countryeslist = AddressTemplate。FindName("PART_CountriesList",addressPresenter);
抛出异常,因为addressPresenter还没有应用它的模板。我知道ContentPresenter有OnApplyTemplate覆盖,但它似乎愚蠢的扩展它为这个用途。
我想如果我要扩展ContentPresenter,我会做一个新的可重用版本,每当OnApplyTemplate方法执行时触发一个事件。这可能会解决我的问题,但这似乎太疯狂了。有没有更好的办法?
我很好奇是否有人有"正确"的方法来做到这一点,但对于FindName,我总是最终诉诸于这样的东西:
Dispatcher.BeginInvoke( new Action(() =>
{
// Call FindName here
}), System.Windows.Threading.DispatcherPriority.Render );
这可能会导致闪烁,因为你要等到数据模板完成并呈现之后再去执行你的代码,所以如果你想做的事情影响控件的外观,这并不总是一个好的选择。