WPF元素树
如果您在项目中开启一个新窗口并在设计器中将按钮拖入窗口内,您会得到 XAML 格式的元素树,如下所示(为了清楚略去了属性):
<Window>
<Grid>
<Button/>
</Grid>
</Window>
其 中的每个元素都代表对应 .NET 类型的一个运行时实例,元素的声明分层结构形成了所谓的逻辑树。此外,WPF 中的许多控件不是 ContentControl 就是 ItemsControl,这代表他们可以有子元素。例如,Button 是一个 ContentControl,它可以将复杂的子元素做为其内容。您可以展开逻辑树,如下所示:
<Window>
<Grid>
<Button>
<StackPanel>
<Image/>
<TextBlock/>
</StackPanel>
</Button>
</Grid>
</Window>
生成的 UI 如图 1 所示。
图1包含按钮内容的简单窗口
如 您所想,树可以有多个分支(Grid 中的另一 Button),因此逻辑树会变得极为复杂。对于逻辑树的 WPF 元素,您需要意识到您所见到的并不是您在运行时真正得到的内容。每个这样的元素通常都会在运行时扩展为更为复杂的可视元素树。在本例中,元素的逻辑树扩展 为可视元素树,如图 2 所示。
图2简单窗口可视树
我使用名为 Snoop 的工具 (blois.us/Snoop) 查看图 2 中所示可视树的元素。您可以看到窗口 (EventsWindow) 实际是将其内容置入 Border 和 AdornerDecorator 之内,用 ContentPresenter 显示其中的内容。按钮也与此类似,将其内容置入 ButtonChrome 对象,然后用 ContentPresenter 显示内容。
单 击按钮时,我可能实际根本没有单击 Button 元素,可能是单击可视树中的某一子元素,甚至是逻辑树中未显示的元素(如 ButtonChrome)。例如,假设我在按钮内的图像上方单击鼠标。这一单击操作在一开始实际是将其表达为 Image 元素中的 MouseLeftButtonDown 事件。但却需要转化为 Button 层级的 Click 事件。这就要引入路由事件中的路由。