WPF中的XAML是如何转换成对象的?

2024-08-24 09:28
文章标签 对象 转换成 wpf xaml

本文主要是介绍WPF中的XAML是如何转换成对象的?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

起因

最近有遇到有小伙伴在实现TreeView不同层级使用不同数据模板时,遇到了一些问题。

经过查阅资料,我提供了两种解决方案。

第一种是使用TemplateSelector,这种方式可以根据ViewModel设置不同的数据模板。

第二种是根据数据动态创建数据模板。

这两种解决方案都是基于后台代码的,但这位小伙伴他执着于在XAML中实现。

我跟他说XAML的实现和后台代码实现是一样的,都会转换成对应的对象的,XAML相当于一种助记符,它内部其实还是.cs代码。但是我也拿不出证据来证明这个。

我记得以前在哪看到过,XAML最终都是会转换为.cs代码的,但我一下也想不起来了。

干脆就直接查一查WPF的内部实现好了,看看XAML是如何转换为对象的。

InitializeComponent

平常我们在WPF中创建一个窗口时,都会在构造函数里看到有个默认的函数InitializeComponent();

让我们来看一看它的内部

 1  /// <summary>2  /// InitializeComponent3  /// </summary>4  [System.Diagnostics.DebuggerNonUserCodeAttribute()]5  [System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]6  public void InitializeComponent() {7      if (_contentLoaded) {8          return;9      }
10      _contentLoaded = true;
11      System.Uri resourceLocater = new System.Uri("/WpfApp25;component/mainwindow.xaml", System.UriKind.Relative);
12      
13      #line 1 "..\..\MainWindow.xaml"
14      System.Windows.Application.LoadComponent(this, resourceLocater);
15      
16      #line default
17      #line hidden
18  }

可以看到最核心的一句是

1 System.Windows.Application.LoadComponent(this, resourceLocater);

这一句的作用是加载位于指定统一资源标识符的 XAML 文件 (URI) 。它会将Uri指定的XAML转换为Window对象,我们接着往下看。

Application启动WPF程序的过程

Application类是封装WPF应用程序的一个类,可以把它理解为一个壳,然后通过Application.Run()函数启动WPF应用程序并打开指定的窗口。

