本文将和大家介绍如何将 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);
}

运行项目,将可以看到如下界面

本文以上代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 框架的更多相关文章

  1. dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门

    本文将告诉大家如何在 dotnet 的控制台模式下,采用 MAUI 自绘库 Microsoft.Maui.Graphics 进行绘图,设置 Microsoft.Maui.Graphics 底层调用 M ...

  2. DotNet IOC Framework - Microsoft Unity介绍

    一. 新建一个ASP.NET MVC4项目 二. 安装Microsoft Unity 1) 管理Nuget程序包 2)安装Unity3程序包 在你的App_Start文件夹里会多出来两个文件 三. 一 ...

  3. 使用 MAUI 在 Windows 和 Linux 上绘制 PPT 的图表

    我在做一个图表工具软件,这个软件使用 MAUI 开发.我的需求是图表的内容需要和 PPT 的图表对接,需要用到 OpenXML 解析 PPT 内容,读取到 PPT 图表元素的内容,接着使用 MAUI ...

  4. 乘风破浪,.Net Core遇见MAUI(.NET Multi-platform App UI),进击现代化跨设备应用框架

    什么是MAUI https://github.com/dotnet/maui .NET Multi-platform App UI (MAUI) 的前身是Xamarin.Forms(适用于Androi ...

  5. .NET MAUI 正式版GA发布

    .NET MAUI – 一个代码库,多个平台 欢迎使用 .NET 多平台应用 UI.此版本标志着我们统一 .NET 平台的多年旅程中的新里程碑.现在,您和超过 500 万其他 .NET 开发人员拥有了 ...

  6. [MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条

    @ 目录 关于图形绘制 创建自定义控件 使用控件 创建专辑封面 项目地址 我们将绘制一个圆形的音乐播放控件,它包含一个圆形的进度条.专辑页面和播放按钮. 关于图形绘制 使用MAUI的绘制功能,需要Mi ...

  7. 国产化之Arm64 CPU+银河麒麟系统安装.NetCore

    背景 某个项目需要实现基础软件全部国产化,其中操作系统指定银河麒麟,银河麒麟就是一个Linux发行版,数据库使用达梦V8,这个数据库很多概念和Oracle相似,CPU平台的范围:龙芯.飞腾.鲲鹏等. ...

  8. .NET周报【11月第3期 2022-11-22】

    国内文章 .NET Conf China 2022 第一批讲师阵容大揭秘!整个期待了! https://mp.weixin.qq.com/s/4p89hhBPw6qv-0OB_T_TOg 目光看过来 ...

  9. .NET 6使用ImageSharp给图片添加水印

    ​ .NET 6 中,使用System.Drawing操作图片,生成解决方案或打包的时候,会有警告,意思是System.Drawing仅在 'windows' 上受支持.微软官方的解释是: Syste ...

  10. ASP.NET Core 通过 Microsoft.DotNet.Watcher.Tools 实现热部署

    之前开发前端的时候,webpack 会有热更新工具,在修改了代码之后,自动将代码编译,实时展现到页面上,给开发带来了极大的方便. Java也可以通过第三方插件JRebel实现热部署,不用频繁的重启To ...

随机推荐

  1. 记录转载:Vite多环境配置--让项目拥有更高定制化能力

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 业务背景 近些年来,随着前端工程架构发展,使得前端项目中也能拥有如后端工程的模块能力.正所谓 "能力(越)越大(来),责任(越) ...

  2. 记录--前端实现电子签名(web、移动端)通用

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 在现在的时代发展中,从以前的手写签名,逐渐衍生出了电子签名.电子签名和纸质手写签名一样具有法律效应.电子签名目前主要还是在需要个人确 ...

  3. 快速上手系列:JavaScript

    第一章 基础语法 1 javascript 的简介 * 是基于对象和事件驱动的语言,应用于客户端. - 基于对象: ** 提供好了很多对象,可以直接拿过来使用 - 事件驱动: ** html 做网站静 ...

  4. [HTML、CSS]细节、经验

    [版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://blog.csdn.net/m0_69908381/article/details/130134573 出自[进步* ...

  5. KingbaseES V8R6 逻辑恢复到新的 schema

    前言 本文介绍一下KingbaseES V8R6版本中逻辑恢复时,将原有的对象恢复到新的schema. sys_restore命令中如果只加入了-g(原schema) -G(新schema)参数 那么 ...

  6. c语言的一些类型声明符

    基本类型: char: 字符类型 int: 整数类型 float: 单精度浮点数类型 double: 双精度浮点数类型 void: 无类型 修饰符: short: 短整数类型 long: 长整数类型 ...

  7. Hadoop_04 验证Hadoop安装成功

    [root@master ~]# cd hadoop-2.7.7/[root@master hadoop-2.7.7]# cd etc[root@master etc]# cd hadoop/[roo ...

  8. #根号分治,前缀和,双指针#CF1446D2 Frequency Problem (Hard Version)

    题目 给定一个长度为 \(n\) 的序列,问是否存在一个最长的区间使得至少存在两个众数. 分析 实际上 Easy Version 是用来启发大于根号的做法的. 众数可以说有一个性质吧,答案区间中的其中 ...

  9. 【Learning eBPF-3】一个 eBPF 程序的深入剖析

    从这一章开始,我们先放下 BCC 框架,来看仅通过 C 语言如何实现一个 eBPF.如此一来,你会更加理解 BCC 所做的底层工作. 在这一章中,我们会讨论一个 eBPF 程序被执行的完整流程,如下图 ...

  10. MyBatis-Plus 代码生成(旧)

    MyBatis-Plus官网的代码生成器配置不是特别全,在此整理了较为完整的配置,供自己和大家查阅学习. // 代码生成器 AutoGenerator mpg = new AutoGenerator( ...