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

相关文章

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

python dict转换成json格式的实现

《pythondict转换成json格式的实现》本文主要介绍了pythondict转换成json格式的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下... 一开始你变成字典格式data = [ { 'a' : 1, 'b' : 2, 'c编程' : 3,

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

Java实现将byte[]转换为File对象

《Java实现将byte[]转换为File对象》这篇文章将通过一个简单的例子为大家演示Java如何实现byte[]转换为File对象,并将其上传到外部服务器,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言1. 问题背景2. 环境准备3. 实现步骤3.1 从 URL 获取图片字节数据3.2 将字节数组

Javascript访问Promise对象返回值的操作方法

《Javascript访问Promise对象返回值的操作方法》这篇文章介绍了如何在JavaScript中使用Promise对象来处理异步操作,通过使用fetch()方法和Promise对象,我们可以从... 目录在Javascript中,什么是Promise1- then() 链式操作2- 在之后的代码中使

MyBatis的配置对象Configuration作用及说明

《MyBatis的配置对象Configuration作用及说明》MyBatis的Configuration对象是MyBatis的核心配置对象,它包含了MyBatis运行时所需的几乎所有配置信息,这个对... 目录MyBATis配置对象Configuration作用Configuration 对象的主要作用C

SpringBoot实现导出复杂对象到Excel文件

《SpringBoot实现导出复杂对象到Excel文件》这篇文章主要为大家详细介绍了如何使用Hutool和EasyExcel两种方式来实现在SpringBoot项目中导出复杂对象到Excel文件,需要... 在Spring Boot项目中导出复杂对象到Excel文件,可以利用Hutool或EasyExcel

Springboot控制反转与Bean对象的方法

《Springboot控制反转与Bean对象的方法》文章介绍了SpringBoot中的控制反转(IoC)概念,描述了IoC容器如何管理Bean的生命周期和依赖关系,它详细讲解了Bean的注册过程,包括... 目录1 控制反转1.1 什么是控制反转1.2 SpringBoot中的控制反转2 Ioc容器对Bea

Java对象和JSON字符串之间的转换方法(全网最清晰)

《Java对象和JSON字符串之间的转换方法(全网最清晰)》:本文主要介绍如何在Java中使用Jackson库将对象转换为JSON字符串,并提供了一个简单的工具类示例,该工具类支持基本的转换功能,... 目录前言1. 引入 Jackson 依赖2. 创建 jsON 工具类3. 使用示例转换 Java 对象为

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行