• WPF Unleashed Chapter 2:XAML Demystified 翻译(第三部分)


        
                        
                 上一篇WPF Unleashed Chapter 2:XAML Demystified 翻译(第二部分)
                声明: 本译文仅供学习讨论,禁止用于商业用途,否则后果自负



       编译:XAML与程序代码结合(Compilation: Mixing XAML with Procedural Code)   
            wpf应用程序完全可以用纯程序代码来实现。而XAML的数据绑定(第九章将介绍)和触发器(第三章将介绍)特性也使得我们能够用纯XAML代码创建简单的应用程序,并且这种松散结构的XAML文件(loose XAML pages)可以被IE来浏览。不过在实际情况中大多数程序都是由两者一起构建的。本节的开始部分将介绍XAML与程序代码结合的两种方法,后面将介绍XMAL命名空间内的关键字,这些关键字使XAML与程序代码能够和好的集成在一起。   
     
         运行时装载 解析XAML(Loading and Parsing XAML at Run-Time) 
         WPF的运行时XAML解析器暴露给我们两个位于System.Windows.Markup命名空间里的类:XamlReader和XamlWriter类。这两个API非常简单。XamlReader类包含一个静态方法Load,该方法包含3个重载版本;XamlWriter类有一个静态方法Save,该方法有5个重载版本。有了这两个类,任何.NET语言使用XAML了。   
      
        XamlReader   
        XamlReader的Load方法有许多重载,用于解析XAML,生成.NET中的对象,并返回根元素的实例。假设当前目录下有名为MyWindow.xaml的文件,该文件的根元素是一个Window对象(第七章的Structing and Deploying an Application将会解释),我们可以用下面的代码来获得这个Window对象:
             Window window = null;   
              using (FileStream fs =new FileStream(“MyWindow.xaml”, FileMode.Open, FileAccess.Read))   
              {   
                    // Get the root element, which we know is a Window   
                    window = (Window)XamlReader.Load(fs);   
              }
      
        
           上面的代码中,XamlReader.Load方法使用FileStream的实例作为其输入参数。方法执行完后,Xaml文件中对象已经装载到了内存中,之后XAML文件就没有用处了,可以将它对应的文件流关闭文:当代码执行到using括号的外围后,FileStream会自动关闭。之前说过,XamlReader的Load方法有多种重载,所以我们可以将任意一个Stream对象(或者System.Xml.XmlReader对象)作为Load的输入参数,用起来非常灵活。   
         
       Tip   

      
        XamlReader类也定义了异步方法,可以异步解析XAML中的内容。我们可以在XamlReader对象的实例里调用它们。如果在读取一个大文件时要保持用户UI的响应性,就可以使用异步读取的方法。和异步读取方法匹配的还有一个CancelAsync方法,用于停止读取操作。XamlReader还定义了LoadCompleted事件,在读取完成后会触发该事件 

           
           我们已经获得了根级元素的实例,现在可以通过适当的内容属性或集合属性(关于内容属性和集合部分请参见上一篇译文)得到对象的子元素。下面的代码演示了如何使用这些属性获取子元素(假设Window对象包含了一个StackPanel类型的子元素,并且这个StackPanel对象还包含15个Button按钮)    
              Window window = null;    
              using (FileStream fs =new FileStream(“MyWindow.xaml”, FileMode.Open, FileAccess.Read))    
              {    
                    // Get the root element, which we know is a Window    
                    window = (Window)XamlReader.Load(fs);    
              }    
              // Grab the OK button by walking the children (with hard-coded knowledge!)    
              StackPanel panel = (StackPanel)window.Content;    
              Button okButton = (Button)panel.Children[4];
      
     
           引用到Button后,就可以对其进行操作了:添加属性(在xaml中难以实现的逻辑),事件处理 或者添加xaml中不能实现的行为。   
      
          我们可以编写代码去操作这些元素。例如查找所有内容为ok字符串的Button元素,但即使是这么简单的任务也都需要大量工作。另外,如果你要为Button元素添加一个图形内容,应该如何确定这些Button呢? 非常幸运,我们可以XAML为元素命名,这样可以通过名称来找到相应的元素。     
      
            命名xaml元素(Naming XAML Elements)    
            XAML的命名空间包含一个"Name"关键字,该关键字用来给元素命名。对于刚才ok Button,您可以把Name关键字应用到Button的声明中:   
            <Button x:Name=”okButton”>OK</Button>    
         
         有了这个功能,就可以使用Window.FindName方法来检索元素了:
          Window window = null;   
          using (FileStream fs =new FileStream(“MyWindow.xaml”, FileMode.Open, FileAccess.Read))   
          {   
                // Get the root element, which we know is a Window   
                window = (Window)XamlReader.Load(fs);   
          }   
          // Grab the OK button, knowing only its name   
          Button okButton = (Button)window.FindName(“okButton”);   
     
        不只是Window对象可以使用FindName方法,FrameworkElement,FrameworkContentElement和许多WPF中重要的基类都包含FindName方法   
      
       DIGGING DEEPER   

                                             Naming Elements Without x:Name  
      
         x:Name语法用于为元素命名元素,还有一些类还定义了自己的属性,也用于为元素命名(那些被System.Windows.Markup.RuntimeNamePropertyAttribute修饰的类)。例如,FrameworkElement和FrameworkContentElement就被RuntimeNameProperty("Name")属性修饰。这两个类包含Name属性,我们在为这类元素命名的时候可以使用这个属性了。实际上,我们可以选择任意一种命名方式,但同一时刻只能选择一种!有两种命名的方式多少让人有些困惑,但好处在于:有了Name属性以后我们就可以在程序代码中方便地使用了。而且同一时刻只能选择一种总比同一时刻能选择两种要好,否则会更加困惑。

     
         编译XAML(Compiling XAML)   
          在运行时装载和解析XAML对于动态换肤(dynamic skinning )非常有意义(第十章Styles, Templates, Skins, and Themes将介绍)。否则.NET就没必要支持XAML了。多数WPF的工程都会使用MSBuild 或VisualStudio完成XAML的编译。XAML的编译需要完成三间事情:1将XAM转化成的二进制形式 2将转换的内容以二进制资源的形式嵌入到将要编译的程序集 3执行连接XAML与程序代码的管道。本书编写时,C#和VB均支持XAML的编译。   
      
          如果您不关心程序代码与XAML文件是如何合成的,那您唯一要做的就是将XAML文件添加到在Visual Studio工程中,然后将它的“生成操作”(Build Action)属性设置为Page即可。将XAML编译并将其和程序代码合成的第一步是在XAML文件的根元素中添加一个子类,这部操作需要用到XAML命名空间内的Class关键字。代码如下:   
          <Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”   
                xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”   
                x:Class=”MyNamespace.MyWindow”>   
                …   
          </Window>   
      
        然后将子类定义到一个单独的源文件中(但同属于一个工程),然后添加您需要的成员:   
              namespace MyNamespace   
          {   
                partial class MyWindow : Window   
                {   
                      public MyWindow   
                      {   
                            // Necessary to call in order to load XAML-defined content!   
                            InitializeComponent();   
                                  …   
                      }   
                Any other members can go here…   
                }   
          }   
          
        这就是后台文件(code-behind),可以在这里定义XAML代码中涉及的事件处理函数(例如Button的Click)   
      
        MyWindow类中的partial关键字至关重要,partial说明这个类的实现是分散到不同文件里完成的。如果.net不支持partial关键字,那么就要在XAML文件中使用Subclass关键字了:   
          <Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”   
                xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”   
                x:Class=”MyNamespace.MyWindow” x:Subclass=”MyNamespace.MyWindow2”>   
                …   
          </Window>   
      
        在XAML文件里定义了一个子类(例子中的MyWindow2),而MyWindow作为它的基类。这样就可以以继承的方式来代替了partial的功能。  
       
        当我们创建一个c#/vb的WPF工程,或者在添加新项(Add New Item)里选择添加wpf项目,Visual Studio会自动创建XAML文件及其相应后台的文件。XAML文件的根级包含x:Class字符串,而后台文件里包含了局部类的定义.   
      
        如果您是个MSBuild用户并且想了解为什么工程允许后代码,可以以记事本的方式打开任意一个WPF的工程文件后可以得到如下的信息:   
          <ItemGroup>   
                <Page Include=”MyWindow.xaml”/>   
          </ItemGroup>   
          <ItemGroup>   
                <Compile Include=”MyWindow.xaml.cs”>   
                      <DependentUpon>MyWindow.xaml</DependentUpon>   
                      <SubType>Code</SubType>   
                </Compile>   
          </ItemGroup>   
           
        在处理这样一个工程时,build系统在处理MyWindow.xaml文件时会生成如下文件:   
              1  BAML文件(MyWindow.baml),默认情况下以2二进制资源的形式嵌入到程序集内   
              2  C#文件(MyWindow.g.cs),将其他源文件包含到程序集内。
     
    BAML   
          BAML含义为二进制应用标记语言(Binary Application Markup Language),BAML是XAML被解析,标记化后的二进制形式.虽然XAML可以由程序代码来实现,但是XAML转换成BAML的过程中不会产生程序代码。所以BAML和MSIL不同,它只是一种被压缩的声明格式,在装载(load)和解析方面要优于XAML,并且文件体积也比XAML小。BAML本身没有对外公布,所以以后版本中的BAML的和现在可能会有所不同。   
        
      T I P

             x:Class标记只有在XAML被编译时会用到,但在XAML编译时没有x:Class也不会有任何问题。没有x:Class意味这XAML文件不包含对应的后台文件,所以也不能在XAML中使用那些程序代码才有的特性

      
        
    Generated Source Code   
          有些代码并不是在XAML编译过程中产生的,这些实际上是一种“黏合代码”(glue code)。这些代码会在装载和解析松散XAML文件。这些代码包含在后缀为.g.cs(或.g.vb)的文件中。单词g表示generated  
          每个.g文件都包含一个局部类,局部类的类就是在x:Class关键字中涉及的那个类。该局部类包含许多私有字段(原文为: This partial class contains afield (private by default) for every named element in the XAML file。可能是我的理解有误,我发现在.g文件里使用了internal关键字来修饰字段而非private。如果哪位兄台知道原因,请告诉我,谢谢),每个字段都对应这XAML文件中的一个命名的元素,这些元素的名称就是字段的名称。局部类内还包括InitializeComponent方法,该方法需要完成许多工作:装载BAML资源,将类内的字段对应到XAML中声明的元素,注册事件(如果XAML中指定了事件处理)(实际上注册事件这部操作并没有在InitializeComponent中完成,而是在System.Windows.Markup.IComponentConnector.Connect方法里完成的,笔者注)   
      
          实际上这些.g文件中的局部类是都是在后台文件类的一部分。对于BAML文件的存在,装载和解析您都不需要关心。您只需要像使用类成员一样使用那些被命名的元素即可,build系统会将他们联系到一起。唯一要记住的是:在构造函数中调用InitializeComponent方法。   
      
    DIGGING DEEPER

                                                    There Once Was a CAML…    
      
          在WPF正式发布之前的早期版本中,WPF可以将XAML编译成BAML或MSIL。这种MSIL叫做CAML,全称是Compiled Application MarkupLanguage. BAML在存储空间上比较有优势,而CAML则是在执行速度上略胜一筹。但最终WPF的开发团段决定使用BAML而抛弃了CAML。因为BAML有几个优势:相对于CAML更加安全,体积更小。

      

    WARNING

                            Don’t forget to call InitializeComponent in the constructor of your code-behind class!    

          如果没有调用InitializeComponent方法,就无法使用XAML任何元素(应为相应的BAML没有加载),XAML中的命名元素也都会变成空值。

      
    DIGGING DEEPER

                                                     Procedural Code Inside XAML!    
      
          实际上我们可以将后台的程序代码内嵌到XAML中(和ASP.NET类似),这是利用了XAML命名空间的Code关键字:   
          <Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”   
                xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”>   
                <Button Click=”button_Click”>OK</Button>   
                      <x:Code><![CDATA[   
                            void button_Click(object sender, RoutedEventArgs e)   
                                  {   
                                        this.Close();   
                                  }   
                      ]]></x:Code>   
          </Window>    
      
         XAML文件被编译后,X:Code元素中的内容会被添加到.g.cs文件。我们不一定需要将代码包裹在<![CDATA[…]]>标记内,但使用了<![CDATA[…]]>可以避免&lt,&amp的使用。xml解析器不会解析CDATA中的内容,代价就是在CDATA中不能使用]]标记。虽然WPF提供了这个功能,但还是不建议在xaml中添加程序代码。那样会破坏UI与逻辑分离这个特性并且松散xaml也不支持这个功能;Visual Studio也不会为里面的关键字添加关键字应用的颜色
        
    FAQ

                                    Can BAML be decompiled back into XAML?    
      
      当然可以。.NET中类的实例可以被序列化成xaml。所以第一步就是取得实例。可以用System.Windows.Application.LoadComponent方法来完成:    
        System.Uri uri = new System.Uri(“MyWindow.xaml”, System.UriKind.Relative);    
        Window window = (Window)Application.LoadComponent(uri);
        
      
            LoadComponent方法与之前使用FileStream读取xaml文件的不同之处在于使用了Uniform Resource Identifier(URI)。得到URI后,LoadComponent会自动从资源中取得BAML(利用MSBuild得到其原始的xaml文件)。实际上Visual Studio自动产生的InitializeComponent方法就就调用Application.LoadComponent方法得到BAML.第八章将介绍使用URI得到资源的一些知识。      
      
       当您得到了根元素的实例后,就可以使用System.Windows.Markup.XamlWriter类获得XAML的结构了。XamlWriter类的Save方法共有5个重载。最简单的就是“接收对象实例然后返回字符串”的重载了:   
        string xaml = XamlWriter.Save(window);   
      
        BAML很容易被“破解”。在这一点上它和那些运行在本地或者会将UI呈现到本地的软件没什么区别(例如,您可以很轻松地获得一个网页中的html,javascript和css)

      
    XAML关键字(XAML Keywords)   
            XAML语言命名空间(http://schemas.microsoft.com/winfx/2006/xaml)定义了许多关键字。这些关键字大多数是永安里管理XAML中元素与程序代码的xxxx,也有一小部分有其他用途。那写有其他用途的关键字您之前已经见识(Key,Name,Class,Subclass),不过表2.1也包含这些关键字。它们都以"x"字符串作为前缀出现,这也符合他们在XAML中的形式。
            表2.2中列举了XAML命名空间内的扩展标记,为了避免和表2.1中的关键字混淆,我们将它单独列举出来。在使用扩展标记是一般都会将其后缀省略,所以表2.2中也没有将后缀打印出来。   
        
     
  • 相关阅读:
    动态规划之最大子段和问题
    hdu1203 I NEED A OFFER!---概率DP(01背包)
    hdu1087 Super Jumping! Jumping! Jumping!---基础DP---递增子序列最大和
    hdu2062 Subset sequence----递推
    java线程基础巩固---通过实验分析This锁和Class锁的存在
    java8学习之BiFunction函数式接口实例演示&Predicate函数式接口详解
    java8学习之Function与BiFunction函数式接口详解
    java8学习之Lambda表达式继续探讨&Function接口详解
    java8学习之Lambda表达式深入与流初步
    java8学习之深入函数式接口与方法引用
  • 原文地址:https://www.cnblogs.com/stswordman/p/790552.html
Copyright © 2020-2023  润新知