WPF入门教程

1:什么是XAML?

  XAML,英文全称Extensible Application Markup Language,即“可扩展应用程序标记语言”。这个东西就是微软开发出来抄袭(HTML+CSS+JavaScript框架的),当然了,web前端框架也有它的好处,前后端可以完美的实现分离,前端开发人员专注于界面的UI设计以及美化,后端的程序员们专注于后台逻辑代码的实现,这种UI与逻辑之间的松耦合关系无疑增添了项目上的灵活性,微软正是在这个思路的基础上,设计了XAML。说的简单点,XAML是WPF技术中专门用于设计UI的语言。

2:我第一个 WPF 桌面应用程序

下面,让我们直接撸微软的一个小例子吧。

1:首先,在你的Visual Studio中新建名为HelloWPF的Application项目。

  运行这个项目,针对XAML文件,是可以进行所见即所得的可视化设计的。你在XAML代码上做的修改,只要是合乎语法的,那么在设计器里就会立刻反映出来(有时候需要刷新一下)。如果你发现设计器里显示不出来了,那一定是XAML语句出了问题,最好想办法修正它。不然的话,在设计器里都看不到效果、只能运行起来看,这还算什么可视化编程呢?要XAML还有什么意义呢?

  在我们正式剖析代码之前,让我们牢记两件事:

1. 这个世界是一个“组合”的世界——汽车是由一个车身和四个轮子组合成的;飞机是由机翅和机身组合成的。这些组成部分,我们称之为元素(Element

2. 在XAML文件里,每写一个元素就相当于定义了一个元素所对应的.NET Framework类的实例。

  有必要强调一点:如果一个实体是由一些(同类或者不同类的)子对象组合成的,我们就称这个实体为“父元素”、称这些子对象为“子元素”,因为父元素包含着子元素,所以常把父元素称为“包含元素”、把子元素称为“被包含元素”或父元素的“内容”——我们需要注意,被包含元素并不等同于包含元素的属性(property),被包含元素只是包含元素的一个部分。  初听这句话,肯定是一头雾水,OK,让我举个两个例子。比如有一个班级,这个班由56个学生、1个老师、60张桌子、70把椅子组成,那么这些学生、老师、桌子和椅子,只是这个班级的一些“组成部分”;而这个班级的人数、班级隶属的年级、班级的编号是这个班级的属性。再比如我有一个Window,这个Window里有1个Grid,这个Grid里又包含着3个TextBox、2个Button,那么这1个Grid就是这个Window的子元素,3个TextBox和2个Button又是Grid的子元素;而Window的Name、Icon、尺寸乃至Resources都是这个Window的属性。

你可能会问,这个道理这么简单,有什么好强调的呀?

原因是这样的:对于C#的类而言,属性(property)肯定是一个对象(比如Window的Name属性,它就是一个String类型的对象),这个对象也是类实例的一个组成部分;而在对这个类进行扩展的时候(对这个类进行派生),我们新添加进来的元素(比如3个TextBox和2个Button)也是类实例的组成部分。OK,大家看到了,从现实世界抽象到编程世界来之后,它们的区别就不那么鲜明了。为了再让它们的区别“鲜明”起来,请大家记住两句话:

  • 属性对象(元素)是父元素所固有的,子元素则可由设计人员来进行增减
  • 属性对象(元素)是隶属于父类的(从父类继承而来),子元素是在设计派生类时新添加进来的

之所以在剖析代码之前讲述这些东西,是因为XAML是一种XML语言,它的语法完全是元素嵌套组合式的,而属性和子元素也都是类实例的组合体,如果不先分清楚,读代码的时候一定会感觉混乱。

 在了解了这些内容之后,我们就可以放心地读代码了。

  1. 打开MainWindow.xaml ,你会看到代码如下:
<Window x:Class="HelloWPF.MainWindow"
        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"
        xmlns:local="clr-namespace:HelloWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        
    </Grid>
</Window>

    那么,让我们一句一句的分析这段代码。

    就像我们遇到复杂长句时先要分析它的主干一样——让我们暂时抛开花花绿绿的代码,看看这段程序的主干是什么。喔~~~这段程序的主干是如此的简单!就是一个<Window>元素里包含着一个 <Grid>元素。

<Window>
    <Grid>
        
    </Grid>
</Window>

   一个句子的主干找出来之后,句子的大意也就明白了。我们已经得到这段程序的主干了,那这段程序说的是什么呢?前面我叮嘱大家一定要记住两件事件。其中一件就是“见到元素就相当于创建实例”。我想你一定会说:这段程序就是在定义一个Window类的实例,这个实例的一个组成部分是一个Grid类的实例。

  呵呵,对于这个答案,既可以说它是对的,也可以说它是错的,为什么呢?

  请注意,Window元素的一个attributex:Class="HelloWPF.MainWindow",这个Class就是在告诉我们,嘿!本XAML文件实际上是这个类的UI部分哦!。本例中,类名就是等号后面的“HelloWPF.MainWindow,也就是说,是HelloWPF名称空间中的MainWindow这个类。在项目浏览器中找到与这个XAML文件配套的C#文件(XAML文件名.cs),果然能找到这个类。

namespace HelloWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}
从这个角度来看,上面的答案就是错的了——因为这段代码是在定义一个MainWindow的实例会是什么样子。


  那为什么又可以说它的对的呢?显然,MainWindow是Window类的派生类,根据多态的原理,你说MainWindow是一个”Window并没有错——就像你说“鸭子是一只鸟”一样正确。在派生过程中,我们使用<Grid>标签为它添加了一个Grid类型的UI成员——派生么,一定是要做些扩展的。

  看到这儿,我想你已经猜到了,XAML文件就是用来定义MainWindow这个类的UI部分(一旦这个类创建了实例,那这个实例的UI将与XAML代码的描述相一致)。微软通过XAML语言把UI设计完全暴露给了我们,让设计师可以像设计网页一样来设计桌面程序的界面。至于这个类的逻辑部分,还是用传统的C#语言来实现。这样,设计人员和开发人员就能各司其职、协同工作了。

  一个类能够“掰成两半”来写,这要归功于partial这个关键字,使用这个关键字,可以把一个类的代码分散在多处来实现。可问题又来了——XAML代码怎么和C#代码“对接”啊?呵呵,这个还真不用咱们操心,微软的XAML解析器本着“进村悄悄地,开枪地不要”原则,在背后把这件事完成了。因为XAML代码中没有逻辑,所以,解析XAML的大部分工作就是按照元素标签的描述把对象创建出来——比如,解析器见到有<Grid>标签出现,就会生成与C#代码new Grid()等价的代码。

