转载:http://blog.csdn.net/fwj380891124/article/details/8093001

1,XAML文档的树形结构:

UI在用户眼里面是个平面结构。如下图所示,在用户眼里看来,这个界面就是一个窗体里面平铺了4个文本框和一个按钮的界面。

在传统的Visual C++、Delphi、Visual Basic6.0和Windows Form程序员的思维里,UI也是一个平面的结构。因此,程序员要做的事情就是根据美工给的给定的UI布局把控件安置在窗体的表面,并用使用长度,宽度和间距把控件对齐。

与传统的设计思维不同,XAML使用树形逻辑结构来描述UI,下面是用来描述界面布局的XAML代码:

<Window x:Class="WpfApplication2.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2" Height="331" Width="454">
<StackPanel Height="279" Name="stackPanel1" Width="402" Margin="5" Background="AliceBlue">
<TextBox Height="23" Name="textBox1" Width="260" Margin="5"/>
<TextBox Height="23" Name="textBox2" Width="259" />
<StackPanel Height="100" Name="stackPanel2" Width="273" Orientation="Horizontal">
<TextBox Height="23" Name="textBox3" Width="120" Margin="5"/>
<TextBox Height="23" Name="textBox4" Width="120" Margin="5"/>
</StackPanel>
<Button Height="33" Name="button1" Width="99" Margin="10">
<Image Source="/WpfApplication2;component/Images/track.png" />
</Button>
</StackPanel>
</Window>

因为代码中有许多对Attribute的属性,所以结构看起来并不是那么清晰。如果我们把对Attribute的赋值都去掉,那么上面的代码就显现了它的树形框架结构。
     

<Window>
<StackPanel>
<TextBox />
<TextBox />
<StackPanel>
<TextBox />
<TextBox />
</StackPanel>
<Button>
<Image />
</Button>
</StackPanel>
</Window>

如果用一张图来表示上面的那段代码,它会是下面这个样子

有意思的是,针对一个“看上去一样”的UI布局,XAML代码不一定是唯一的。拿上面的UI代码布局来说,我们还可以使用不同的XAML代码来描述它。

<Window x:Class="WpfApplication2.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3" Height="348" Width="538">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250*" />
<ColumnDefinition Width="266*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="42" />
<RowDefinition Height="48" />
<RowDefinition Height="66" />
<RowDefinition Height="153" />
</Grid.RowDefinitions>
<TextBox HorizontalAlignment="Center" Margin="10,10,0,0" Name="textBox1" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<TextBox HorizontalAlignment="Center" Margin="10,10,0,0" Name="textBox2" VerticalAlignment="Top" Grid.Row="1" Grid.ColumnSpan="2"/>
<TextBox HorizontalAlignment="Right" Margin="10,10,0,0" Name="textBox3" VerticalAlignment="Top" Grid.Row="2" Grid.Column="0"/>
<TextBox HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBox4" VerticalAlignment="Top" Grid.Row="2" Grid.Column="1"/>
<Button HorizontalAlignment="Center" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Grid.Row="3" Grid.ColumnSpan="2">
<Image Source="/WpfApplication2;component/Images/track.png" />
</Button>
</Grid>
</Window>

精简后的代码是:

<Window>
<Grid>
<TextBox />
<TextBox />
<TextBox />
<TextBox />
<Button>
<Image />
</Button>
</Grid>
</Window>

框架变成了如图所示的样子:

虽然两段代码对UI的实现方式不同,但是框架都是树形的,以<Window>对象为根节点,一层一层向下包含。这种树形结构对于WPF整个体系都具有非常重要的意义,它不但影响着UI的布局设计,还深刻的影响着WPF的属性(Property)子系统和事件(Event)子系统等方方面面。在实际的编程过程中,我们经常要在这棵树上进行按名称查找元素,获取父/子节点等操作,为了方便操作这棵树,WPF基本类库为程序员准备了VisualTreeHelper和LogicTreeHelper两个助手类(Helper Class),同时还在一些重要的基类里封装了一些专门用于操作这棵树的方法。

UI布局:

你也许可能会问:既然有这么多方法可以实现同一个UI,到底应该选择哪一种方式来实现UI呢?实际上,设计师给出的UI布局是软件的一个静态快照(Static Snap),这个静态快照加上用户有可能动态操作才能够构成选择实现布局形式的完整依据。

拿上面两段代码来说,如果你希望用户在改变窗体大小后需要等比例缩小自己内部控件的尺寸,那么你选择第二种Grid;如果只希望控件在界面上做一个简单的排列,第一种足矣 StackPanel。

