WPF 元素的查找
?
javascript查找元素
在CSS選擇器出現之前我想下面兩行代碼是web開發人員最為熟悉的
document.getElementById(""); document.getElementsByName("");html整個頁面的Id和Name是必須唯一的,并沒有局部Id和Name的概念,其實只要做到規范,就很少會出現Name重復等問題。
ASP.NET的Id
ASP.NET控件是有命名范圍的概念的,如一個UserControl,其Id會一直拉長,以導致寫js的時候異常麻煩.
下面介紹WPF的NameScope
預設置元素名字
WPF有兩種方式設置元素的Name
<StackPanel x:Name="panel"><Label Name="name1" Content="Name1Label"/><Label x:Name="name2" Content="Name2Label"/></StackPanel>這里我們的重點不在于討論Name和x:Name的區別,
Name是真正元素上的屬性,x:Name而則xaml(語法解析)的魔力,我們所看到的只能是表象.
查找已設置Name的元素
WPF的基類元素FrameworkElement提供了FindName方法以提供查找設定的元素
以Code的形式添加元素
var label = new Label(); label.Name = "label3"; panel.Children.Add(label);注意已經設置了Name
添加好以后,然后查找這個元素,會發現并不能查找到這個元素
上面就說過xaml是有魔力的,并非設置一個Name屬性就可以了的,其內部還有了一個叫RegisterName的方法,以注冊元素的Name(想象一下,如果有1000個樹節點,從根節點開始找最內部的節點那得多慢),有了Name就跟元素綁到一起了.
現修改如下,用RegisterName方法注冊(注意:即使注釋了Name 屬性也不會有影響)
現在查找恢復成功
但會發現查找出來的元素Name還是空的.這就會造成歧義,建議應該Name屬性和RegisterName方法一起設置,
不僅僅是注冊元素的名字
除了Element之外,其他類型也是可以的,如
<Label><Label.BorderBrush><SolidColorBrush x:Name="brushName"></SolidColorBrush></Label.BorderBrush></Label>有些基本類型在xaml中無法設置,如String,Int類型等.但可以通過代碼設置
this.RegisterName("str", "Hello");這里只有功能示例而已,但實際中千萬別這么做,本身不為此設計.查找UserControl的元素
先定義一個UserControl
<UserControl x:Class="NameScopeDemo.MyUserControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"><Grid><Button HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="btn">UserControl Button</Button></Grid> </UserControl>在主窗體中使用UserControl
<StackPanel x:Name="panel"> <local:MyUserControl x:Name="myControl"></local:MyUserControl></StackPanel>現在查找結果如下
若使用主窗體去無法查找到btn的話,但通過UserControl就可以.
在設計上將UI切分了,但卻給查找元素造成了麻煩了。
命名范圍(NameScope)
若以Code方式,添加方式則如下
uc = new MyUserControl(); var ns = new NameScope(); this.RegisterName("myControl", uc); panel.Children.Add(uc);為UserControl創建了一個獨立的命名范圍,想要查找MyUserControl的元素可以通過MyUserControl的級別的FindName來查找
模板取元素
<ContentControl x:Name="cc"><ContentControl.ContentTemplate><DataTemplate><Button HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="btn">UserControl Button</Button></DataTemplate></ContentControl.ContentTemplate></ContentControl>?
看到上面代碼,cc無法通過FindName查找到btn,只有模板的根元素才可以,這個根元素一般是ContentPresenter,所以當在模板內查找元素時,必須告訴其根元素
public Object FindName(string name,FrameworkElement templatedParent )那么首先我們就必須找到ContentPresenter,而只能通過視覺樹上面找,整體而言還是比較麻煩的,不知道為何內部API不封裝一下.?將模板內的元素名字注冊到父級
?
var ns = uc.GetValue(NameScope.NameScopeProperty) as IDictionary<string, object>; var localNS = this.GetValue(NameScope.NameScopeProperty) as IDictionary<string, object>; foreach (var n in ns) {if (localNS.ContainsKey(n.Key)){this.RegisterName(n.Key, n.Value);} }首先獲取當前元素的NameScope,然后再將其注冊到父級命名范圍內以方便查找,值得注意的是模板內部的有些Name是固定的,如ScrollBar,其內部模板元素Name命名是有規定的,所以不要將其注冊在內.
下篇將繼續討論NameScope,綁定,視覺樹,邏輯樹之間的關系
總結
- 上一篇: Android使用ActivityGro
- 下一篇: 终于看到了希望——基于美女的VGA系统构