XAML那些事儿

     这是来北京第二篇随笔。

     很久没有写Silverlight的文章了,如果你从我只有十几篇的随笔中找出可怜的2,3篇Silverlight的文章,你会发现那已经是几个月之前的事情了。我其实比较低产,所以关注我其实没什么好处,我仅仅只能向我的粉丝们保证:这里的内容,其他地儿没有!

     这个标题其实也在Live Writer里放了很久。直到前几天,看到Alexis在闪存中回复说苏鹏老师的Silverlight视频中讲到我的一篇Silverlight的文章,才突然来了点兴致,于是决定把这篇随笔在找到工作之前完成,因为一旦开始新工作可能会比较忙没时间写。

     XAML在Silverlight或WPF的开发中,初学者会认为很简单:不就是设计布局的吗,我把控件拖到适当的位置,然后了解一下数据绑定就可以做项目了。

     哎呀,咱们太有缘分了,我也是这么开始学习的耶!

     可是话是这么说,嘿,朋友,问你几个问题:

1. 为什么在XAML中还可以声明一个非UI对象,它不是专用来布局的吗

2. 怎样在some.xaml.cs中引用style.xaml文件中的对象呢,我没有在some.xaml中引用style.xaml

3. 郁闷,ListBox中绑定的数据在设计器中根本看不到效果,每次都要实际运行才能看到布局效果

     其实,这一切都是XAML的问题。深入了解XAML会对学习Silverlight的过程大有益处。

1. 从绘图说起

     系统有绘图的API,因此运行在之上的应用程序能够调用相关API绘制出应用程序需要的图形。

     而对于应用程序设计人员来说,你必须告诉应用程序中绘图的方法,你想要绘制一个什么样的图形,在什么位置。

     我们通常用一个类来表示某个图形结构,这样便于抽象出一些常用的图形结构和功能,比如TextBox,Button等等都是代表不同图形结构和功能的对象。而为了区别于一般对象,也为了让绘制图形的方法更好的区别对象,通常具有UI特性的对象必须是一些固定类的子类,比如Silverlight中的UIElement。

     因此,这里注意一点,XAML并不是用来布局或者绘制UI的,我们后面看到,它仅是一种声明对象的方法。这里我们不需要借助XAML,我们一样可以绘制UI,试试显示这个UserControl:

public class NoXamlControl : UserControl
{
    public NoXamlControl()
    {
        Rectangle rect = new Rectangle()
        {
            Fill = new SolidColorBrush(Colors.Red),
            Width = 50,
            Height = 50
        };

        Grid grid = new Grid();
        grid.Children.Add(rect);
        this.Content = grid;
    }
}

     我们看到,只需要声明一定的UI对象(代表一定的图形结构和位置),就可以绘制UI。

2. 声明式语言

     然而你可以看到上面的绘制UI的方式,如果我们整个页面都是这样的方式去绘制UI,那么会有以下问题:

1. 完全设计时看不到效果,必须运行才能看到

2. 大量的对象构造代码,没有结构,不便于维护,比如你想修改某个控件,可能不能很快找到这个对象在什么地方构造的

3. UI设计人员没有工作了

     XAML是一种声明式语言,它其实就是一个XML文件。因为XML的结构很容易用来表示一个对象:

1. XML中的Property或者子节点对应对象的属性

2. XML中的Property的值或者节点的Value对应某个属性的值

3. 子节点可以是另一个对象的引用

     所以,XML很自然被用于声明对象,而且运用XML声明对象可以有以下好处:

1. 具有清晰的层次结构,很容易用于表示UI对象,因为UI对象其实很容易表示成一棵树,只要添加一个容器的概念

2. 便于修改,很容易定位到一个需要修改的对象

3. 可以让UI设计人员和业务开发人员分开

     因此,XML被大量用于声明对象,这不需要我们通过代码来构造对象。

3. 应用程序怎样结合声明式语言

     那声明的对象怎么被用在程序中呢,因为程序在运行的时候需要使用这些对象(当然这是不好的设计模式,我们看到MVC中,其实View中声明的对象就不会被我们的程序使用,但还是会被框架使用)。

     所以,一般有一个加载声明的对象的方法,我们看UserControl的构造函数(在InitializeComponent()方法上右键—》Go To Definition):

/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
    if (_contentLoaded) {
        return;
    }
    _contentLoaded = true;
    System.Windows.Application.LoadComponent(this, new System.Uri("/SilverlightApplication2;component/MainPage.xaml", System.UriKind.Relative));
    this.LayoutRoot = ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
}

     这里的LoadComponment方法其实就是加载构造XAML的对象树,它可以将制定的XAML文件构造成一个该XAML文件所表示的对象,除非这个XAML文件格式错误。所以一个名为style.xaml的XAML文件:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

</ResourceDictionary>

     我们可以在程序中这样加载:

ResourceDictionary dict = new ResourceDictionary();
System.Windows.Application.LoadComponent(dict, new System.Uri("/SilverlightApplication2;component/style.xaml", System.UriKind.Relative));

     这样,Silverlight就可以和声明式语言结合起来。其实所有支持声明式语言的程序都是这样的,有一个XML文件来声明对象,有一个加载器来构造XML中的对象。

     从这里我们可以看出,声明式语言的作用主要在于以声明的方式创建对象,而不仅仅是布局,所以在XAML中我们同样可以创建非UI的对象。例如添加一个string的对象或者其他自定义的非UI对象。