平常我们在创建WPF程序时,系统已经为我们定义好了一个Application类,在App.xaml中,并指定了StartupUri属性(指定StartupUri属性后不需要再调用Application.Run函数了,Application内部会将窗口创建出来并显示

1 <Application x:Class="WpfApp25.App"
2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4              xmlns:local="clr-namespace:WpfApp25"
5             StartupUri="MainWindow.xaml">
6     <Application.Resources>
7          
8     </Application.Resources>
9 </Application>

在Application类内部会调用DoStartup函数时,会将指定Uri的XAML转换为Window对象。

然后再设置这个窗口的Visibility为Visible,就将窗口显示了出来。

为了更清晰的了解整个过程,我们创建了一个startup类,并在里面添加了一个Main函数,同时将工程的启动对象设置我们自己创建的startup类。

这里我们手动创建了一个Application类对象,并通过Application类的LoadComponent函数将XAML加载并转换为Window对象,再通过Application.Run函数启动整个WPF程序

 1    public class startup2    {3        [STAThread]4        public static void Main(string[] args)5        {6            Application application = new Application();7            System.Uri resourceLocater = new System.Uri("/WpfApp25;component/MainWindow.xaml", System.UriKind.Relative);8            var obj  =  System.Windows.Application.LoadComponent(resourceLocater);
10            application.Run(obj as Window);
11        }
12    }

到这里我们应该很清晰的看到系统是通过Application.LoadComponent函数将XAML读取,并转换成相应的类。

Application.LoadComponent的内部

我们往下查找,可以看到关键的步骤是一个LoadBamlStreamWithSyncInfo函数,在前面部分代码中,将XAML转换成一个流,然后再通过这个函数读取流

 1 [SecurityCritical]2 [SecurityTreatAsSafe]3 internal static object LoadComponent(Uri resourceLocator, bool bSkipJournaledProperties)4 {5     Uri resolvedUri = BindUriHelper.GetResolvedUri(BaseUriHelper.PackAppBaseUri, resourceLocator);6     PackagePart resourceOrContentPart = GetResourceOrContentPart(resolvedUri);7     ContentType contentType = new ContentType(resourceOrContentPart.ContentType);8     Stream stream = resourceOrContentPart.GetStream();9     ParserContext parserContext = new ParserContext();
10     parserContext.BaseUri = resolvedUri;
11     parserContext.SkipJournaledProperties = bSkipJournaledProperties;
12     if (MimeTypeMapper.BamlMime.AreTypeAndSubTypeEqual(contentType))
13     {
14         return LoadBamlStreamWithSyncInfo(stream, parserContext);
15     }
16     if (MimeTypeMapper.XamlMime.AreTypeAndSubTypeEqual(contentType))
17     {
18         return XamlReader.Load(stream, parserContext);
19     }
20     throw new Exception(SR.Get("ContentTypeNotSupported", contentType.ToString()));
21 }

LoadBamlStreamWithSyncInfo的内部调用了XamlReader.LoadBaml函数

 1 internal static object LoadBamlStreamWithSyncInfo(Stream stream, ParserContext pc)2 {3     object obj = null;4     if (s_NestedBamlLoadInfo == null)5     {6         s_NestedBamlLoadInfo = new Stack<NestedBamlLoadInfo>();7     }8     NestedBamlLoadInfo item = new NestedBamlLoadInfo(pc.BaseUri, stream, pc.SkipJournaledProperties);9     s_NestedBamlLoadInfo.Push(item);
10     try
11     {
12         return XamlReader.LoadBaml(stream, pc, null, closeStream: true);
13     }
14     finally
15     {
16         s_NestedBamlLoadInfo.Pop();
17         if (s_NestedBamlLoadInfo.Count == 0)
18         {
19             s_NestedBamlLoadInfo = null;
20         }
21     }
22 }

XamlReader.LoadBaml的内部调用如下

这里有两个关键步骤

1、创建了一个System.Windows.Baml2006.Baml2006ReaderInternal对象,将前面的流通过参数传了进去

2、调用WpfXamlLoader.LoadBaml函数,将System.Windows.Baml2006.Baml2006ReaderInternal对象传进去。

 1 [SecurityCritical]2 [SecurityTreatAsSafe]3 internal static object LoadBaml(Stream stream, ParserContext parserContext, object parent, bool closeStream)4 {5     object obj = null;6     EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordPerf | EventTrace.Keyword.KeywordXamlBaml, EventTrace.Event.WClientParseBamlBegin, parserContext.BaseUri);7     if (TraceMarkup.IsEnabled)8     {9         TraceMarkup.Trace(TraceEventType.Start, TraceMarkup.Load);
10     }
11     try
12     {
13         IStreamInfo streamInfo = stream as IStreamInfo;
14         if (streamInfo != null)
15         {
16             parserContext.StreamCreatedAssembly = streamInfo.Assembly;
17         }
18         Baml2006ReaderSettings baml2006ReaderSettings = CreateBamlReaderSettings();
19         baml2006ReaderSettings.BaseUri = parserContext.BaseUri;
20         baml2006ReaderSettings.LocalAssembly = streamInfo.Assembly;
21         if (baml2006ReaderSettings.BaseUri == null || string.IsNullOrEmpty(baml2006ReaderSettings.BaseUri.ToString()))
22         {
23             baml2006ReaderSettings.BaseUri = BaseUriHelper.PackAppBaseUri;
24         }
25         Baml2006ReaderInternal xamlReader = new Baml2006ReaderInternal(stream, new Baml2006SchemaContext(baml2006ReaderSettings.LocalAssembly), baml2006ReaderSettings, parent);
26         Type type = null;
27         if (streamInfo.Assembly != null)
28         {
29             try
30             {
31                 type = XamlTypeMapper.GetInternalTypeHelperTypeFromAssembly(parserContext);
32             }
33             catch (Exception ex)
34             {
35                 if (CriticalExceptions.IsCriticalException(ex))
36                 {
37                     throw;
38                 }
39             }
40         }
41         if (type != null)
42         {
43             XamlAccessLevel xamlAccessLevel = XamlAccessLevel.AssemblyAccessTo(streamInfo.Assembly);
44             XamlLoadPermission xamlLoadPermission = new XamlLoadPermission(xamlAccessLevel);
45             xamlLoadPermission.Assert();
46             try
47             {
48                 obj = WpfXamlLoader.LoadBaml(xamlReader, parserContext.SkipJournaledProperties, parent, xamlAccessLevel, parserContext.BaseUri);
49             }
50             finally
51             {
52                 CodeAccessPermission.RevertAssert();
53             }
54         }
55         else
56         {
57             obj = WpfXamlLoader.LoadBaml(xamlReader, parserContext.SkipJournaledProperties, parent, null, parserContext.BaseUri);
58         }
59         if (obj is DependencyObject dependencyObject)
60         {
61             dependencyObject.SetValue(BaseUriHelper.BaseUriProperty, baml2006ReaderSettings.BaseUri);
62         }
63         if (obj is Application application)
64         {
65             application.ApplicationMarkupBaseUri = GetBaseUri(baml2006ReaderSettings.BaseUri);
66         }
67     }
68     finally
69     {
70         if (TraceMarkup.IsEnabled)
71         {
72             TraceMarkup.Trace(TraceEventType.Stop, TraceMarkup.Load, obj);
73         }
74         EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordPerf | EventTrace.Keyword.KeywordXamlBaml, EventTrace.Event.WClientParseBamlEnd, parserContext.BaseUri);
75         if (closeStream)
76         {
77             stream?.Close();
78         }
79     }
80     return obj;
81 }

