Inside XAML - XAML入门 by na57

来源:互联网 发布:line交友软件 编辑:程序博客网 时间:2024/05/17 13:45
原文:http://www.ondotnet.com/pub/a/dotnet/2004/01/19/longhorn.html


什么是XAML?

XAML的全称是eXtensible Application Markup Language,用于像HTML构建WEB页面一样构建应用程序的界面——当然,同样也可以构建WEB页面,只要使用不同的编译器就可以。Longhorn的系统界面就是使用XAML构建的。

XAML的"Hello World"

<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml">
    
<Text>Hello World</Text>
    
<Button>Click me!</Button>
</FlowPanel>



以上代码表示在页面上以“流”的方式显示一段文本和一个按钮。其效果如下图:

Some text and a button Some text and a wrapped button

为什么需要一种新的标记语言呢?

       微软设计了这种新的标记语言,而没有使用原有的HTML或SVG(Scalable Vector Graphics -- 一种基于XML的强大的图形系统)。其主要原因是:
1、HTML主要是用于WEB上的,而XAML则可以用到应用程序上。
2、XAML与运行时(runtime)有者很大的关联,而SVG则没有。XAML是被设计来直接集成在WinFX上的。

XAML与对象

    每一个XAML中的元素在运行时里都对应一种对象,拿上面的例子来说,程序在运行的时候就对应三种对象:FlowPanel, Text, 和 Button。这些对象都是Avalon类库种的一部分——XML命名空间http://schemas.microsoft.com/2003/xaml 已经告诉了我们类的来源。当然,你也可以自己定义自己的类。甚至可以用XAML写出控制台应用程序。
    通常,运行时对XAML文件的处理时编译而不是解析。当你编译一个基于XAML的项目的时候,编译器会为每一个XAML文件生成一个类,那些类包含了一些代码,用来生成在XAML文件中指定的对象。如果希望看到XAML生成的代码,可以使用Longhorn下的Visual Studio .NET Whidbey(具体过程不在此阐述,请查看原文)。
    打开生成的代码文件,可以看到一个类的定义。对于每一个XAML文件来说,编译器将只会生成一个类,而且此类将继承自XAML根元素所描述的类。例如,对于上面所说的那个XAML文件,它的根元素是FlowPanel,生成的代码就是:

public partial class MyPanel : MSAvalon.Windows.Controls.FlowPanel 
{
// 创建主面板
MSAvalon.Windows.Controls.FlowPanel _FlowPanel_1_ = this;
((MSAvalon.Windows.Serialization.ILoaded)(_FlowPanel_1_))
    .DeferLoad();

// 创建Text
MSAvalon.Windows.Controls.Text _Text_2_ = 
    
new MSAvalon.Windows.Controls.Text();
((MSAvalon.Windows.Serialization.ILoaded)(_Text_2_))
    .DeferLoad();
// 把Text设置为FlowPanel的子结点
((MSAvalon.Windows.Serialization.IAddChild)(_FlowPanel_1_))
    .AddChild(_Text_2_);
((MSAvalon.Windows.Serialization.IAddChild)(_Text_2_))
    .AddText(
"Hello World");
((MSAvalon.Windows.Serialization.ILoaded)(_Text_2_))
    .EndDeferLoad();
// 创建Button
MSAvalon.Windows.Controls.Button _Button_3_ = 
    
new MSAvalon.Windows.Controls.Button();
((MSAvalon.Windows.Serialization.ILoaded)(_Button_3_))
    .DeferLoad();
((MSAvalon.Windows.Serialization.IAddChild)(_FlowPanel_1_))
    .AddChild(_Button_3_);
((MSAvalon.Windows.Serialization.IAddChild)(_Button_3_))
    .AddText(
"Click me!");
((MSAvalon.Windows.Serialization.ILoaded)(_Button_3_))
    .EndDeferLoad();
((MSAvalon.Windows.Serialization.ILoaded)(_FlowPanel_1_))
    .EndDeferLoad();
}

    所有代码都是在编译期被生成的,也就是说,在运行期XAML文件是用不着的。如果你想动态地生成用户界面,没有必要通过编译XAML来实现,你可以使用WinFX提供的API来把未被编译的XAML直接转换成对象。

属性

    就像创建对象一样,XAML也允许为对象设置属性。例如,我们可以这样修改Button元素:

<Button Background="Red">Click me!</Button>
也就是说,把Button的背景色设置为红色。这样的话,在生成的代码中就会多了一句:

Button_3_.Background = 
    
new MSAvalon.Windows.Media.SolidColorBrush(
      MSAvalon.Windows.Media.Color.FromARGB(
25525500)
                                              );



    如果想知道XAML编译器是怎么把Red转换成以上代码的,你可以使用.Net一直在使用的技术:类型转换。类型转换是.Net设计时环境的一部分,它用来对显示在VS.Net属性窗口中的属性值和对象属性的实际属性进行相互转换。类型转换也可以生成代码来初始化对象的属性——在Windows Forms应用程序中,VS.Net在InitializeComponent方法中使用他们来创建代码。同样,XAML编译器使用类型转换技术来把属性字符串转换成初始化代码。

