zoukankan      html  css  js  c++  java
  • WPF,Silverlight与XAML读书笔记第五 XAML与传统过程式代码的关系

     

    说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。

      

             WPF程序中很少单纯用XAML代码或单纯用传统过程式代码来完成,大多数程序混合使用了这两种代码。本节总结了在程序中控制XAML的方法:

    1. 在运行时加载和解析XAML

             WPF中有两个类是作为XAML解析器,位于System.Windows.Markup命名空间中:

    1. XamlReader,包含了一些对静态Load方法的重载;该方法解析XAML创建合适的.NET对象,返回一个根元素类型的实例。

    2. XamlWriter,包含了5个对静态Save方法的重载;Save方法实现由一个根元素的实例得到其XAML的表示。如下代码传入一个window对象,返回一个包含XAML的字符串:

    string Xaml = XamlWriter.Save(window);

           通过这两个类你可以在运行时使用XAML。

    下面给出一个示例:

        以下代码加载当前目录下一个名为MyWindow.xaml的XAML文件,其包含一个window对象作为根节点。这段代码解析此XAML,获取window对象的引用并访问其子元素/子对象及属性。

    Window window = null;
    using (FileStream fs = new FileStream("MyWindow.xaml", FileMode.Open, FileAccess.Read))
    {
        //获取根元素,一个Window对象
        window = (Window)XamlReader.Load(fs);
    }
    //遍历得到子元素中OK按钮
    StackPanel panel = (StackPanel)window.Content;
    Button okButton = (Button)panel.Children[4];

           当然,仅仅使用加载XAML文件的方式进行上面展示的访问元素等操作就小题大做了,使用这种方式多为完成一些XAML代码难以或无法完成的操作,如添加事件处理程序或调用方法。通过给元素命名,可以更方便的在代码中操作加载的XAML的元素。这归功于WPF的Window元素提供了FindName方法来搜索其子元素并返回找到的元素的实例。

           这种动态加载的方式另一个作用是动态使用XSLT建立XML(XAML),交由WPF加载,这是一种动态建立用户界面的方式(数据绑定是更好的方式)。

           如为Button添加x:Name="okButton"属性后,可以使用如下代码替换前文代码:

    Button okButton = (Button)window.FindName("okButton");

    WPF类中也有其它像FrameworkElement, FrameworkContentElement等类也提供了FindName方法。

           另外有一部分WPF类,如FrameworkElementFrameworkContentElement,标记了位于System.Windows.Markup下的RuntimeNamePropertyAttribute。此Attribute可以将指定给其的参数为名称的属性作为元素的名称。由于FrameworkElementFrameworkContentElement被标记为[RuntimeNameProperty("Name")],所以FrameworkElementFrameworkContentElement可以通过Name属性来设置名称,而无需使用x:Name语法。类可以使用任意一种机制来设置名称,但不能同时使用两者。

           另外,XamlReader也定义了LoadAsync实例方法用于异步加载与解析XAML内容。这通常用于加载较大的文件或位于网络上的一个位置。与其配合使用的有CancelAsync方法用于停止处理,LoadCompleted事件在加载完成时通知程序。

     

    编译XAML

    XAML编译有三件事:

    1. 将一个XAML文件转换为一种特殊的二进制文件。

    2. 将转好的内容作为二进制资源嵌入到正在被创建的程序集。

    3. 执行链接操作,将XAML与过程式代码自动连接起来。

    为了编译一个XAML并让其可以与过程式代码交互,就像VSWPF模板创建出的.xaml文件与.xaml.cs文件之间可以交互一样,需要给XAML文件中的根元素指定一个子类,这可以用XAML的关键字x:Class来设置(正如第一篇所介绍)。

    示例(注意粗体部分) 

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

     过程代码文件:

    namespace WpfApplication1
    {
        public partial class Window1 : Window
        {
            public   Window1()
            {
                InitializeComponent();
            }
        }
    }

            以上代码中partial关键字可以将一个两部分分别位于XAMLC#代码中的类合在一起。在用户添加XAML时,VS生成两个文件并会自动完成添加x:Class的过程。这种方式与ASP.NET WebForm模型中将.aspx页面与.aspx.cs隐藏代码分开的方式极其相似。

    在不支持部分类特性(partial关键字)的语言中,需要使用ASP.NET1.1(那时的C#1.0版不支持部分类)中处理此问题的方法。即在XAML中再声明一个子类,并将代码文件中的代码定义在这个子类中。

    示例: 

    <Window x:Class="WpfApplication1.Window1" x:SubClass="WpfApplication1.Window1Child"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    </Window>

    代码中x:SubClass关键字定义的Window1Child作为Window1的子类,表现在过程代码中如下(使用支持部分类的C#,仅供演示):

    namespace WpfApplication1
    {
        public partial class Window1Child   : Window1
        {
            public   Window1Child()
            {
                InitializeComponent();
            }
        }
    }

    这样通过继承模拟了把代码分散在两个文件中。

            

    通过以上分析可以看出,在代码隐藏文件中调用InitializeComponent很关键。这个函数主要完成:建立由XAML中元素所设定的对象树状列表;需要为属性赋值;需要为命名元素生成字段;绑定所有特定的事件处理。当需要为了传递参数等目的重载构造函数后,需要注意调用InitializeComponent方法或调用无参的构造函数。

     

    .xaml.xaml.cs文件被编译的过程中:

    1. XAML被生成为一个BAML文件,作为二进制资源嵌入到程序集中。BAMLBinary Application Markup Language的缩写,意为二进制应用程序标记语言。在XAML生成BAML的过程中没有生成任何过程代码,只是进行了一种格式的压缩,使其体积比XAML要小。这是为了减少运行时的消耗。(被添加到VS的纯XAML(无x:Class)被编译为BAML,并由Parser.LoadBaml方法来加载,生成对应于源XAML的对象树状显示。)

    2. 隐藏代码文件.xaml.cs在未被编译的情况下以粘贴方式放入.g.cs文件中(g含义为generated),每个.g.cs文件都有一个以XAML文件中x:Class关键字定义的类名为名称的部分类,其中还存放了一些以XAML中命名元素为名的私有成员,另外在.xaml.cs中看不到定义的InitialCompenent方法的定义也存在于.g.cs文件中。在最终生成程序时.g.cs文件会被编译到程序集中。

           当然如果一个XAML不需要使用过程式代码,则不需要使用x:Class指定类,只需要将其添加到项目中,其会被编译成高效的BAML文件并被使用。

             另外,XAML还支持代码嵌入,这使用XAML命名空间的Code关键字实现:

    示例代码:

        <x:Code>

            <![CDATA[

            void button_Click(object sender,   RoutedEventArgs e)

            {

                this.Close();

            }

        ]]>

        </x:Code>

    其中的过程代码在编译时被放到.g.cs文件中。

             另外,把代码放置于<![CDATA[…]]>嵌入不是必需的,其可以避免使用'&lt'替换'; ',或者使用'&amp'替换'&'CDATA允许非XML的文本逐字的插入,因为XML解析器会忽略CDATA节。使用<![CDATA[…]]>需要注意避免在代码中使用']',这个符号会终结CDATA节。这种“代码嵌入”特性尽量不要使用,其在VS中没有语法高亮与智能感知的支持。

     

    BAML的反编译

    可以在过程代码中使用如下代码来加载BAMLVS也是使用下面函数的另一个重载供InitializeComponent方法来加载BAML

    示例代码:

    System.Uri uri = new   System.Uri("MyWindow.xaml",   System.UriKind.Relative);
    Window window = (Window)Application.LoadComponent(uri);

    在参数uri实例构造函数中指定的是一个xaml文件的名称,然后LoadComponent会找到对应的此xaml文件编译后作为嵌入资源的BAML

     

             如果XAML中没有类定义(x:Class关键字),有两种方式可以以树状方式展示,既可以在运行时编译XAML,也可以调用BAMLXAML预编译成二进制格式后导入,无论怎样,我们都可使用System.Windows.Serialization命名空间中的Parse类来建立对象的树状表示。这是解释XAML的方式,如果XAML中含有x:Class,则必须用XAML编译器编译XAML

     

    参考:

    WPF揭秘》

     

     

  • 相关阅读:
    java多线程为什么要用while而不是if
    爆竹声响,新人入场
    java多线程状态转换
    java接口的方法默认都是public abstract类型
    java中的静态static关键字
    我的第一个python爬虫程序
    python爬取某些网站出错的解决办法
    spring
    hibernate中文乱码问题
    eclipse(Version: Neon Release (4.6.0))安装hibernate tools
  • 原文地址:https://www.cnblogs.com/lsxqw2004/p/4546717.html
Copyright © 2011-2022 走看看