3. Xaml名称空间

  如果你问一个初学XAML的人(碰巧他还没有XML编程经验):最让他迷惑的是什么?我想他会告诉是:“就是那个x!”老实讲,我就是他们中的一员,初学的时候我也很“痛恨”那个x。一会儿是“:x”,一会儿是“x:”……这个x到底是什么呢?

   其实非常简单——这个x是一个名称空间、一个使用XML语法声明的名称空间。只是XML语言声明名称空间的时候语法比较怪而已。下面,让我一一为你解释。

  首先,如果你使用C#,那么你对这几句代码一定不陌生:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  这是对.NET Framework类库中名称空间的引用。一旦引用了这些名称空间,在使用这些名称空间中的类时就不并在再类名前加上长长的前缀了。

  请大家考虑这样一种情况:有两个很长的名称空间,我需要使用它们中的类,但不巧的是这两个名称空间里的类又有很多是重名的……怎么办呢?呵呵,我们可以使用名称空间的别名来解决这个问题:

using Software = Microsoft.Google.Adobe.RedHat.CA;
using Hardware = IBM.Sun.RedHat.Dell.Lenovo.HP.Oracle;

  这样,即解决了输入字符过多的问题,又解决了类名冲突的问题:

Software.Company c1 = new Software.Company();
Hardware.Company c2 = new Hardware.Company();
     XAML名称空间跟C#的名称空间别名类似,但不完全一样。先让我们看那个xx其实就是一个简写的名称空间啦!xmlns就是XML Namespace的简写意思是要声明一个名称空间。
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    这句话的意思就是:声明一个名为x的名称空间(xmlns与名称空间的名字间用冒号隔开)。后面为什么要跟一个“网址”呢?呵呵,我们都被骗了——那根本不是一个网址,不信你用IE试试。其实,它就是一个普通的字符串,你尽可以把它当成“Microsoft.WinFX.XAML”来理解。但值得注意的一点是:这个字符串不只代表着一个名称空间,而是代表了一组名称空间,这组名称空间合称“XAML语言空间”——因此,它的名字是x。换句话说,这个x相当于一下子引用了好几个名称空间进来,这几个名称空间在.NET Framework里都能查到,包含这些名称空间里的类都是与XAML语言的语法、特性、功能有关的。

    在XAML中,想使用某个名称空间里的类就要使用“名称空间+冒号+类名”的格式,所以:x:Class的意思是使用x名称空间里名为Class的类。类似地,以后我们还会看到x:Static、x:Type、x:XData等等,这都是在使用x这个名称空间里的类。

  与声明x名称空间类似,这儿还有一句:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    这回的网址与前面的不一样,最后一个词是“presentation”,顾名思义,这回引用进来的一组名称空间一定是与显示相关的。说对了!比如System.Window.Control这个.NET Framework的名称空间就包含在里面,这个名称空间里几乎包含了所有WPFUI元素(WPF里,我们称控件为UI元素)。 

    你可能会问:这不是在声明名称空间吗!名字哪儿去了?
   问的非常好!当xmlns后面没有跟随名称空间的名字时,就相当于省去了名称空间的名字,当使用这个名称空间中的类时就无需再加前缀(根本没前缀可加,怎么加?)。换句话说,当一个类名前面没有前缀时,“默认”就是此名称空间里的类。因此,它称为“默认名称空间”。这个用法跟using System差不多。BTW:默认名称空间只能有一个。

  大家可以动手试试看,把
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

改成:

xmlns:n="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

