dotnet 如何将 Microsoft.Maui.Graphics 对接到 UNO 框架
本文将和大家介绍如何将 Microsoft.Maui.Graphics 对接到 UNO 框架里面。一旦完成 Microsoft.Maui.Graphics 对接,即可让 UNO 框架复用现有的许多绘制的基础设施和现有基础库,且可以更进一步与 MAUI 打通
众所周知,在 UNO 里面有大量的项目类型都是基于 Skia 作为底层渲染引擎构建出来的。在 Microsoft.Maui.Graphics 中有对 Skia 的一个实现,即 Microsoft.Maui.Graphics.Skia 实现方式。根据 UNO 的 Skia 例子 可以知道,在 UNO 里面是可以采用 SKXamlCanvas 执行直接对 Skia 绘制的内容的
然而坏消息是当前在 2024.01.29 时,在 UNO 里面的 SKXamlCanvas 是采用一个 Hack 的方式与 UNO 对接。在 SKXamlCanvas 里面将会重新创建 SKSurface 用于让开发者在此之上绘制,接着再将绘制的 Bitmap 通过 CPU 拷贝到 UNO 的 WriteableBitmap 上,让 UNO 使用图片绘制的形式将 WriteableBitmap 绘制出来。通过 WriteableBitmap 与 Skia 进行间接对接的方式,将会极大影响绘制性能,在绘制的中间存在很大的 CPU 压力和绘制延迟。此问题的讨论地址: https://github.com/unoplatform/uno/discussions/15097
为了减少 SKXamlCanvas 的 Hack 方式的影响,本文这里采用了 UNO 框架里面未公开的 Visual 绘制方式。由于本方式用到的是 UNO 框架私有的 API 实现的功能,因此可能在后续的 UNO 更新版本,本文提供的方法将会无效
本文不是提供一个开箱即用的方法,代替的是,本文提供的是一套源代码搭建对接的方法。阅读完成本文,你将学会如何自行在自己的 UNO 项目里面搭建与 Microsoft.Maui.Graphics 对接的代码,且了解其中的细节实现逻辑,方便你进行更进一步的定制。在本文末尾你将找到本文用到的所有代码的下载方法
本文以下提供了在 Uno.Skia.WPF 和 Uno.Skia.GTK 平台下,使用与 UNO 的 Visual 直接对接的方式,而不是经过了 WriteableBitmap 的 SKXamlCanvas 方式,进行与 Microsoft.Maui.Graphics.Skia 对接,进而将 Microsoft.Maui.Graphics 对接到 UNO 框架
整体的架构引用关系图如下