WpfXamlLoad.LoadBaml函数的内部调用如下

这一步有两个关键步骤

1、创建一个System.Xaml.XamlObjectWriter对象

2、调用TransformNodes函数。在TransformNodes函数内部,将System.Windows.Baml2006.Baml2006ReaderInternal对象读取的内容进行转换,根据特定规则,将数据重新写入到System.Xaml.XamlObjectWriter对象中

 1 private static object Load(System.Xaml.XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, bool skipJournaledProperties, object rootObject, XamlObjectWriterSettings settings, Uri baseUri)2 {3     XamlObjectWriter xamlObjectWriter = null;4     MS.Internal.Xaml.Context.XamlContextStack<WpfXamlFrame> stack = new MS.Internal.Xaml.Context.XamlContextStack<WpfXamlFrame>(() => new WpfXamlFrame());5     int persistId = 1;6     7     xamlObjectWriter = ((writerFactory == null) ? new XamlObjectWriter(xamlReader.SchemaContext, settings) : writerFactory.GetXamlObjectWriter(settings));
 8     IXamlLineInfo xamlLineInfo = null;9     try
10     {
11         xamlLineInfo = xamlReader as IXamlLineInfo;
12         IXamlLineInfoConsumer xamlLineInfoConsumer = xamlObjectWriter;
13         bool shouldPassLineNumberInfo = false;
14         if (xamlLineInfo != null && xamlLineInfo.HasLineInfo && xamlLineInfoConsumer != null && xamlLineInfoConsumer.ShouldProvideLineInfo)
15         {
16             shouldPassLineNumberInfo = true;
17         }
18         IStyleConnector styleConnector = rootObject as IStyleConnector;
19         TransformNodes(xamlReader, xamlObjectWriter, onlyLoadOneNode: false, skipJournaledProperties, shouldPassLineNumberInfo, xamlLineInfo, xamlLineInfoConsumer, stack, styleConnector);
20         xamlObjectWriter.Close();
21         return xamlObjectWriter.Result;
22     }
23     catch (Exception ex)
24     {
25         if (CriticalExceptions.IsCriticalException(ex) || !XamlReader.ShouldReWrapException(ex, baseUri))
26         {
27             throw;
28         }
29         XamlReader.RewrapException(ex, xamlLineInfo, baseUri);
30         return null;
31     }
32 }