这时候程序就编译不过去了。当你把后面的<Grid>元素改成<n:Grid></n:Grid>后,就又可以通过编译了。

同时,让我们再看一一下X:Class的功效。

还剩下

x:Class="HelloWPF.MainWindow"
       没有仔细分析,X:这个前缀说明 这个Attribute来自于x映射的名称空间——前面我刚刚分析过,这个名称空间是对应XAML解析功能的。x:Class,顾名思义它与类有此关系,是何种关系呢?让我们做个有趣的实验:

    首先,我们把x:Class="MyFirstWpfApplication.MainWindow"这个Attribute删掉,再到MainWindow.xaml.cs文件里,把InitializeComponent();这个函数调用也删掉。编译程序,你会发现程序仍然可以运行。为什么呢?打开App.xaml这个文件,你能发现这样一个Attribute——StartupUri="MainWindow.xaml",它是在告诉编译器把由MainWindow.xaml解析后生成的窗体作为程序启动时的主窗体。也就是说,只要MainWindow.xaml文件能够被正确解析成一个窗体,程序就可以正常运行。

  然后,我们恢复x:Class这个Attribute,更改它的值为x:Class="MyFirstWpfApplication.WindowABC"。编译之后,仍然可以正确运行。这时,我们使用IL Disassembler(中间语言反编译器)打开项目的编译结果,你会发现在由项目编译生成的程序集里包含一个名为WindowABC的类。

   这说明, x:Class 这个 Attribute 的作用是当 XAML 解析器将包含它的标签解析成 C# 类后,这个类的类名是什么。这里,我们已经触及到的 XAML 的本质。前面我们已经看到,示例代码的结构就是使用 XAML 语言直观地告诉我们,当然被设计的窗体是在一个 <Window> 里嵌套一个 <Grid> 。如果使用 C# 来完成同样的设计呢?显然,我们不可能去更改 Window 这个类,我们能做的是从 Window 类派生出一个类来(比如叫 WindowABC ),再为这个类添加一个 Grid 类型的字段,然后把这个字段在初始化的时候赋值给派生类的内容属性。代码看起来大概是这样:
  using System.Windows;
    using System.Windows.Controls;
    class WindowABC : Window
    {
        private Grid grid;
        public WindowABC()

        {
            grid = new Grid();
            this.Content = grid;
        }
    }

  最后,让我们回到最初的代码。你可能会问:在XAML里有x:Class="MyFirstWpfApplication.Window1",在Window1.xaml.cs里也声明了Window1这个类,难道它们不会冲突吗?仔细看看Window1.xaml.csWindow1类的声明就知道了——在声明时使用了partial这个关键字。使用partial关键字,可以把一个类分拆在多处定义,只要各部分代码不冲突即可。显然,由XAML解析器生成的Window1类在声明时也使用了partial关键字,这样,由XAML解析成的类和C#文件里定义的部分就合而为一了。正是由于这种partial机制,我们可以把类的逻辑代码留在.cs文件里、用C#语言来实现,而把那些与声明及布局UI元素相关的代码分离出去,实现UI与逻辑分离。并且,用于绘制UI的代码(比如声明控件类型的字段、设置它们的外观和布局等)也不必再使用C#语言——使用XAMLXAML编辑工具就能轻松搞定!


最后,

Title="MainWindow" Height="300" Width="300"
  的意思是设置MainWindow类(也可以说是Window类)的几个实例属性。这种语法称为使用标签的attribute设置对象的property”,碰巧,attributeproperty这两个词都被译为了属性,所以这句话就没法翻译了。除了使用attribute设置对象property的语法外,XAML还支持使用子元素方式设置元素属性的语法。下面这段代码与原代码是等价的:

    

<Window x:Class="HelloWPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Title>MainWindow</Window.Title>
    <Window.Height>300</Window.Height>
    <Window.Width>300</Window.Width>
    <Grid>
        
    </Grid>
</Window> 

    大家注意啦!<Window.Title>、<Window.Height>、<Window.Width>叫做“属性元素”,表示它虽然是一个子元素,但它是父元素的一个属性;而<Grid>则是一个普通元素,而非<Window>元素的属性——它们虽然都是<Window>的组成元素,但不是一个圈子里的(请跳转到上面,看看准备知识)。总有初学者问我:“反正Title也是Window的一个组成部分,能不能写成<Title>MainWindow</Title>啊?”幽默点讲,XAML解析器没那么聪明;地道的说法是,从物理上讲,并没有<Title>这个UI元素;从XAML语法上讲,这样会造成语义上的含混、远不及<Window.Title>来得清晰。

    啰嗦一句:当对象的property用一个简单的string就能描述清楚时,完全没必要使用子元素式语法小题大作。当对象的属性是一个复杂的对象时(你想用attribute式语法都办不到),再使用子元素式语法。到此,一个最简单的WPF程序(的XAML部分)就算分析完了。


参考:http://blog.csdn.net/fantasiax/article/details/3669790

以及上面作者的一系列文章。












猜你喜欢

转载自blog.csdn.net/godqiao/article/details/79422756