4. 声明式语言用在IDE中--视图设计器(工作原理)

     声明式语言其实有很多用处,比如你可以在配置文件中配置某些值,而不是将对象的构造放在程序内部,而不可修改。

     当然,我们最熟悉的却是视图设计器。

     需要复杂UI的设计需要我们能在设计时看到运行时的效果,并且需要将UI设计部分从程序抽取出来。而我们看到UI对象非常适合用声明式语言来构造,于是这样的方式很快被用于视图设计器。

     视图设计器的工作原理:其实就是将XML中声明的UI对象按照UI对象表示的结构模拟运行时立即显示出来。它一般有一个单独的方法构造声明的对象树,然后将按照UI特性立即显示给开发者。所以,一般设计器有两种视图:XAML和Design。

     所以,这里只要XAML表示的是一个UI元素,那么它就可以被呈现,而非UI的资源文件就不会被呈现,我们看下面代码:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Rectangle Fill="Red" Height="100" Width="100"/>
    </Grid>
</UserControl>

     如果你足够细心,你会发现这仅仅是一个XAML文件,它没有对应的.xaml.cs隐藏文件,你切换到Design视图,仍然是能够被呈现的。只要是UI元素,就可以在设计时被呈现。

     那想想如果XAML中代表的是特定的对象结构和另一个设计时模拟器,这个Design是不是还可以有特殊的呈现方式呢,没错,WF。所以我们知道WF可以使用XAML来设计。

     这里我们也可以了解设计时模拟器和真实运行时的区别:

1. 设计时XAML对象树是由设计时模拟器加载的,是我们无法控制的,而在运行时是可以修改的对象属性的

2. 设计时只需要XAML文件即可

3. 设计时无法执行事件,及其他方法,因此不会调用数据库之类获取数据,所以如果ListBox的数据需要来自数据库,将无法看到设计时效果

5. Silverlight加载XAML对象树

     那么Silverlight其实到底是怎么加载XAML对象树的呢,不管是设计时模拟器的调用还是运行时的调用,这里其实都是一样的。

     XAML是一个XML文件,它表示的是一个对象。因此Silverlight加载器会根据对象类型创建这个对象树,起创建方式是从上至下顺序创建,在遇到一个对象类型的时候,调用类型的公共无参构造函数。

     因此:

1. 任何具有公共无参构造函数的类型都可以声明在XAML对象树中被构造。所以我们说XAML是另一种声明对象的方式

2. 如果一个元素需要引用另一个元素的属性值,另一个元素必须声明在这个元素的前面,所以我们通常将资源Resources声明在前面

3. 在构造对象时,公共无参构造函数将被执行

因此,如果有一个UserControl,它具有这样的构造函数:

public partial class SilverlightControl1 : UserControl
{
    public SilverlightControl1()
    {
        InitializeComponent();
        MessageBox.Show("Hello");
    }
}

     则你在设计时能看到这个弹出的Hello的消息。

     Silverlight就是这样构造对象树的。

6. Visual Studio和Expression Blend设计时分析

     那么我们要怎样在设计时能够看到效果呢,因为往往很多数据都是来自数据库。

     如果看我以前关于Blend的设计教程,我里面提到的一点就是示例数据,也就是本地数据。其实现在我们很容易理解,就是将资源文件添加到XAML中,后面就可以引用,所以Blend设计一定需要这样的示例的模拟支持。

blend

     另外值得注意的是,在Blend中,XAML不仅仅是设计静态布局,还具备设计行为的能力。这其实主要是归功于Behavior,VisualState等相关对象的设计,可以参看我这方面详细介绍的文章。

     但是,为什么XAML还可以做这么神奇的事情,其实原因也很简单,它可以调用构造函数,它的设计时模拟器可以根据相关类型呈现不同的视图给你,比如编辑各种模板,编辑动画,等等。这些我相信你想得通。

7. 总结

     好了,XAML的介绍到此为止!

     自上一篇Silverlight的文章,已经有几个月了,虽然低产,但是我希望每篇文章都会给你不一样的收获。也许能让你真正的理解一些东西,做到举一反三,也许我还没没有做到这个目标,读者自己去评价吧,我只能做到这样了,呵呵。

     虽然只有几篇,其实每一篇的内容都是精心挑选,精心设计写作思路的,这里再将地址整理一下。

     Silverlight系列文章:

     Behavior:http://www.cnblogs.com/hielvis/archive/2010/10/06/1806813.html

     Silverlight布局:http://www.cnblogs.com/hielvis/archive/2010/10/12/1847025.html

     Blend模板设计:http://www.cnblogs.com/hielvis/archive/2010/10/21/1857415.html

     XAML那些事儿:http://www.cnblogs.com/hielvis/archive/2011/02/27/1966201.html

     Blend 4:http://www.cnblogs.com/hielvis/archive/2010/10/09/1846046.html

     再加上下一篇:《依赖属性那些事儿》,Silverlight系列就到此结束,应该是我想表达的一个特别的系列,对不起没有太多简单的教程,我希望让你去理解一些东西,如果你不愿意去理解,那就不是我的事了,呵呵。

     有什么问题,可以留言,欢迎讨论!

posted on 2011-02-27 12:21  秦春林  阅读(6224)  评论(4编辑  收藏  举报

导航