我有一个自定义控件,它定义了模板,模板包含以下代码:
<FlipView Grid.Row="3"
Grid.ColumnSpan="2" x:Name="FlipView1" BorderBrush="Black"
ItemsSource="{Binding ItemsCollection, RelativeSource={RelativeSource TemplatedParent}}">
<FlipView.ItemTemplate>
<DataTemplate>
<ScrollViewer>
<Grid>
<local:UserControlA x:Name="PART_UserControlA"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<local:UserControlB Grid.Column="1"
View="{Binding View}"
x:Name="PART_UserControlB"
ItemsSource="{Binding ItemsSourcePropertyOfAnItemInItemsCollection}"
ItemTemplate="{Binding TemplatePropertyOfAnItemInItemsCollection}" />
</Grid>
</Grid>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
在我的自定义控件的代码隐藏中,我有这样的代码来加载模板中的控件(我不得不这样做,因为GetTemplateChild返回null,因为PART_UserControlB再次是FlipView模板的一部分,并且GetTemplateChild不会递归地获取模板化的子控件):
protected override void OnApplyTemplate()
{
FlipView flipView = GetTemplateChild("FlipView1") as FlipView;
DataTemplate dt = flipView.ItemTemplate;
DependencyObject dio1 = dt.LoadContent();
DependencyObject dio = (dio1 as ScrollViewer).Content as DependencyObject;
foreach (var item in FindVisualChildren<UserControlB>(dio))
{
if (item.Name == "PART_UserControlB")
{
UserControlB controlB = item;
controlB.ApplyTemplate();
controlB.PointerPressed += OnPointerPressed;
}
}
}
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
问题是,当我点击UserControlB中的一个项目时,它不会触发该控件的OnPointerPressed事件。这就像我在后面的代码中没有得到相同的UserControlB实例。
当您检索Template Child(像您的零件一样)时,您应该使用FrameworkElement.GetTemplateChild 来检索它
在您的情况下:
UserControlB controlB = GetTemplateChild("PART_UserControlB") as UserControlB;
因此,要回答标题中的问题:不,这不是正确的做法。
此外,我认为您不应该在这里调用ApplyTemplate()。
我在这里看到的另一件事是在你的模板中没有ElementName或RelativeSource的绑定:这真是一件糟糕的事情。您不能保证您的自定义控件DataContext在运行时是什么。这将导致意想不到的行为。
Template中的所有绑定都应该以Template父级或Template内的可视控件作为目标,但不应该使用DataContext。
编辑
好的,所以我再次阅读了您的代码,您的PART_UserControlB在DataTemplate中,在ItemsControl的ItemTemplate中,这意味着对于ItemsControl中的每个项目,您将有一个名为PART_UserControl B的UserControlB。您注意到的行为是正常的:您找到了第一个名为PART_UserControlB的控件,并在其中一个事件上放置了事件处理程序。但是其他的UserControlB呢?
你在这里并没有真正使用Template子项,你指的是根据ItemsControl内容可能存在或不存在的东西。这些不是自定义控件的一部分,因此不应命名为part_xxx。您可以使用UserControlB中的命令DP,该命令DP将在引发事件时执行:
//in your UserControlB.cs
public event EventHandler<YourEventArgs> PointerPressed;
private void OnPointerPressed() {
YourEventArgs arg = new YourEventArgs();
if (PointerPressed != null) {
PointerPressed(this, arg);
}
if (PointerPressedCommand != null && PointerPressedCommand.CanExecute(PointerPressedCommandParameter)) {
PointerPressedCommand.Execute(PointerPressedCommandParameter);
}
}
#region PointerPressedCommand
public ICommand PointerPressedCommand
{
get { return (ICommand)GetValue(PointerPressedCommandProperty); }
set { SetValue(PointerPressedCommandProperty, value); }
}
private readonly static FrameworkPropertyMetadata PointerPressedCommandMetadata = new FrameworkPropertyMetadata {
DefaultValue = null,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
public static readonly DependencyProperty PointerPressedCommandProperty =
DependencyProperty.Register("PointerPressedCommand", typeof(ICommand), typeof(UserControlB), PointerPressedCommandMetadata);
#endregion
然后将命令绑定到TemplatedPrent中的命令。
//in your Template
<local:UserControlB Grid.Column="1"
View="{Binding View}"
x:Name="PART_UserControlB"
ItemsSource="{Binding ItemsSourcePropertyOfAnItemInItemsCollection}"
ItemTemplate="{Binding TemplatePropertyOfAnItemInItemsCollection}"
PointerPressedCommand="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MyCommand}"/>
使用事件处理程序可能是一种选择,但这将是一场噩梦:您必须监视ItemsControl的ItemsSource以了解更改,通过可视化树并添加处理程序。这将是一种快速而有点肮脏的方式来实现你想要实现的目标。