上图的 Microsoft.Maui.Graphics.UnoAbstract 和 UnoHacker (SamplesApp) 就是接下来咱重点要工作的部分,额外的一部分工作则放在 Uno.Skia.WPF 和 Uno.Skia.GTK 平台对接代码上,平台项目的对接工作量很小,所需的代码量很少
先从 Microsoft.Maui.Graphics.UnoAbstract 项目的搭建开始,在 Microsoft.Maui.Graphics.UnoAbstract 项目里面提供了一些用于 UnoHacker (SamplesApp) 使用的 Hack 接口,用于方便上层应用框架对接,其定义的代码如下
using Microsoft.UI.Xaml;
namespace Microsoft.Maui.Graphics.UnoAbstract;
public interface IHack
{
FrameworkElement Create();
}
public static class HackHelper
{
public static IHack? Hack { set; get; }
}
通过以上的代码,即可在 HackHelper 里面注入 Hack 对象的值。另外的,为了获取到绘图的通知,即与 FrameworkElement 的更特殊的实现,这里也需要在 Microsoft.Maui.Graphics.UnoAbstract 项目里面添加名为 IDrawableNotify 的接口,代码如下
namespace Microsoft.Maui.Graphics.UnoAbstract;
public interface IDrawableNotify
{
event EventHandler<ICanvas>? Draw;
}
完成基础的接口定义之后,即可在 Microsoft.Maui.Graphics.UnoAbstract 项目编写用于给通用跨平台的 UNO 业务项目直接使用的 GraphicsCanvas 类型。此 GraphicsCanvas 类型将继承 Microsoft.UI.Xaml.Controls.Canvas 类型,可以直接在 XAML 里面加入,且在开始绘制时触发 Draw 事件,方便业务代码使用 Draw 事件编写 Microsoft.Maui.Graphics 的绘制业务代码
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.Maui.Graphics.UnoAbstract;
public class GraphicsCanvas : Canvas, IDrawableNotify
{
public GraphicsCanvas()
{
SizeChanged += OnSizeChanged;
var frameworkElement = HackHelper.Hack?.Create();
if (frameworkElement != null)
{
IDrawableNotify drawableNotify = (IDrawableNotify) frameworkElement;
drawableNotify.Draw += OnDraw;
Children.Add(frameworkElement);
FrameworkElement = frameworkElement;
}
else
{
var textBlock = new TextBlock()
{
Text = "Not Supported"
};
FrameworkElement = textBlock;
}
}
private FrameworkElement FrameworkElement { get; }
private void OnDraw(object? sender, ICanvas e)
{
Draw?.Invoke(this, e);
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement.Width = e.NewSize.Width;
FrameworkElement.Height = e.NewSize.Height;
}
public event EventHandler<ICanvas>? Draw;
}
接下来创建 UnoHacker (SamplesApp) 项目,这个项目需要设置项目的程序集名为 SamplesApp 才可以使用到 UNO 框架里面的一些内部成员,方便起见可以将此项目直接命名为 SamplesApp 项目。之所以叫这个名字是因为在 UNO 里面添加了 Internal 对名为 SamplesApp 程序集可见
再次说明,由于 UnoHacker (SamplesApp) 项目将使用 UNO 的一些不公开的类型,可能在后续的 UNO 版本里将会失效
在 UnoHacker (SamplesApp) 项目里面只需一个简单的类型,此类型将与 UNO 的 Visual 进行对接,代码如下
public class GraphicsCanvasElement : FrameworkElement
{
public GraphicsCanvasElement()
{
Visual.Children.InsertAtBottom(new GraphicsCanvasVisual(Visual.Compositor, this));
}
public event EventHandler<ICanvas>? Draw;
internal void InvokeDraw(ICanvas canvas)
{
Draw?.Invoke(this, canvas);
}
class GraphicsCanvasVisual : Visual
{
public GraphicsCanvasVisual(Compositor compositor, GraphicsCanvasElement owner) : base(compositor)
{
_owner = new WeakReference<GraphicsCanvasElement>(owner);
}
private readonly WeakReference<GraphicsCanvasElement> _owner;
internal override void Draw(in DrawingSession session)
{
if (_owner.TryGetTarget(out var graphicsCanvasElement))
{
using var skiaCanvas = new SkiaCanvas();
skiaCanvas.Canvas = session.Surface.Canvas;
graphicsCanvasElement.InvokeDraw(skiaCanvas);
}
}
}
}
如此就完成了与 Microsoft.Maui.Graphics 对接的基础代码了,剩余的工作就需要在具体的 Uno.Skia 平台项目里面编写对接代码,通过在 Uno.Skia 平台项目里面编写对接代码将 Microsoft.Maui.Graphics.UnoAbstract 与 UnoHacker (SamplesApp) 项目进行对接。额外说明的是为什么不能将 Microsoft.Maui.Graphics.UnoAbstract 与 UnoHacker (SamplesApp) 项目合并,原因是为了让 Microsoft.Maui.Graphics.UnoAbstract 项目还可以在 UNO 的非 Skia 实现平台上使用,只让 UnoHacker (SamplesApp) 项目强引用 UNO 的 Skia 实现,如此即可让 Microsoft.Maui.Graphics.UnoAbstract 项目同时被 UNO 的 WinUI3 等非 Skia 的实现的项目进行对接
接下来开始编写 Uno.Skia.WPF 和 Uno.Skia.GTK 平台对接代码,这两个部分的平台对接代码内容都是相同的。先定义一个名为 HackElement 的继承 GraphicsCanvasElement 的控件,代码如下,定义此类型控件仅仅只是为了方便统一对接代码而已
public partial class HackElement : GraphicsCanvasElement, IDrawableNotify
{
}
接着创建名为 Hack 的类型,让此类型继承 Microsoft.Maui.Graphics.UnoAbstract 的 IHack 接口,代码如下
public class Hack : IHack
{
public FrameworkElement Create()
{
return new HackElement();
}
}
最后创建 HackInitializer 类型,用于将 Hack 类型放入到 Microsoft.Maui.Graphics.UnoAbstract 的 HackHelper 静态类型里面
static class HackInitializer
{
public static void Init()
{
HackHelper.Hack = new Hack();
}
}
通过以上的封装代码,即可在各平台项目里面通过调用 HackInitializer 的 Init 方法,即可完成所有的对接逻辑。比如在 Uno.Skia.Wpf 的默认 App 构造函数里面,调用 HackInitializer.Init(); 代码。比如在 Uno.Skia.GTK 项目里面的 Program 里面,使用如下面代码调用对接方法
public class Program
{
public static void Main(string[] args)
{
ExceptionManager.UnhandledException += delegate (UnhandledExceptionArgs expArgs)
{
Console.WriteLine("GLIB UNHANDLED EXCEPTION" + expArgs.ExceptionObject.ToString());
expArgs.ExitApplication = true;
};
HackInitializer.Init();
var host = new GtkHost(() => new AppHead());
host.Run();
}
}
完成所有对接逻辑之后,接下来即可在 UNO 的全平台项目里面,使用 GraphicsCanvas 绘制业务代码的界面。比如在 MainPage.xaml 里面添加以下代码
xmlns:graphics="using:Microsoft.Maui.Graphics.UnoAbstract"
<StackPanel x:Name="StackPanel"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock x:Name="TextBlock" AutomationProperties.AutomationId="HelloTextBlock"
Text="Hello Uno Platform"
HorizontalAlignment="Center" />
<Border Background="AliceBlue">
<graphics:GraphicsCanvas Draw="GraphicsCanvas_OnDraw"/>
</Border>
</StackPanel>
在后台代码即可在 GraphicsCanvas_OnDraw 方法的 Microsoft.Maui.Graphics.ICanvas 参数里面获取到与整个 Microsoft.Maui.Graphics 对接的开始,如下面代码简单绘制界面
private void GraphicsCanvas_OnDraw(object? sender, ICanvas e)
{
e.StrokeSize = 5;
e.StrokeColor = Colors.Red;
e.DrawRectangle(0, 0, 100, 100);
}
运行项目,将可以看到如下界面

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin daf3e44a853177d55e9ebb15d989e27b1e497591
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin daf3e44a853177d55e9ebb15d989e27b1e497591
获取代码之后,进入 KefalurcilaybelJallbuderenajel 文件夹
dotnet 如何将 Microsoft.Maui.Graphics 对接到 UNO 框架的更多相关文章
- dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门
本文将告诉大家如何在 dotnet 的控制台模式下,采用 MAUI 自绘库 Microsoft.Maui.Graphics 进行绘图,设置 Microsoft.Maui.Graphics 底层调用 M ...
- DotNet IOC Framework - Microsoft Unity介绍
一. 新建一个ASP.NET MVC4项目 二. 安装Microsoft Unity 1) 管理Nuget程序包 2)安装Unity3程序包 在你的App_Start文件夹里会多出来两个文件 三. 一 ...
- 使用 MAUI 在 Windows 和 Linux 上绘制 PPT 的图表
我在做一个图表工具软件,这个软件使用 MAUI 开发.我的需求是图表的内容需要和 PPT 的图表对接,需要用到 OpenXML 解析 PPT 内容,读取到 PPT 图表元素的内容,接着使用 MAUI ...
- 乘风破浪,.Net Core遇见MAUI(.NET Multi-platform App UI),进击现代化跨设备应用框架
什么是MAUI https://github.com/dotnet/maui .NET Multi-platform App UI (MAUI) 的前身是Xamarin.Forms(适用于Androi ...
- .NET MAUI 正式版GA发布
.NET MAUI – 一个代码库,多个平台 欢迎使用 .NET 多平台应用 UI.此版本标志着我们统一 .NET 平台的多年旅程中的新里程碑.现在,您和超过 500 万其他 .NET 开发人员拥有了 ...
- [MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条
@ 目录 关于图形绘制 创建自定义控件 使用控件 创建专辑封面 项目地址 我们将绘制一个圆形的音乐播放控件,它包含一个圆形的进度条.专辑页面和播放按钮. 关于图形绘制 使用MAUI的绘制功能,需要Mi ...
- 国产化之Arm64 CPU+银河麒麟系统安装.NetCore
背景 某个项目需要实现基础软件全部国产化,其中操作系统指定银河麒麟,银河麒麟就是一个Linux发行版,数据库使用达梦V8,这个数据库很多概念和Oracle相似,CPU平台的范围:龙芯.飞腾.鲲鹏等. ...
- .NET周报【11月第3期 2022-11-22】
国内文章 .NET Conf China 2022 第一批讲师阵容大揭秘!整个期待了! https://mp.weixin.qq.com/s/4p89hhBPw6qv-0OB_T_TOg 目光看过来 ...
- .NET 6使用ImageSharp给图片添加水印
.NET 6 中,使用System.Drawing操作图片,生成解决方案或打包的时候,会有警告,意思是System.Drawing仅在 'windows' 上受支持.微软官方的解释是: Syste ...
- ASP.NET Core 通过 Microsoft.DotNet.Watcher.Tools 实现热部署
之前开发前端的时候,webpack 会有热更新工具,在修改了代码之后,自动将代码编译,实时展现到页面上,给开发带来了极大的方便. Java也可以通过第三方插件JRebel实现热部署,不用频繁的重启To ...
随机推荐
- 分享一个项目:go `file_line`,在编译器得到源码行号,减少运行期runtime消耗
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 file_line https://github.com/ ...
- Oracle两表关联更新
表结构.测试数据 drop table t1; drop table t2; CREATE TABLE T1 ( name VARCHAR2(10) , code VARCHAR2(10) ); AL ...
- 取cookie时报错“[object Object]“ is not valid JSON
做谷粒学苑项目时,在保存登录状态环节出现该问题 取cookie时老师强调要使用JSON.pase()解析cookie为json对象 var userStr = cookie.get("gul ...
- 双向循环链表(DoubleLoopLinkList)
双向循环链表 关于双向循环链表可以先阅读这篇文章这里就不再赘述:双向链表(DoubleLinkList) Node template<typename T> class Node { pu ...
- set集合的HashSet 类
set是一个没有重复元素, 没有存储顺序的集合,它主要有三个实现类,如下: HashSet 类 HashSet 类是按照哈希算法来存储集合中的元素,当向 Set 集合中添加一个元素时,HashSet ...
- C# Break 和 Continue 语句以及数组详解
C# Break 它被用于"跳出" switch 语句. break 语句也可用于跳出循环. 以下示例在 i 等于 4 时跳出循环: 示例: for (int i = 0; i & ...
- CMake vs Makefile 如何选择适合你的项目构建工具
CMake vs Makefile: 如何选择适合你的项目构建工具 在软件开发中,构建(build)是一个非常重要的过程.我们需要将源代码转换为可执行文件或库文件.为了完成此过程,我们通常使用构建工具 ...
- C# 面试问答
引用:https://www.cnblogs.com/zh7791/p/13705434.html 1.什么是 COM? COM 代表组件对象模型.COM 是微软技术之一.使用这项技术,我们可以开 ...
- Qt:获取WIFI列表
示例:使用QT来获取Windows电脑WIFI列表中所有WIFI的名称,实际是执行CMD命令来完成(netsh wlan show networks) // 获取WIFI列表 QProcess pro ...
- VS Qt扩展插件下载地址
使用vs开发qt项目,需要安装qt插件 QT插件下载地址:https://mirrors.ustc.edu.cn/qtproject/official_releases/vsaddin/