Name与x:Name的关系



如果想用Google搜包含冒号内容如何办?比如我想搜x:Name这个串……

原来应该是这样——x::Name

这世道连搜索也要加转义全民要不要人活了?

正文:

从第天学习XAML语言开始我就直没分清为什么对于个XAML标签既可以设置它Name又可以设置它x:Name问过些同事大家好像对这种比较孔乙己问题不太感兴趣今天花了些时间看了看收获还挺多和大家分享

首先让我们剖析下XAML代码和C#代码的间关系

大家都知道XAML是“用来设计UI”设计师用XAML设计出来UI其后台代码(逻辑)可以由员用C#或者VB去写——这叫做Code-behind实际上设计师用XAML和用C#都是在构建同个类换句话说就是:把个类劈成两半和UI相关那半由设计师用XAML写和逻辑相关那半由员用C#写

.NET的所以支持这种劈开写功能得益于partial这个关键字请大家看这两段代码

// For UI
public partial Car
{
Color bodyColor;
Color windowColor;
Polygon door;
Polygon seat;
}

// For logic
public partial Car
{
public void Accelerate { /*80, 90... 120, 140....1200...flying...*/}
public void Break {/*zizizizizizizizizi....*/ }
}
public Car
{
// UI
Color bodyColor;
Color windowColor;
Polygon door;
Polygon seat;

// logic
public void Accelerate { /*80, 90... 120, 140....1200...flying...*/}
public void Break {/*zizizizizizizizizi....*/ }
}
实际效果是完全只是前者是把UI和逻辑劈开写后者是混在起写罢了

劈开确是劈开了但让设计师用C#代码去实现UI恐怕不现实——让Blend直接生成C#不是不可能是事情只是C#描述UI太不直观了于是微软更进把界面描述语言又向设计师方向推进了也就是XAML语言于是开发和设计格局就变成了这样:

有了XAML和将XAML解析为C#/VB解析器设计师们就能以自己最高工作效率和员们合作开发软件Software了目前有关XAML是如何解析成C#/VB资料非常少

Name揭秘

下面让我们把目光集中在XAML->C#解析上来看看Name和x:Name本质是什么

让我们看段代码:

<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="100" Width="300" Background="SteelBlue">
<StackPanel>
<TextBox Name="textBox1"/>
<TextBox Name="textBox2"/>
<Button Content="Show Name" Click="Button_Click"/>
</StackPanel>

<x:Code>
<![CDATA[

private void Button_Click(object sender, RoutedEventArgs e)
{
Button btn = e.OriginalSource as Button;
textBox1.Text = btn.Name;

textBox2.Name = "Made_in_China";
textBox2.Text = textBox2.Name;
}

]]>
</x:Code>
</Window>

运行结果是:

我用XAML定义了 3个UI元素其中两个TextBox是有Name凡是你在XAML代码里设置了它Name那么在C#代码里就会有个对应变量这可也很好解释看看IL集就知道了——

不难看出XAML解析器会为XAML代码中设置了Name元素声明同名引用变量而且设置Name元素则不会有引用变量生成(不过这个元素对应对象是存在并且是VisualTree/LogicalTree上结点)

通过上面代码我看可以看出Name作用有两个:

1. 告诉XAML解析器为设置了Name元素声明对应引用变量(本例中是textBox1和textBox2)变量名使用Name

2. 将XAML元素对应对象(本例中是两个TextBox例子)Name属性设置为Name

注意引用变量旦声明的后名字就不能改了但对象Name属性仍然可以改(举例中我就把由textBox2变量引用着例子Name属性改成Made_in_China了)

让我们再挖深点儿——TextBoxName属性是从哪儿继承来呢?查查MSDN原来是从FrameworkElement那儿继承来这个Name属性是非常重要——如果你想在棵“树”上查找叫某个名字元素“树根”FindName思路方法就可以做到了特别需要注意是——FindName所使用参数是对象Name属性值而不是引用着这个对象变量名字如果你里只在XAML里设置了次Name那么引用变量名字和对象Name属性值恰好但如果你改变了对象Name属性那可就要小心了!请看下面代码:

<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="100" Width="300" Background="SteelBlue">
<StackPanel>
<TextBox Name="textBox1"/>
<TextBox Name="textBox2"/>
<Button Content="Show Name" Click="Button_Click"/>
</StackPanel>

