过去一直有个Silverlight的技术疑难没有得到解决,那就是如果在Code Behind中获取模板中定义的各个对象的引用。参考一下Xaml代码:
代码
<
ListBox
x:Name
="lbx_listBox"
<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
x:Name
="lbx_listBox"
<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
WrapPanel m_listWrapper;
private
void
wrp_listWrapper_Loaded(
object
sender, RoutedEventArgs e)
{
m_listWrapper
=
sender
as
WrapPanel;
}
就是这样简单的方式,就可以把这个定义在模板中的WrapPanel对象的引用保存下来了。对于其他类似的,定义在模板中的控件对象也能使用这种方式。但要注意,假如面对的是
DataTemplate这一类模板,一般会被多个数据项使用,例如一个ListBox中的DataTemplate会被该ListBox的所有列表项使用。这种情况下,模板中定义的控件的Loaded事件会发生多次,每次是由不一样的列表项触发。但事件处理程序就只有一个,因此必须要在该程序代码中想办法区分各个实例。
例如:
代码
<
ListBox
x:Name
="lbx_listBox"
<ListBox.ItemTemplate
>
<
DataTemplate
>
<
Image
src
={Binding}
Loaded
="Image_Loaded"
/>
</
DataTemplate
>
</
ListBox.ItemTemplate
>
</
ListBox
>
该ListBox中的每个列表项都是一张图片。假如我们用同样的方法在Code Behide中获取该图片的引用:
private
IList
<
Image
>
m_imgs
=
new
List
<
Image
>
();
private
void
Image_Loaded(
object
sender, RoutedEventArgs e)
{
m_imgs.Add(sender
as
Image);
}
这里要使用一个Image的List集合来保存模板中的Image,因为多个列表项会运行多次该处理程序。假如要选择指定的某一个Image,则必须添加筛选代码。