2,XAML中为对象赋值的方法

XAML是一种声明性语言,XAML会为每一个标签创建一个与之对应的对象,对象创建之后要对它的属性进行必要的初始化之后才有使用意义。因为XAML语言不能够编写程序的运行逻辑,所以一份XAML文档除了使用标签声明对象就是初始化对象属性了。

注意:

XAML中对对象赋值总共有两种方法:

A:使用字符串进行简单赋值。

B:使用属性元素(Property Element)进行复杂赋值。

我用一个<Rectangle>标签的Fill为例来介绍这两种方法:

2.1   使用标签的Attribute为对象属性赋值

前面我们已经知道,一个标签的Attribute有一部分与对象的Property对应,<Rectangle>标签里面的Fill这个Attribute就是这样,他与Rectangle类对象的Fill属性对应,在MSDN文档库里可以查询到,Retangle类的Fill类型是一个Brush。Brush是一个抽象类,凡是以Brush为基类的类都可以成为Fill的属性值。Brush的派生类有很多:

  • SolidColorBrush:单色画刷。

  • LinearGradientBrush:线性渐变画刷。
  • RadialGradientBrush:径向渐变画刷。
  • ImageBrush:位图画刷。
  • DrawingBrush:矢量图画刷。
  • VisualBrush:可视元素画刷。

下面这个例子是使用SolidColorBrsh和LinearGradientBrush两种。

我们先学习使用字符串对Attribute的简单赋值,假设我们的Rectangle只需要填充成单一的蓝色,那么我们只需要简单的写成:

<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window4" Height="335" Width="472">
<Grid>
<Rectangle Height="127" HorizontalAlignment="Left" Margin="103,96,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="243" Fill="Blue"/>
</Grid>
</Window>

运行效果如下图:

我们看到,blue这个字符串最终被翻译成了SolidcolorBrush并赋值给了Rectangle对象。换成C#代码是这样。

SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.Blue;
this.rectangle1.Fill = brush;

需要注意的是,这种Attribute=Value的赋值时,由于XAML语法有限,Value只能是一个字符串值,这就引发了下面两个问题:

A,如果一个类可以使用XAML类来进行声明,并允许它的的Property可以和它的Attribute互相映射,那就需要为这些Property准备适当的转换机制。

B,由于Value是个字符串,所以其格式复杂程度有限,尽管可以在转换机制里面包含一定的按格式解析字符串的功能以便转换成较复杂的目标对象,但这会让最终的XAML使用者头疼不已,因为他们不得不在一个没有编码辅助的情况下手写一个格式复杂的字符串来满足需求。
第一个问题的解决方式是使用TypeConverter类的派生类,在派生类里面重新TypeConverter的一些方法,第二个问题的解决办法就是使用属性元素(Property Element)。

2.2使用TypeConverter类将XAML标签的Attribute与对象的Property进行映射

注意

本小节的例子对于初学者来说理解起来比较困难而且实用性不大,主要是为喜欢刨根问底的WPF开发人员准备的,初学者可以跳过这一节。

首先我们准备一个类:

public class Human
{
public string Name{get;set;}
public Human Child { get; set; }
}

这个类具有连个属性:

String类型的Name;

Human类型的Child;

现在我们期望的是,如果我们在XAML里面这样写:

<Window.Resources>
<local:Human x:Key="human" child="ABC"></local:Human>
</Window.Resources>

则能够为Human实例的Child属性赋一个Human类型的值,并且Child.Name就是这个字符串的值。

我们点看看直接写行不行。在UI上添加一个Button,并在Click事件里面写上:

Human human = (Human)this.FindResource("human");
MessageBox.Show(human.Child.Name);

编译没有问题,但是在单击按钮的时候会弹出异常,告诉Child不存在,为什么Child不存在呢?原因很简单,Human的Child属性是Human类型,而XAML代码中的ABC是个字符串,编译器不知道如何将一个字符串转换成一个Human实例。那我们应该怎么办呢?办法是使用TypeConverter和TypeConvertAttribute这两个类。

首先,我们要从TypeConvert派生出自己的类,并重写它的ConverFrom方法。这个方法有一个参数名叫做Value,这个值就是XAML文档里面为其设置的值。我们要做的就是将这个值翻译成合适对象的值并赋值给对象的属性:

public class StringToHumanTypeConverter:TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if(value is string)
{
Human human = new Human();
human.Name = value as string;
return human;
}
return base.ConvertFrom(context, culture, value);
}
}

