• WPF 基础到企业应用系列5——WPF千年轮回 续前缘


    一。摘要

           首先非常高兴这个系列能得到大家的关注和支持,前端时间身体状况不适,所以暂停了更新,对此表示非常抱歉,以后会逐渐加快进度。只是因为这是一个非常长的系列,我也想把它写好,所以以后也会慢慢来,在这个系列的过程中也会穿插发一些其它文章,比方Windows Azure、设计模式、WCF、Silverlight等,同一时候也会发一些自己的技术随感和心得。反正仅仅要自己写得开心且对大家有帮助即可。因为自己才疏学浅且是对这些技术的使用总结和心得体会。错误之处在所难免。怀着技术交流的心态,在这里发表出来,所以希望大家可以多多指点,这样在使一部分人受益的同一时候也能纠正我的错误观点,以便和各位共同提高。

           这篇课程主要是对上几次课程的回想和简单深化,所以没有讲什么比較新的概念。只是掌握好了这篇,对后面的非常多文章都有帮助,同一时候这一篇文章做Demo、构思、研究等也花费了不少时间。所以希望对大家有所帮助。

    二,本文提纲

    · 1.摘要

    · 2.本文提纲

    · 3.前篇回想

    · 4.Xaml基础

    · 5.脱离VS工具CSC编译WPF

    · 6.XamlReader与XamlWriter

    · 7.本文总结

    . 8.系列进度

    三,前篇回想

    在我们日常的开发中,软件企业的开发者通常会有两种类型的工作:

    1,一类是用户界面设计人员。他们关心的是软件和用户之间的交互。就是怎样让用户体验更好。

    2,还有一类是软件开发者。他们关心的是软件的架构设计、业务逻辑的处理和软件功能的实现。

    在BS中,用户界面设计人员使用HTML及其工具来设计界面,开发者使用Java。C#,VB或其它语言来实现当中的逻辑,HTML网页能够用到终于的产品中。

    在CS中,过去我们一直没有分开这两种不同性质的工作。

    用户界面设计人员通常和开发者使用不同的工具。当界面设计人员设计好用户界面时,他们的工作并没实用到终于的产品中,而仅仅是用来展现某种概念或工作流程。

            XAML实现了互联网应用程序和桌面应用程序的统一,界面设计人员能够使用XAML或基于XAML的工具(如微软的Design和 Blend) 来设计CS或BS应用程序的界面。程序开发者则能够在此基础上使用C#或VB.NET等来开发对应的功能。这样,界面设计人员的工作便自然过渡到终于产品中。

    在XAML中,用户界面用XML的元素或属性来表示。WPF引擎把XAML描写叙述的UI元素解释为对应的.NET对象。从而在桌面程序或Silverlight网页上创建对应的控件。例如以下图所看到的:

     

    2

    上面这副就是传统的WinForm开发模式。这两种人没有分离开来。所以在非常多企业里就形成了开发者既要做UI也要做程序的境界。

     

     

    1  

    上图就是如今的WPF和Silverlight程序的开发模式。这两类人能够分开来工作。他们都能够对Window1.xaml进行改动和载入,所以这样就使分工更专业了,因为大家专注于某一个方面,分工协作的同一时候,质量和效率也逐渐提高了。

     

    前几篇介绍了一些基础知识,那么这篇也简单的回想一下。以下第一幅图是WPF的运行顺序,第二副图是WPF的一个项目的构成。第三幅图是WPF所相应的IL代码(这些图处理得不好,还望各位见谅)。

    3

    WPF的运行顺序

     

    9

    WPF的一个项目的构成

     

    10

    WPF所相应的IL代码,通过Reflector查看

    四,Xaml基础

    这个部分要讲的东西就太多了。因为这篇文章篇幅有限,同一时候我认为用代码诠释能让大家能够更清晰地理解,所以就讲得任意一些,通过一个Demo介绍WPF对资源、类、控件的调用和处理,对Dictionary资源、Application资源、window资源以及控件资源的应用等。例如以下图所看到的(本篇全部代码在评论的第一条):

    4

    因为这些概念比較简单而且较多。假设所有写完,也得专门写一长篇。还好大家都喜欢看代码。所以我就不花费大的篇幅来讲它们,感兴趣或者对这些知识还有不清楚的朋友能够下载这个Demo进行查看或调试,我认为对刚開始学习的人非常有帮助。

    五。脱离VS工具CSC编译WPF

    为了更好的认识WPF的编译和运行过程,我们能够临时弃用我们熟悉的VS工具。选用记事本写例如以下的代码:

    using System;
    using System.Windows;
    namespace KnightsWarrior.HelloWorld
    {
        class HelloWorld
        {
            [STAThread]
            public static void Main()
            {
                Window win = new Window();
                win.Height = 300;
                win.Width = 400;
                win.Title = "Hello,KnightsWarrior!";
                win.Show();
    
                Application app = new Application();
                app.Run();
             }
         }
    }

    然后保存到D:HelloWorld.cs 这个位置,通过CMD或者VS cmomand Line中输入下面编译命令:

    csc.exe /out:D:HelloWorld.exe D:HelloWorld.cs /reference:"C:Program FilesReference AssembliesMicrosoftFrameworkv3.0presentationframework.dll"  /reference:"C:Program FilesReference AssembliesMicrosoftFrameworkv3.0windowsbase.dll"  /reference:"C:Program FilesReference AssembliesMicrosoftFrameworkv3.0presentationcore.dll"

    然后就能够手动编译成功了。

    6

    那么通过Reflector能够查看到它的IL代码,假设感兴趣的朋友也能够进行具体的分析。

    5

    假设对MSIL比較熟悉的朋友。也能够用记事本写相同功能的IL代码,因为没有对WPF窗口的IL做详细研究。所以用Console程序取代,等过一段时间再研究WPF控件的IL代码.

    .assembly extern mscorlib { auto }
    .assembly HelloApp {}
    .module HelloApp.exe
    
    .namespace HelloApp
    {
        .class public Program extends [mscorlib]System.Object
        {
            .method static private void Main(string[] args)
            {
                .entrypoint
                
                ldstr "Hello, KnightsWarrior!"
                call void [mscorlib]System.Console::WriteLine(string)
                ret
            }
        }
    }

    然后打开 Visual Studio Command Prompt。使用 ILASM 開始编译,

     11

    这样你就更能看清楚编译器背后的秘密,同一时候也能跟踪每一步运行的操作,同一时候对一些简单的内存泄露问题也比較easy察觉到。

    当然如今也有非常多工具能够跟踪这些问题,我这里仅仅是写一种思路。大家能够依据自己的爱好取舍。

    六,XamlReader与XamlWriter

    System.Windows.Markup 命名空间中提供了 XamlReader、XamlWriter 两个类型,同意我们手工操控 XAML和BAML 文件。

    XamlReader类除了定义Load的实时载入之外,也定义了异步方法。能够异步解析XAML中的内容。我们能够在XamlReader对象的实例里调用它们。

    假设在读取一个大文件时要保持用户UI的响应性,就能够使用异步读取的方法。

    和异步读取方法匹配的另一个CancelAsync方法,用于停止读取操作。

    XamlReader 还定义了LoadCompleted事件,在读取完毕后会触发该事件,那么我们就能够把读完后要做的事情都在这里进行处理。

    7

    XamlWriter 供一个静态 Save 方法(多次重载),该方法可用于以受限的 XAML 序列化方式,将所提供的执行时对象序列化为 XAML 标记。这句话似乎有点难懂。事实上简单的说就是把它序列化为我们须要的类型。

    8

    详细功能代码例如以下:

    通过XamlReader 动态构建并实例化一个Window

    //XamlReader  
    StringBuilder strXMAL = new StringBuilder("<Window ");
    strXMAL.Append("xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ");
    strXMAL.Append("xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" ");
    strXMAL.Append("Title="Window2" Height="600" Width="600">");
    strXMAL.Append("</Window>");
    var window = (Window)XamlReader.Parse(strXMAL.ToString());
    window.ShowDialog();

    同一时候我们还能够从文件流中读取并操作。

    //XamlReader  
    using  (var  stream = new  FileStream(@"Window2.xaml", FileMode.Open))
    {
        var  window2 = (Window)XamlReader.Load(stream);
    
        var  button = (Button)window2.FindName("btnTest");
        button.Click += (x, y) => MessageBox.Show("Knights Warrior");
    
        window2.ShowDialog();
    }

     

    Window2.xaml 的代码:

    <Window x:Class="XamlReaderWriterDemo.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" Height="300" Width="300">
        <Grid>
            <Button Height="23" Name="btnTest" Margin="98,72,105,0" VerticalAlignment="Top">Button</Button>
        </Grid>
    </Window>

    这里我们须要特别注意的是 XamlReader 加载的 XAML 代码不能包括不论什么类型(x:Class)以及事件代码(x:Code),也就是说要XAML自身的代码才受支持(这个也在WPF揭秘这本书讲到过)。

    那么我们能够用 XamlWriter 将一个编译的 BAML 还原成 XAML了,详细代码例如以下:

    //XamlWriter
    var  xaml = XamlWriter.Save(new  Window2());
    MessageBox.Show(xaml);

    输出的Message例如以下(为了效果更好看一些。我粘贴到了VS):

    <Window2 Title="Window2" Width="300" Height="300" xmlns="clr-namespace:XamlReaderWriterDemo;assembly=XamlReaderWriterDemo" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <av:Grid>
            <av:Button Name="btnTest" Height="23" Margin="98,72,105,0" VerticalAlignment="Top">Button</av:Button>
        </av:Grid>
    </Window2>

    XAML 的动态载入在使用动态换肤以及执行时载入等场景颇为实用。以后也会慢慢接触。

    因为使用XamlReader和XamlWriter有非常多限制,比方我想把一批Baml转化为Xaml,再比方我想指定Baml的路径。然后通过Load的方式加载,那么这些场景就无法通过XamlReader和XamlWriter完毕了,这个让我也做过不少的Demo,也跟踪了非常长时间的IL代码,在百思不得其解之后和周永恒Virus等讨论了一下,最后最终找到了一个方案,例如以下代码所看到的:

    public static class BamlWriter
     {
         public static void Save(object obj, Stream stream)
         {
             string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
             Directory.CreateDirectory(path);
    
             try
             {
                 string xamlFile = Path.Combine(path, "input.xaml");
                 string projFile = Path.Combine(path, "project.proj");
    
                 using (FileStream fs = File.Create(xamlFile))
                 {
                     XamlWriter.Save(obj, fs);
                 }
    
                 Engine engine = new Engine();
                 engine.BinPath = RuntimeEnvironment.GetRuntimeDirectory();
                 Project project = engine.CreateNewProject();
                 BuildPropertyGroup pgroup = project.AddNewPropertyGroup(false);
                 pgroup.AddNewProperty("AssemblyName", "temp");
                 pgroup.AddNewProperty("OutputType", "Library");
                 pgroup.AddNewProperty("IntermediateOutputPath", ".");
                 pgroup.AddNewProperty("MarkupCompilePass1DependsOn", "ResolveReferences");
    
                 BuildItemGroup igroup = project.AddNewItemGroup();
                 igroup.AddNewItem("Page", "input.xaml");
                 igroup.AddNewItem("Reference", "WindowsBase");
                 igroup.AddNewItem("Reference", "PresentationCore");
                 igroup.AddNewItem("Reference", "PresentationFramework");
    
                 project.AddNewImport(@"$(MSBuildBinPath)Microsoft.CSharp.targets", null);
                 project.AddNewImport(@"$(MSBuildBinPath)Microsoft.WinFX.targets", null);
                 project.FullFileName = projFile;
    
                 if (engine.BuildProject(project, "MarkupCompilePass1"))
                 {
                     byte[] buffer = new byte[1024];
                     using (FileStream fs = File.OpenRead(Path.Combine(path, "input.baml")))
                     {
                         int read = 0;
                         while (0 < (read = fs.Read(buffer, 0, buffer.Length)))
                         {
                             stream.Write(buffer, 0, read);
                         }
                     }
                 }
                 else
                 {
                     throw new System.Exception("Baml compilation failed.");
                 }
             }
             finally
             {
                 Directory.Delete(path, true);
             }
         }
     }
    
     public static class BamlReader
     {
         public static object Load(Stream stream)
         {
             ParserContext pc = new ParserContext();
             return typeof(XamlReader)
                 .GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static)
                 .Invoke(null, new object[] { stream, pc, null, false });
         }
     }

    上面的代码,大家能够试一下执行效果。或者有更好的方式也请告知。

     

    七,本文总结

            本篇主要对前几次的课程进了一些简单的回想。同一时候用一个比較全的Demo介绍了Xaml中引用各种控件和类等,另外对脱离VS工具CSC编译WPF以及XamlReader与XamlWriter 做了比較具体的介绍。下篇我们将进入WPF布局的世界进行漫游。争取和布局控件及应用来一个全接触!

           最后圣殿骑士 会尽心尽力写好这个系列,同一时候因为是自己对这些技术的使用总结和心得体会。错误之处在所难免,怀着技术交流的心态,在博客园51CTO发表出来,所以希望大家可以多多指点,这样在使一部分人受益的同一时候也能纠正我的错误观点,以便和各位共同提高,兴许文章敬请关注!

    本文全部代码下载:DemoWithXAML.zip

  • 相关阅读:
    codeforces C. Fixing Typos 解题报告
    codeforces B. The Fibonacci Segment 解题报告
    codeforces B. Color the Fence 解题报告
    codeforces B. Petya and Staircases 解题报告
    codeforces A. Sereja and Bottles 解题报告
    codeforces B. Levko and Permutation 解题报告
    codeforces B.Fence 解题报告
    tmp
    API 设计 POSIX File API
    分布式跟踪的一个流行标准是OpenTracing API,该标准的一个流行实现是Jaeger项目。
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/6782404.html
Copyright © 2020-2023  润新知