两只老虎--劳虎和胭脂虎的杰作--无废话XML
XML语法领进门
<?xml version="1.0" encoding="GB2312" ?> <!-- xml 声明,<? 是处理指令PI, version是属性 "GB2313"是属性值,
encoding也是属性 -->
tag: elements , attributes ,俗称的标签实际上包含了元素和属性,属性后的属性值要用""括起来。
Well-formed 格式要正确。xml很严格
elements 要用/>关起来,必须是封闭的。
标签之间不得交叉。
有法可证的XML -- valid xml,用什么验证呢? DTD(document type defination) DTD定义出来的一套应用,专业术语叫语汇
vocabulary
大小写有区分
奇特的数据区: <![CDATA[ ??????? ]]>
空行空格都作数
PI与样规链接
<? ?> PI- procession instruction 给解析器下游的程序用的,提供处理指示。
<?xml-stylesheet href="style.css" type="text/css" ?> 这个标签提供了样规的链接和类型,样规是为xml提供格式用的。
url : uniform resource locator
uri: uniform resource identifier
urn: uniform resource name
命名空间, 命名空间前置字符串,预设的命名空间(没有前置字符串)
XAML(读作zammel) 微软的东东,主要为WPF创建的标记语言
XAML BASICS
The xaml standard is quite straightforward once you understand a few ground rules:
>Every element in a XAML document maps to an instance of a .net class, the name of the element matches the
name of the class exactly.
>As with any xaml ducument, you can nest one element inside another.As you will see,xaml gives every class the
flexibility how it deal with this situation. however, nesting is usually a way to express containment.
>you can set the properties of each class through attributes.
1 <Window x:Class="WindowsApplication1.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="Window1" Height="300" Width="300">
5
6 <Grid>
7 </Grid>
8 </Window>
XAML名字空间:显然,仅仅提供一个类名是不够的,XAML解析器需要知道该类在.NET名字空间中的具体位置,也就是具体的名字空
间。如果不指定名字空间,那么Window类既可以是System.Windows.Window类,又可以是第三方提供的一个组件的名字空间中的类
,也可以是你的应用程序中的一个类。瞧,第二行, xmlns,该属性是xml保留的一个属性名,用来定义名字空间,xmlns后面没有
尾巴,表示它定义的是缺省空间"http://schemas.microsoft.com/winfx/2006/xaml/presentation",所以Window类的名字空间
就是上述的名字空间。显然,xml的名字空间没有和某个特定的.NET 名字空间一致。这有几个原因,其一,XML的名字空间按照惯例
采用URI--Uniform Resource Identifier. 其二,如果xml名字空间和.nethttp://writeblog.youkuaiyun.com/PostEdit.aspx?
entryId=3210013名字空间一一对应,那么会使XAML文档很复杂,它将包含几十个名字空间--System.Windows下的所有名字空间。
如果每个.net名字空间都对应一个XML名字空间,那么你每定义一个控件都需要在前面加上不同的名字空间,很快,XML变得杂乱了
。WPF的创建者将这些.NET名字空间汇集到一个单一的XML名字空间。这样之所以可行,是因为.NET名字空间下的所有的WPF类(可能
处于不同的子名字空间),它们的名字都没有重复。XML解析器看到了缺省的WPF空间下的Window和Grid,它将搜索.对应NET名字空
间(所有WPF类),直到它搜到 System.Windows.Window 和 System.Windows.Controls.Grid.
代码绑定的类:
类名属性:Class
1 <Window x:Class="WindowsApplication1.Window1"
Class 前有一个x的前缀,表示类名是一个更上层的xaml名字空间--http://schemas.microsoft.com/winfx/2006/xaml
Class属性告诉parser创建一个名叫Window1的类,该类从System.Windows.Window类派生出来。在编译时,Window1类自动产生。
但是,一件有趣的事情发生了,你可以自己写一个Window1类,该类将与自动产生的类合并,你自己定义的类正是一个事件处理器。
为什么呢?因为,C#语言有一个特征,叫作 partial classes,即部分类,用一个关键字partial就可以定义部分类,它允许一个类
被分割成不同的部分,处在不同的文件中。
special method--
when xaml is compiled into code, there is a special method named InitializeComponent() in the class contructor
of the corresponding class.
it then calls another method named LoadComponent which is a method of System.Windows.Application.
LoadComponent extract the BAXML from your assembly and uses it to build User Interface. As it parses BAXML, it
creates controls, sets its properties, attaches any event handler.
Name elements
if you want to manipulate controls programmatically,then you should name it. when you name it, it will create
a corresponding field in your class.
6 <Grid x:Name="grid1">
7 </Grid>
the Name property shown above is part of the xaml language, and it is used to helo integrate your code-behind
class.
Somewhat confusingly, many classes define their own Name property. You can set either the XAML Name property
or the Name property that belongs to the actual element(by leaving out the prefix):<Grid Name="grid1">
</Grid> the result is the same. why this also works? because all FrameworkElement classes has a
RuntimeNameProperty attribute which indicates which property should be treated as the name for instances of
that type,obviouly, it is usually the property that is named Name.
Properties and Events in XAML
Simple properties and Type Converters
the attributes of an element set the properties of the corresponding object. the question is that the value in
an XAML attribute is always a plain text string, however, object properties can be any .NET type.
so we need a type converter.
the XAML parser follows two steps to find a type converter:
1.it examines the property declaration, looking for a TypeConverter attribute.(if present, the TypeConverter
attribute indicates what class can perform the conversion) For example, when you use a property such as
Foreground, .NET checks the declaration of the Foreground property.先从属性(property)的声明中找TypeConverter
属性(attribute),这里应该指在xaml中检查property的typeconverter的attribute.
2.if there is no TypeConverter attribute on the property declaration, the XAML paser checks the class
declaration of the corresponding data type,从.NET的类中找
Complex Properties 复杂的properties有自己的properties.
XAML的解决之道:定义一个子元素。(属性元素法property-element syntax)
<Grid Name="grid1">
<Grid.Background>
...
</Grid.Background>
...
请注意,子元素的定义用了一个点符号“.”(period)。
如何设置子元素的properties,答案是嵌套元素:
<Grid Name="grid1">
<Grid.Background>
<LinearGradientBrush> <!--线形梯度画刷-->
</LinearGradientBrush>
</Grid.Background>
...
</Grid>
</Grid>
你还需要设置线形梯度的驻点,同样线形梯度的驻点也较复杂,它可能有很多个,所以又定义了一个嵌套子元素。
<Grid Name="grid1">
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.00" Color="Red" />
<GradientStop Offset="0.50" Color="Indigo" />
<GradientStop Offset="1.00" Color="Violet" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
...
</Grid>
等价的c#代码
LinearGradientBrush brush = new LinearGradientBrush();
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Red;
brush.GradientStops.Add(gradientStop1);
GradientStop gradientStop2 = new GradientStop();
gradientStop2.Offset = 0.5;
gradientStop2.Color = Colors.Indigo;
brush.GradientStops.Add(gradientStop2);
GradientStop gradientStop3 = new GradientStop();
gradientStop3.Offset = 1;
gradientStop3.Color = Colors.Violet;
brush.GradientStops.Add(gradientStop3);
grid1.Background = brush;
瞧!<Button ... Foreground="{x:Static SystemColors.ActiveCaptionBrush}" > (bracketed by curly braces.)
这时扩展的标记法。当你需要:
1.set a property value to an object that already exits.
2.or you may want to set a property value dynamically,by binding it to a property in another control.
The x:prefix indicates that the StaticExtension is found in one of the XAML namespaces. You'll also encounter markup extensions that are a part of the WPF namespaces and donot have the x:prefix.
{MarkupExtensionClass Argument}. All markup extensions are implemented by classes that derive from System.Windows.Markup.MarkupExtension. The base MarkupExtension class is extremely simple - it provides a single ProvideValue() method that get the value you want.
In other words, when the XAML parser encounters the previous statement, it creates an instance of the StaticExtension class(passing in the string "SystemColors.ActiveCaptionBrush" as an argument to the constructor) and then calls ProvideValue() to get the object returned by the SystemColors.ActiveCaption.Brush static property. The Foreground property of the cmdAnswer button is then set with the retrieved object.
<TextBox ... Grid.Row="0">
[Place question here.]
</TextBox>
看到了吧,什么是attached properties. Grid.Row就是附加的属性,一个控件放在一个网格中,就有了位置的属性,这个属性是附加的。attached property的格式: DefiningClass.PropertyName
Attached properties aren't really properties at all. They're actually translated into method calls. The XAML parser calls the static method that has this form: DefiningType.SetPropertyName().
When calling SetPropertyName(), when calling SetPropertyName(), the parser passes two parameters: the object that's being modified and the property value that's specified.
附加的属性
附加的属性使得控件可以放到容器中
语法: DefiningType.PropertyName
看起来属性是属于容器类的,其实不是,是属于控件自身.定义一个附加属性相当于调用了:DefiningType.SetPropertyName(),其实,这还不是真相,真相是:所有的wpf控件都从DependencyObject类派生的,定义一个附加属性其实是在调用 DependencyObject.SetValue(Grid.RowProperty,0) 附加属性是WPF的基本组成部分,它使得WPF控件组成了一个可扩展的系统。
XAML允许每个元素自己来决定如何处理嵌套元素。this interaction is mediated through one of four mechanisms that are evaluated in this order:
>if the parent implements IList, the parser calls IList.Add() and passes in the child.
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Red;
IList list = brush.GradientStops;
list.Add(gradientStop1);
>if the parent implements IDictionary, the parser calls IDictionary.Add() and passes in the child
>if the parent is decorated with the ContentProperty attribute, the parser uses the child to set that property.
<Grid Name="grid1">
...
<TextBox Name="txtQuestion" ... >
...
</TextBox>
<Button Name="cmdAnswer" ... >
...
</Button>
<TextBox Name="txtAnswer" ... >
...
</TextBox>
</Grid>
the Grid does support ContentProperty attribut, which indicates the property that should receive any nested content. Technically, the ContentProperty attribute is applied to the Panel class, form which the Grid derives, and looks like this :
[ContentPropertyAttribute("Children")]
public abstract classPanel
this indicatess that any nested elements should be used to set the Children property. The XAML parser treats the content property differently depending on whether or not it is a collection property.Because the Panel.Children property returns a UIElementCollection, and because UIElementCollection implements IList, the parser uses the IList.Add() method to add nested cotent to the grid.
txtQuestion = new TextBox();
...
grid1.Children.Add(txtQuestion);
cmdAnswer = new Button();
...
grid1.Children.Add(cmdAnswer);
txtAnswer = new TextBox();
...
grid1.Children.Add(txtAnswer);
接下去事情如何发生要看控件是如何实现内容属性的(ContentProperty), 这里,Grid 将在指定的行列中显示所有的子控件。
内容属性在WPF中是常用的,不仅仅因为它可以用作容器来包含一组可视的项目,而且还可以用于包含单个的内容。比如TextBox和Button只包含了单个的元素或者说文字。The TextBox class uses the ContentProperty attribute to flag the TextBox.Text property.The Button class uses the ContentProperty attribute to flag the Button。The XAML parser uses the supplied text to set these properties.内容属性(ConetenProperty)可以接受任何的元素呢。但是因为文字和内容属性都没有使用集合,所以你不能包含多个元素在"按钮"中。否则,XAML解析器将抛出一个异常。
As a general rule of thumb, all controls that derive from ContentControl allow a single nested element. All controls that derive from ItemsControl allow a collection of items that map to some part of the control (such as a list of items or a tree of nodes). All controls that derive from Panel are containers that are used to organize groups of controls. The ContentControl, ItemsControl, and Panel base classes all use the ContentProperty attribute.