<x:Code>
<![CDATA[

private void Button_Click(object sender, RoutedEventArgs e)


{
textBox2.Name = "Made_in_China";
//this.RegisterName("Made_in_China", this.textBox2);
TextBox t = this.FindName("Made_in_China") as TextBox;
(tnull)
{
;
}

{
MessageBox.Show("OK");
}
}

]]>
</x:Code>
</Window>
注意除非我取消对第17行注释不然尽管我已经把textBox2.Name改成了Made_in_China但由于这个新名字还没有被注册(即没有使用RegisterName思路方法将Made_in_China和textBox2所引用对象关联起来)我们仍然不能通过FindName找到它

我知道这段话挺拗口不过有点你想通过某种思路方法查找由DataTemplate自动生成UI元素时或许应该跑来读读这段绕口令:P

最后再啰嗦句:为什么这个Name属性可以起到在运行时被当作查找标识呢?是FrameworkElement被个名为RuntimeNamePropertyAttributeattribute所修饰这个attribute明确指定FrameworkElementName属性具备了作为查找标识资格TextBox等类派生自FrameworkElement自然也有这个功能下面是FrameworkElement类声明

[RuntimeNamePropertyAttribute("Name")]
[StyleTypedPropertyAttribute(Property = "FocusVisualStyle", StyleTargetType = typeof(Control))]
[XmlLangPropertyAttribute("Language")]
public FrameworkElement : UIElement,
IFrameworkInputElement, IInputElement, ISupportInitialize
{
//...
}
x:Name揭秘

x:Namex加个冒号介绍说明它来自x这个名称空间这个名称空间是定义在XAML根元素上也就是这句:

xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
这个x就是XAML字头了这个名称空间本意就是告诉我们——这个名称空间里所装元素都和XAML解析有关比如我在代码里还使用了x:Code把本来应该呆在C#代码里内容请到XAML里来了

可见x:Name和Name根本不是个层面上东西——Name是直接和元素和面向对象编程相关东西;x:Name是XAML语言解析层面上东西

如果我们把上面代码中所有Name都改成x:Name所有效果都是

不知道XAML中标有x:内容是不是会被“预处理”

Name和x:Name关系揭秘

不过如果你逻辑感比较强你会发现这样个问题——为个XAML元素声明对应引用变量这不是面向对象编程层面东西而是XAML解析层东西而且如果Name在语义学上“恪守本分”它应该只去设置下对象Name属性值而不去管是不是声明变量事儿

大胆设想你会猜到当XAML解析器发现个元素Name被设置了就会去x:Name那套机制也就是说引用变量是在x:Name机制被时候声明同样如果你设置是元素x:NameXAML解析器会在声明变量的后再去给例子Name属性设置值

这样猜想能够得到证实吗?让我们在MSDN里搜刮

在x:Name注释里我们能找到这段话:

Under the standard build configuration for a WPF application project that uses XAML, partial es, and code-behind, the specied x:Name becomes the name of a field that is created in the underlying code when XAML is processed, and that field holds a reference to the object.

而在FrameworkElement.Name属性文档里又能找到这句话:

This property essentially provides a WPF framework-level convenience property to the XAML x:Name Attribute.

也就是说Name确会去调x:Name那套机制为什么这么做?可能是为了写起来方便不过我真不太喜欢这种搅和在风格我宁可使用Name去给对象Name属性赋值而使用x:Name去声明变量

貌似“Under the standard build configuration ”这句话有点玄机不知道非standard编译配置会有什么样效果怎样才能自定义编译配置呢?

不喜欢这种风格原因还在于:Name和x:Name互相会在某些逻辑下出问题特别是“先有鸡还是先有蛋”这种情况下

有关在XAML中使用同集中User Control

说到“先有鸡还是先有蛋”问题让我想起了另个困扰自己很久问题请看下面代码:

假设我有这样个project

现在我想把MyControl用在我Window1里如果代码写成这样:

<Window x:Class="WpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-:WpfApplication"
Title="Window1" Height="300" Width="300">
<Grid>
<local:MyControl Name="myControl"/>
</Grid>
</Window>

当编译时候会报出:

最让人哭笑不得原因就是“MyControl是在同集里你就得使用x:Name而不是Name!”这算什么解释?跟是不是同集有什么关系?

Tags: 

延伸阅读

最新评论

发表评论