有了这个类还不够,你还需要使用TypeConvertAtrribute这个特征类把StringToHumanTypeConverter这个类"粘贴"到作为目标的Human类上。

[TypeConverterAttribute(typeof(StringToHumanTypeConverter))]
public class Human
{
public string Name{get;set;}
public Human Child { get; set; }
}

因为特征类在书写的时候可以省略Attribute这个词,所以也可以写作:

[TypeConverter(typeof(StringToHumanTypeConverter))]
public class Human
{
public string Name{get;set;}
public Human Child { get; set; }
}

但这样写,我们需要认清括号里是TypeConverterAttribute而不是TypeConverter。

完成之后我们再单击按钮,我们想要的结果出来了,如下图:

注意:

TypeConverter类的使用远远不只是重载一个ConvertFrom方法这么简单,为了配合这个方法的运行,还需要重载其它几个方法。详细的使用方法请参见TypeConvert类库文档。

2.3属性元素

在XAML中,非空标签均具有自己的内容(Content)。标签的内容就是指夹在起始标签和结束标签中的一些子级标签,每个子级标签都是父级标签中的一个元素(Element),简称为父级标签的一个元素。顾名思义,属性元素指的就是某个标签的一个元素对应这个标签的一个属性,即已元素的形式来表达一个实例的属性。代码描述为:

<ClassName>
<ClassName.PropertyName>
</ClassName.PropertyName>
<ClassName>

这样,这个标签的内部就可以使用对象(而不仅限于字符串)进行赋值了。

如果把上面的例子用标签语法改写一下,XAML将是这样:

<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="335" Width="472">
<Window.Resources>
<local:Human x:Key="human" Child="ABC"></local:Human>
</Window.Resources>
<Grid>
<Rectangle Height="127" HorizontalAlignment="Left" Margin="103,96,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="243">
<Rectangle.Fill>
<SolidColorBrush Color="Blue"></SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="180,256,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window></span>

效果和之前的效果一样,对于简单的赋值而言,属性标签体现不出来什么优势,反而让代码看起来有点冗长。但遇到属性复杂的对象这种语法的优势就体现出来了,如使用线性渐变来填充这个矩形:

<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="335" Width="472">
<Window.Resources>
<local:Human x:Key="human" Child="ABC"></local:Human>
</Window.Resources>
<Grid>
<Rectangle Height="127" HorizontalAlignment="Left" Margin="103,96,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="243">
<Rectangle.Fill> <LinearGradientBrush>
<LinearGradientBrush.StartPoint>
<Point X="0" Y="0.5"></Point>
</LinearGradientBrush.StartPoint>
<LinearGradientBrush.EndPoint>
<Point X="1" Y="1.5"></Point>
</LinearGradientBrush.EndPoint>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Black" Offset="1" />
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#CD524A4A" Offset="0.434" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="180,256,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window>

LinearGradientBrush的GradientStops属性是一个GradientStop的集合(GradientStopCollection),即一些列的矢量渐变填充点。在这些填充点之间,系统会自己进行插值计算,计算出过渡色彩。填充矢量方向是StartPoint和EndPoint两个属性(类型为Point)的连线方向,矩形的左上脚为0,0,右下角为1,1。这段代码中,针对这三个属性都是使用属性标签的赋值方法。

古语道,“过犹不及”。上面的代码为了突出属性元素我将所有的属性都使用属性元素的方法进行赋值,结果代码的可读性一落千丈。经过优化,代码变成了如下,少了1/3。

<Window x:Class="WpfApplication2.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="Window4" Height="335" Width="472">
<Window.Resources>
<local:Human x:Key="human" Child="ABC"></local:Human>
</Window.Resources>
<Grid>
<Rectangle Height="127" HorizontalAlignment="Left" Margin="103,96,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="243">
<Rectangle.Fill>
<LinearGradientBrush></pre><pre name="code" class="html"> <LinearGradientBrush.GradientStops>
<GradientStop Color="Black" Offset="1" />
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#CD524A4A" Offset="0.434" />
</LinearGradientBrush.GradientStops>
</pre><pre name="code" class="html"> </LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="180,256,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window> </span>

这里几个简化XAML的技巧:

能用Atrribute=value方式进行赋值的就不要使用属性元素。

充分利用默认值,除去冗余。

充分利用XAML的简写方式:XAML的简写方式很多,需要在工作中慢慢积累。

