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

news2024/9/28 3:19:47

起因

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

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

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

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

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

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

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

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

InitializeComponent

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

让我们来看一看它的内部

 1  /// <summary>
 2  /// InitializeComponent
 3  /// </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 startup
 2    {
 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2067042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中兴 随身WIFI 5产品参数

产品参数 无线参数无线速率2.4GHz, 300Mbps天线类型内置Wi-Fi天线软件功能手机App中兴ZTE Link APP Pro更多功能移动网络&#xff08;4G/3G&#xff09;接入、Wi-Fi接入、Wi-Fi加密认证、WebUI、PIN保护、FOTA升级等硬件规格接口Micro USB/标准SIM卡&#xff08;2FF&#xff0…

软件测试 缺陷报告处理流程

系统软件 操作系统 软件缺陷 缺陷报告 当测试人员发现了一个缺陷&#xff0c;需要填写一份 缺陷报告 来记录这个缺陷&#xff0c;并通过这个缺陷报告告知开发人员所发生的问题————缺陷报告是测试人员和开发人员交流沟通的重要工具。 缺陷报告的组成 1、缺陷ID 缺陷编号&…

JuiceFS 在多云架构中加速大模型推理

在大模型的开发与应用中&#xff0c;数据预处理、模型开发、训练和推理构成四个关键环节。本文将重点探讨推理环节。在之前的博客中&#xff0c;社区用户 BentoML 和贝壳的案例提到了使用 JuiceFS 社区版来提高模型加载的效率。本文将结合我们的实际经验&#xff0c;详细介绍企…

Linux——网络(2)

一、通信 --- 不同主机上进程间的通信 1、IP和端口号 IP&#xff1a;标识网络中的一台主机 本质上 32位的整型数据 端口号: 标识某个进程 本质上 16位的整型数据 2、udp和tcp udp的特点: 1.无连接 2.不可靠 tcp的特点&#xff1a; 1.面…

【赵渝强老师】执行Oracle的冷备份与冷恢复

冷备份与冷恢复是指发生在数据库已经正常关闭的情况下进行的备份和恢复。由于此时数据库已经关闭&#xff0c;通过冷备份可以将数据库的关键性文件拷贝到另外存储位置。冷备份因为只是拷贝文件&#xff0c;因此备份的速度非常快。在执行恢复时&#xff0c;只需将文件再拷贝回去…

命令模式:如何利用命令模式实现手游后端架构?

成长路上不孤单&#x1f60a;【14后boy&#xff0c;C爱好者&#xff0c;持续分享所学&#xff0c;如有需要欢迎收藏转发&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#xff01;&#xff01;&#xff01;接上篇博文&#xf…

拍抖音在哪里去水印,三招教你快速掌握去水印技巧

在抖音上&#xff0c;我们经常会看到一些精彩的内容&#xff0c;想要保存下来&#xff0c;但往往视频上会有水印。本文将分享五个免费且高效的去除抖音视频水印的技巧&#xff0c;帮助你轻松保存无水印的视频。 技巧一&#xff1a;奈斯水印助手(小程序) 奈斯水印助手是一款专…

为技术博客添加评论功能:Gitalk 教程与实战

为技术博客添加评论功能&#xff1a;Gitalk 教程与实战 简介安装使用创建 Github Application方式1方式2 主页传送门&#xff1a;&#x1f4c0; 传送 简介 Gitalk是一个基于 GitHub Issue 和 Preact 开发的评论插件。   Gitalk是一个现代、无后端、基于GitHub Issue的评论系…

基于单片机的程控电源显示控制电路设计

摘要 : 介绍了基于单片机程控电源显示控制电路的硬件设计和软件实现 &#xff0c; 该设计可以实现程控电源的输出显示和手动控制功能。 实践验证 &#xff0c; 该设计具有很好的使用效果和工程价值 。 关键词 : 程控电源 ; 显示控制 ; 单片机 0 引言 程控电源广泛地应用在…

python怎么写乘法表

代码如下&#xff1a; 代码详解&#xff08;为了让自己理解&#xff09;&#xff1a; for i in range(1,10):# print(i,end )for j in range(1,i1):print(%s*%s%s %(i,j,i*j),end )print() 1. for i in range(1,10) 这是一个for循环语句&#xff0c;range&#xff08;&…

无线数传模块是啥东西?

一 、 产品概述 无线数传模块是用来替代传统数据采集、通讯、控制布线的占用工业级模块。 无线数传模块一款工作在免费频段、5000m传输距离模块发射功率158mW、具有高稳定性、低功耗、高性价比、工业级特点。 模块具有多种传输距离规格可供选择&#xff0c;根据应用场景需要&am…

【python】基础一

目录 数据类型数据类型转换-整数/浮点数/字符串标识符运算符字符串扩展字符串拼接字符串格式化字符串精度字符串格式化-快速写法对表达式进行格式化字面量变量数据输入 数据类型 查看数据类型&#xff1a;type(数据) #输出结果&#xff1a;classint> print(type(10))数据类型…

自动生成依赖清单:pipreqs,Python项目的救星

文章目录 **自动生成依赖清单&#xff1a;pipreqs&#xff0c;Python项目的救星**背景&#xff1a;为何选择pipreqs&#xff1f;pipreqs是什么&#xff1f;如何安装pipreqs&#xff1f;库函数使用方法场景应用场景一&#xff1a;新项目初始化场景二&#xff1a;更新现有项目依赖…

继Ollama之后,Go在AI领域再下一城

AI isnt a thing; its a magnifier of a thing. And that thing is human creativity. 在AI领域&#xff0c;最火的使用Go开发的项目莫过于Ollama项目了&#xff0c;但近期有一个项目也吸引了Gopher眼球&#xff0c;它就是fabric。叫fabric的项目太多&#xff0c;这个fabric又是…

keil在debug时,watch窗口中变量不变化的解决方法

在DEBUG时&#xff0c;我们可能遇到自己定义的变量在watch窗口中一直是一个固定值&#xff0c;不会变化&#xff0c;我们只需要在debug时点开view勾选上最后一个选项periodic window update即可

airflow调度时间详解

⭐️ airflow调度概述 Apache Airflow 是一个开源的工作流调度和监控平台&#xff0c;广泛用于数据工程、ETL&#xff08;提取、转换、加载&#xff09;管道以及各种自动化任务。下面我将详细说明 Airflow 的调度算法。 1. DAG&#xff08;有向无环图&#xff09; Airflow 的…

萌拉数据需要下载吗,萌啦数据使用需不需要下载

在数字化浪潮席卷全球的今天&#xff0c;数据成为了驱动企业决策、优化产品服务、精准市场营销的关键要素。而提到数据处理与分析的利器&#xff0c;“萌拉数据”这一名字逐渐在业界崭露头角。面对这样一个功能强大的数据平台&#xff0c;不少用户心中或许会产生疑问&#xff1…

前端:内嵌微信扫码登陆在chrome浏览器失败

前端&#xff1a;内嵌微信二维码登录。 官方文档&#xff1a; 关于微信快速登录功能的说明 | 微信开放文档 按照官方文档书写后&#xff0c;二维码出现在了页面上。但是扫码登录时&#xff0c;浏览器控制台报错 Unsafe JavaScript attempt to initiate navigation for frame w…

无需多部备用机,云手机方便又便宜!

云手机&#xff0c;是云计算技术的又一创新应用&#xff0c;它通过在云服务器上虚拟出带有原生安卓操作系统的手机实例&#xff0c;为用户提供了一种全新的手机使用体验。无需携带多部手机&#xff0c;只需通过云手机&#xff0c;便可轻松实现多账号管理、应用运行及数据存储等…

用这个项目管理工具创建管理表,轻松实现项目管理!

在项目管理中&#xff0c;时间表不仅仅是一个简单的计划工具&#xff0c;而是确保项目按时完成的关键 许多项目经理和团队成员在推进项目时&#xff0c;常常因为缺乏明确的时间表而陷入混乱&#xff0c;导致进度拖延、资源浪费&#xff0c;甚至项目失败。 那么&#xff0c;如何…