然后再获取System.Xaml.XamlObjectWriterResult属性,最终得到了MainWindow对象

System.Xaml.XamlObjectWriter的内部实现这里就没去深究了,有兴趣的小伙伴可以查阅相关资料进行了解。

最后我们来看一看TransformNodes函数的内部实现

  1 internal static void TransformNodes(System.Xaml.XamlReader xamlReader, XamlObjectWriter xamlWriter, bool onlyLoadOneNode, bool skipJournaledProperties, bool shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, MS.Internal.Xaml.Context.XamlContextStack<WpfXamlFrame> stack, IStyleConnector styleConnector)2 {3     while (xamlReader.Read())4     {5         if (shouldPassLineNumberInfo && xamlLineInfo.LineNumber != 0)6         {7             xamlLineInfoConsumer.SetLineInfo(xamlLineInfo.LineNumber, xamlLineInfo.LinePosition);8         }9         switch (xamlReader.NodeType)10         {11         case System.Xaml.XamlNodeType.NamespaceDeclaration:12             xamlWriter.WriteNode(xamlReader);13             if (stack.Depth == 0 || stack.CurrentFrame.Type != null)14             {15                 stack.PushScope();16                 for (WpfXamlFrame wpfXamlFrame = stack.CurrentFrame; wpfXamlFrame != null; wpfXamlFrame = (WpfXamlFrame)wpfXamlFrame.Previous)17                 {18                     if (wpfXamlFrame.XmlnsDictionary != null)19                     {20                         stack.CurrentFrame.XmlnsDictionary = new XmlnsDictionary(wpfXamlFrame.XmlnsDictionary);21                         break;22                     }23                 }24                 if (stack.CurrentFrame.XmlnsDictionary == null)25                 {26                     stack.CurrentFrame.XmlnsDictionary = new XmlnsDictionary();27                 }28             }29             stack.CurrentFrame.XmlnsDictionary.Add(xamlReader.Namespace.Prefix, xamlReader.Namespace.Namespace);30             break;31         case System.Xaml.XamlNodeType.StartObject:32             WriteStartObject(xamlReader, xamlWriter, stack);33             break;34         case System.Xaml.XamlNodeType.GetObject:35             xamlWriter.WriteNode(xamlReader);36             if (stack.CurrentFrame.Type != null)37             {38                 stack.PushScope();39             }40             stack.CurrentFrame.Type = stack.PreviousFrame.Property.Type;41             break;42         case System.Xaml.XamlNodeType.EndObject:43             xamlWriter.WriteNode(xamlReader);44             if (stack.CurrentFrame.FreezeFreezable && xamlWriter.Result is Freezable freezable && freezable.CanFreeze)45             {46                 freezable.Freeze();47             }48             if (xamlWriter.Result is DependencyObject dependencyObject && stack.CurrentFrame.XmlSpace.HasValue)49             {50                 XmlAttributeProperties.SetXmlSpace(dependencyObject, stack.CurrentFrame.XmlSpace.Value ? "default" : "preserve");51             }52             stack.PopScope();53             break;54         case System.Xaml.XamlNodeType.StartMember:55         {56             if ((!xamlReader.Member.IsDirective || !(xamlReader.Member == XamlReaderHelper.Freeze)) && xamlReader.Member != XmlSpace.Value && xamlReader.Member != XamlLanguage.Space)57             {58                 xamlWriter.WriteNode(xamlReader);59             }60             stack.CurrentFrame.Property = xamlReader.Member;61             if (!skipJournaledProperties || stack.CurrentFrame.Property.IsDirective)62             {63                 break;64             }65             WpfXamlMember wpfXamlMember = stack.CurrentFrame.Property as WpfXamlMember;66             if (!(wpfXamlMember != null))67             {68                 break;69             }70             DependencyProperty dependencyProperty = wpfXamlMember.DependencyProperty;71             if (dependencyProperty == null || !(dependencyProperty.GetMetadata(stack.CurrentFrame.Type.UnderlyingType) is FrameworkPropertyMetadata frameworkPropertyMetadata) || !frameworkPropertyMetadata.Journal)72             {73                 break;74             }75             int num = 1;76             while (xamlReader.Read())77             {78                 switch (xamlReader.NodeType)79                 {80                 case System.Xaml.XamlNodeType.StartMember:81                     num++;82                     break;83                 case System.Xaml.XamlNodeType.StartObject:84                 {85                     XamlType type = xamlReader.Type;86                     XamlType xamlType = type.SchemaContext.GetXamlType(typeof(BindingBase));87                     XamlType xamlType2 = type.SchemaContext.GetXamlType(typeof(DynamicResourceExtension));88                     if (num == 1 && (type.CanAssignTo(xamlType) || type.CanAssignTo(xamlType2)))89                     {90                         num = 0;91                         WriteStartObject(xamlReader, xamlWriter, stack);92                     }93                     break;94                 }95                 case System.Xaml.XamlNodeType.EndMember:96                     num--;97                     if (num == 0)98                     {99                         xamlWriter.WriteNode(xamlReader);
100                         stack.CurrentFrame.Property = null;
101                     }
102                     break;
103                 case System.Xaml.XamlNodeType.Value:
104                     if (xamlReader.Value is DynamicResourceExtension)
105                     {
106                         WriteValue(xamlReader, xamlWriter, stack, styleConnector);
107                     }
108                     break;
109                 }
110                 if (num == 0)
111                 {
112                     break;
113                 }
114             }
115             break;
116         }
117         case System.Xaml.XamlNodeType.EndMember:
118         {
119             WpfXamlFrame currentFrame = stack.CurrentFrame;
120             XamlMember property = currentFrame.Property;
121             if ((!property.IsDirective || !(property == XamlReaderHelper.Freeze)) && property != XmlSpace.Value && property != XamlLanguage.Space)
122             {
123                 xamlWriter.WriteNode(xamlReader);
124             }
125             currentFrame.Property = null;
126             break;
127         }
128         case System.Xaml.XamlNodeType.Value:
129             WriteValue(xamlReader, xamlWriter, stack, styleConnector);
130             break;
131         default:
132             xamlWriter.WriteNode(xamlReader);
133             break;
134         }
135         if (onlyLoadOneNode)
136         {
137             break;
138         }
139     }
140 }