4 WPF学习---系统的学习XAML语法的更多相关文章

  1. set学习(系统的学习)

    set是STL中一种标准关联容器.它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高.set,顾名思义是“集合”的意思,在set中 ...

  2. 【WPF系列】基础学习-XAML

    引言 WPF框架中已经提到,WPF框架提供XAML基本服务.WPF中XAML的引入向开发者提供UI设计和代码分离的编程型.XAML是WPF中提出的一个具有重要意义的新技术,基本涉及WPF中所有UI开发 ...

  3. WPF学习(2)XAML

    XAML(eXtensible Application Markup Language,可扩展应用程序标记语言)是一种声明式的编程语言,遵循XML的语法.WPF使用XAML来设计UI具有易用性.高效性 ...

  4. 【WPF学习】第一章 XAML介绍

    XAML(Extensible Application Markup Language的简写,发音为“zammel”)是用于实例化.NET对象的标记语言.尽管XAML是一种应用于诸多不同问题领域的技术 ...

  5. WPF学习01:初始XAML浅析

    本文内容: 浅析WPF应用默认创建的XAML中元素.attributes. 新建WPF工程“HelloWPF”. 初始创建的主窗体XAML代码如下: <Window x:Class=" ...

  6. 如何系统的学习Java

    初学者记住一点,学习Java一定是连续性的且循序渐进的“系统化”学习,首先我给你提供一个优秀Java工程师的学习路线. web前端方面:html.css,Java.jQuery.xml解析.Boots ...

  7. JavaSE入门学习7:Java基础语法之语句(下)

    继续接着Java基础语法来:JavaSE入门学习5:Java基础语法(一)和JavaSE入门学习6:Java基础语法(二). 语句 Java经常使用的3种循环:while.do...while,for ...

  8. JavaSE入门学习6:Java基础语法之运算符和语句(上)

    继续接着上篇:JavaSE入门学习5:Java基础语法(一)来看Java的基础语法. 五运算符 运算符是一种"功能"符号,用以通知Java进行相关的运算.比方.我们须要将变量age ...

  9. Linux 系统编程 学习:11-线程:线程同步

    Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...

随机推荐

  1. [OC Foundation框架 - 22] 集合的内存管理

    A.集合的手动内存管理 NSArray addObject: 加入的元素执行一次retain removeObject: 被删除的元素执行一次release removeAllObjects: 所有元 ...

  2. 转载 https协议和http协议的区别

    转载原地址: http://aajs800.blog.51cto.com/519255/109555 什么是HTTPS: HTTPS(Secure Hypertext Transfer Protoco ...

  3. 推荐《C Primer Plus(第五版)中文版》【worldsing笔记】

      老外写的C书,看了你会有一种哇塞的感觉,这里提供PDF扫描版的下在,包含数内的例程,请大家支持原版!! C Primer Plus(第五版)中文版.pdf  下载地址:http://pan.bai ...

  4. 初学XPath,其实很简单

    XPath 是一门在 XML 文档中查找信息的语言.XPath 用于在 XML 文档中通过元素和属性进行导航. (我的理解:XPath 就是一个用来查找xml节点的路径语言,一个路径字符串语法) XM ...

  5. DotNET 开发常用工具汇集

    开发用专业软件已经很多了,来说说开发用的辅助软件把--分享我常使用的辅助软件 个人工具清单 .NET 程序员十种必备工具 新.net开发十大必备工具 .NET开发不可错过的25款必备工具 我的生活必备 ...

  6. jdbc连接的工具类

    在不实用框架的情况下,有一个jdbc的工具类来进行数据库的连接就再好不过了,下面提供这个工具类DBUtil.java package org.jdbc.test; import java.io.Inp ...

  7. 【新闻】超灵敏MRI技术:照亮人体肺部

    人口健康直接影响到一个国家的经济发展和社会进步.据我国2013年发布的肿瘤发病率统计年报表明,肺癌是我国目前首位恶性肿瘤,是癌症死亡的头号杀手,目前城市中每4名死亡的癌症患者中,约有1名是肺癌.如何开 ...

  8. Windows Phone开发工具初体验【转载】

    Windows Phone开发工具在MIX 2010上火热登场了.Windows Mobile开发者们压抑许久的热情终于爆发出来,对于Windows Phone的华丽转身,开发者们褒贬不一,有人对Si ...

  9. ORM之二:核心接口与扩展操作

    一.数据库提供者接口 /// <summary> /// 数据库提供者 /// </summary> public interface IDbProvider : IDispo ...

  10. zookeeper使用场景【转】

    分布式网站架构后续:zookeeper技术浅析   Zookeeper是hadoop的一个子项目,虽然源自hadoop,但是我发现zookeeper脱离hadoop的范畴开发分布式框架的运用越来越多. ...