复杂属性

    并不是所有属性都可以使用字符串来表示,有些属性是由一系列嵌套的对象组织起来的。XAML支持一种被叫做“复杂属性”的特殊语法,使用它可以通过设置子元素——而不是属性字符串——来设置对象的属性,这个时候,XAML元素表示一个复杂属性,而不是子对象。表示复杂属性的元素必须有一定的规则:它必须是父元素所表示对象的一个属性的名称。例如:

<Button>
  
<Button.Background>
    
<LinearGradientBrush>
      
<LinearGradientBrush.GradientStops>
        
<GradientStopCollection>
          
<GradientStop Color="Red" Offset="0" />
          
<GradientStop Color="Magenta" Offset="0.25"/>
          
<GradientStop Color="Blue" Offset="0.5"/>
          
<GradientStop Color="White" Offset="1"/>
        
</GradientStopCollection>
      
</LinearGradientBrush.GradientStops>
    
</LinearGradientBrush>
  
</Button.Background>
  Click me!
</Button>



       这个例子并不是想一般的类型转换机制那样把字符串转换成一个“brush”,而实际上我们通过标记创建了一个“复杂的brush”——LinearGradientBrush ,同时,还创建了很多的填充区(fill stages)。这个例子创建了两个复杂属性,他们一个嵌套着一个。<Button.Background>元素设置了Button的背景属性,在它的内部,<LinearGradientBrush.GradientStops>元素设置了“linear gradient brush”的GradientStops 属性。复杂属性的语法工作方式与其他的XAML元素一样——它也允许构建对象树,唯一不同的是,他们将会被指定为元素的属性而不是子元素。显示的结果是:

A button with a gradient fill background

添加代码

    如我们所知,XAML编译器为每一个XAML文件生成一个派生自根元素类型的类,并且包含创建所有子元素的代码。然而,只生成这些代码还不足以构建一个UI,用户界面不仅是要展示信息,也必须能够响应用户的输入。因此,通常都要添加一些代码来提供UI的行为。
        把代码写进XAML文件是可以实现的。你可以通过Definition 命名空间中的<Code> 元素,把代码直接写在里面,XAML编译器将会把它添加到生成的代码中去。添加代码时,应该把代码写到CDATA 段中去,例如:

<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml"
    xmlns:def
="Definition">
    
<Text>Hello World</Text>
    
<Button>Click me!</Button>
    
<def:Code>
      
<![CDATA[
        // Will be added to generated source file
        public string Hello()
        {
          return "Hello!";

        }
      
]]>
    
</def:Code>
</FlowPanel>


        然而,做过动态WEB页面的人都知道,把代码和标记都放到一个文件中时极其不易维护的。最好就是把UI的描述和响应UI动作的代码分割开来。使用一种与“code-behind”很像的技术就可以实现代码和标记的分离——利用partial 关键字(partial关键字的介绍请参考C# 2.0规范)。于是,上面的代码就可以写成:
public partial class MyPanel
{
  
public string Hello()
  
{
    
return "Hello!";
  }

}


      当你用Visual Studio .NET创建一个XAML项目时,每个XAML文件会自动产生一个与之相对应的partial代码文件,在这个文件中添加的代码将会在编译XAML的时候自动添加到XAML的代码中。
   当你为一个XAML文件写代码的时候,你也希望可以对标记描述的对象进行操作,以便控制它。这时候,你需要给标记中描述的对象加上一个ID属性:

<Text ID="textElem">Hello World</Text>


    完成上面的工作以后,XAML编译器将会把对象的ID设置成上面所指定的ID。例如,上面的例子就可以这样使用:

textElem.TextRange.Text = "Foo";

要写的代码并不经常是上面所说的那种,而是那些复杂的事件处理。

事件处理

添加代码最主要的原因是进行事件处理,处理那些来自用户,或是来自UI本身的事件。我们可以通过简单的添加属性字段来描述对象要处理的事件,例如,让Button处理Click事件:

<Button Click="OnClick">Click me!</Button>


这将使编译器生成这样一段代码:

_Button_4_.Click += 
    
new MSAvalon.Windows.Controls.ClickEventHandler(this.OnClick);


为了让他通过编译,我们必须在代码文件中写一个相应的OnClick方法,Button使用的delegate是ClickEventHandler ,所以,我们写的方法应该有相应的签名:

private void OnClick(object sender, ClickEventArgs e)
{
    textElem.TextRange.Text 
= "Foo";
}


结论

XAML是一种简单但很强大的构建.Net对象树的方法。因为XAML基于XML,所以我们可以直接基于XAML来创建程序。这不仅仅使手工创建UI很容易,这也是与生成XAML的工具直接相关的——未来的设计工具也将能过使用XAML格式导出文档或绘制界面,也将会使通过XSLT把XML数据转换到XAML中变得容易。XAML使用户界面设计清晰的从代码中分离出来,而且,XAML被集成到WinFX中,这使得使用代码操作那些定义在文件中的元素很容易。

 
原创粉丝点击