参考资料:

https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Application.cs,5374798b91523184

这篇关于WPF中的XAML是如何转换成对象的?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102084

相关文章

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

多重背包转换成0-1背包

http://acm.hdu.edu.cn/showproblem.php?pid=2191 多重背包特点: 一种物品有C个(既不是固定的1个,也不是无数个) 优化的方法: 运用神奇的二进制,进行物品拆分,转化成01背包 物品拆分,把13个相同的物品分成4组(1,2,4,6) 用这4组可以组成任意一个1~13之间的数! 原理:一个数总可以用2^

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

Prompt - 将图片的表格转换成Markdown

Prompt - 将图片的表格转换成Markdown 0. 引言1. 提示词2. 原始版本 0. 引言 最近尝试将图片中的表格转换成Markdown格式,需要不断条件和优化提示词。记录一下调整好的提示词,以后在继续优化迭代。 1. 提示词 英文版本: You are an AI assistant tasked with extracting the content of

HTML5自定义属性对象Dataset

原文转自HTML5自定义属性对象Dataset简介 一、html5 自定义属性介绍 之前翻译的“你必须知道的28个HTML5特征、窍门和技术”一文中对于HTML5中自定义合法属性data-已经做过些介绍,就是在HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们要在一个文字按钮上存放相对应的id: <a href="javascript:" d

PHP7扩展开发之对象方式使用lib库

前言 上一篇文章,我们使用的是函数方式调用lib库。这篇文章我们将使用对象的方式调用lib库。调用代码如下: <?php $hello = new hello(); $result = $hello->get(); var_dump($result); ?> 我们将在扩展中实现hello类。hello类中将依赖lib库。 代码 基础代码 这个扩展,我们将在say扩展上增加相关代码。sa