[Silverlight]如何获取模板中定义各个对象
过去一直有个Silverlight的技术疑难没有得到解决,那就是如果在Code Behind中获取模板中定义的各个对象的引用。参考一下Xaml代码:
<ListBox.ItemPanel>
<ItemsPanelTemplate x:Key="fileListItemsPanelTemplate">
<controlsToolkit:WrapPanel x:Name="wrp_listWrapper"
Width="450"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemPanel>
</ListBox>
这段代码定义了一个列表,列表中我选用WrapPanel作为列表项的容器,很明显是为了实现水平列表项自动换行的功能。但现在我是硬编码了450这个宽度给WrapPanel,因此当列表控件变大或缩小时,WrapPanel不会自动跟随变化。由此,我必须实现一个自动更新的机制。可以使用数据绑定,或者使用ListBox的SizeChanged事件。但无论使用哪种方式,我都必须获取这个WrapPanel对象的引用。
我故意给这个WrapPanel一个Name属性(值为wrp_listWrapper),这样并不会引起编译错误或者运行时异常。但尽管拥有一个Name属性,我仍然无法从Code Behind中获取该WrapPanel的引用,因为它是定义在模板中的对象。
实现获取引用的方式就是Loaded事件。参考以下改进代码:
<ListBox.ItemPanel>
<ItemsPanelTemplate x:Key="fileListItemsPanelTemplate">
<controlsToolkit:WrapPanel x:Name="wrp_listWrapper"
Width="450"
Orientation="Horizontal"
Loaded="wrp_listWrapper_Loaded"/>
</ItemsPanelTemplate>
</ListBox.ItemPanel>
</ListBox>
高亮的代码就是对WrapPanel的Loaded事件注册处理程序。该事件只会在列表加载完成时触发一次,仅一次。于是在Code Behind中,采用以下代码保存该WrapPanel的引用:
private void wrp_listWrapper_Loaded(object sender, RoutedEventArgs e)
{
m_listWrapper = sender as WrapPanel;
}
就是这样简单的方式,就可以把这个定义在模板中的WrapPanel对象的引用保存下来了。对于其他类似的,定义在模板中的控件对象也能使用这种方式。但要注意,假如面对的是
DataTemplate这一类模板,一般会被多个数据项使用,例如一个ListBox中的DataTemplate会被该ListBox的所有列表项使用。这种情况下,模板中定义的控件的Loaded事件会发生多次,每次是由不一样的列表项触发。但事件处理程序就只有一个,因此必须要在该程序代码中想办法区分各个实例。
例如:
<ListBox.ItemTemplate>
<DataTemplate>
<Image src={Binding} Loaded="Image_Loaded"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
该ListBox中的每个列表项都是一张图片。假如我们用同样的方法在Code Behide中获取该图片的引用:
private void Image_Loaded(object sender, RoutedEventArgs e)
{
m_imgs.Add(sender as Image);
}
这里要使用一个Image的List集合来保存模板中的Image,因为多个列表项会运行多次该处理程序。假如要选择指定的某一个Image,则必须添加筛选代码。