WinRT/Metro:解析元素在Visual Tree和Logical Tree中的位置
严格上讲,Visual Tree和Logical Tree是WPF的概念,不过既然都有XAML,那么它们在WinRT中也是存在的。
首先WinRT中没有WPF中的Visual,UIElement是直接继承自DependencyObject的,然后下面是FrameworkElement。
接下来就是使用Windows.Ui.Xaml命名空间下的VisualTreeHelper类型,使用起来和WPF类似,比如根据当前元素查找上面的Visual Tree,代码:
IEnumerable<DependencyObject> GetVisualTree(DependencyObject obj)
{
var list = new List<DependencyObject>() { obj };
var res = obj;
while ((res = VisualTreeHelper.GetParent(res)) != null)
list.Add(res);
return list.AsEnumerable().Reverse();
}
比如我们在界面上放置一个ListBox(名称是lbx),使用上述自己输出自己的Visual Tree:
lbx.ItemsSource = GetVisualTree(lbx).Select(d => d.GetType().Name);
结果:
好了,接着看Logical Tree,不过WinRT目前还没有LogicalTreeHelper类型,不过我们可以通过FrameworkElement.Parent属性来获取对象的Logical Parent。
事实上WPF中的LogicalTreeHelper就是这样工作的,只不过同时支持FrameworkElement和FrameworkContentElement,如下WPF中LogicalTreeHelper.GetParent方法源码:
//WPF LogicalTreeHelper.GetParent
public static DependencyObject GetParent(DependencyObject current)
{
if (current == null)
{
throw new ArgumentNullException("current");
}
FrameworkElement element = current as FrameworkElement;
if (element != null)
{
return element.Parent;
}
FrameworkContentElement element2 = current as FrameworkContentElement;
if (element2 != null)
{
return element2.Parent;
}
return null;
}
因此获得Logical Tree,可以写如下方法:
IEnumerable<DependencyObject> GetLogicalTree(DependencyObject obj)
{
var list = new List<DependencyObject>() { obj };
var res = obj;
while ((res = GetParent(res)) != null)
list.Add(res);
return list.AsEnumerable().Reverse();
}
DependencyObject GetParent(DependencyObject obj)
{
var ff = obj as FrameworkElement;
if (ff != null)
return ff.Parent;
return null;
}
一个完整的代码:
主界面XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock>Logical Tree</TextBlock>
<TextBlock Grid.Column="1">Visual Tree</TextBlock>
<ListBox Name="lbxLogical" Grid.Row="1"/>
<ListBox Name="lbxVisual" Grid.Row="1" Grid.Column="1"/>
</Grid>
然后将ListBox的Logical Tree和Visual Tree显示在界面上,在Page的Loaded事件上调用上面的方法:
private void Page_Loaded_1(object sender, RoutedEventArgs e)
{
lbxLogical.ItemsSource = GetLogicalTree(lbxLogical).Select(d => d.GetType().Name);
lbxVisual.ItemsSource = GetVisualTree(lbxVisual).Select(d => d.GetType().Name);
}
结果: