Photino:通过.NET 构建跨平台桌面应用程序

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Photino:通过.NET 构建跨平台桌面应用程序相关的知识,希望对你有一定的参考价值。

介绍

本文将指导您构建您的第一个Photino桌面应用程序(基于.NET Core构建),该应用程序将在所有三大平台(Linux、Mac、Windows)上运行。

为什么?

您梦寐以求的未来终于来了:一次构建您的桌面应用程序,随处运行。

是的,这个未来确实伴随着html5(HTML、javascript、CSS),但它很好,我经验丰富的桌面开发者朋友。这很好,因为现在您拥有.NET Core Framework的强大功能。

一次构建您的用户界面(使用HTML5、JavaScript和CSS),同时利用.NET Core的所有功能来访问桌面API功能(读/写文件、加密API、通过.NET Core公开的所有内容)。

背景

为什么我对跨平台应用感兴趣

我编写了一个密码生成器(windows store链接[ ^ ] FOSS(完全开源软件),因此您可以在我的Github链接[ ^ ] 上获取源代码。

如果您要编写一个人们将要使用的密码生成器,它必须在每个已知平台上运行,因此无论用户在哪里需要她的密码,它都将可用。

原始版本是使用ElectronJS(Chrome引擎)编写的,也可以在所有主要平台上运行。现在Photino已经到来,我打算将应用程序转换为.NET Core,它有一个简单的方法可以做到这一点。

官方Photino项目文档

顺便说一句,Photino得到了CODE杂志的优秀人士的支持,您可以在tryphotino.io上查看所有文档。另外,正如我所说,这都是开源的,您可以在github上获取所有代码。

这是我正在处理的一个FileViewer快速示例。请记住,UI是基于HTML5、JavaScript和CSS构建的,但它能够通过.NET Core调用本地“桌面”API --Directory.GetFiles()等。

 

但是,要了解Photino能为您做什么,让我们使用该库编写我们的第一个程序。

入门

更新说明:.NET 5.x与6.x

将代码克隆到未安装.NET Core的新系统后,当我安装.NET Core 6.x时,项目无法构建。  

.NET Core 6.x是新标准,因此必须同时安装旧版本会很痛苦。

一种替代的做法是,您可以简单地更新HelloPhotino.NET.csproj*文件以引用.net6.0。

*此名称是Phtino模板为您的项目提供的默认项目名称。我应该重命名它。😖

只需在编辑器中打开.csproj文件并更改以下行:

<TargetFramework>net5.0</TargetFramework>

只需将5更改为6即可构建。

<TargetFramework>net6.0</TargetFramework>

你需要什么

  • .NET Core 5.0或6.0 SDK已安装并准备就绪:转到此处从Microsoft获取。

  • Photino项目模板——从命令行创建项目非常简单

  • 代码编辑器:我在本文中使用Visual Studio Code

我将假设您确实安装了.NET Core 5或6。

您可以使用以下命令确定您拥有的版本:

$ dotnet --version

安装Photino项目模板

打开命令行提示符并运行以下命令:

$ dotnet new -i TryPhotino.VSCode.Project.Templates

这将简单地添加可用于dotnet new命令的项目模板列表。

您可以运行以下命令来查看所有项目模板的列表(您将看到列表中包含的新模板):

$ dotnet new -l // that's a lowercase L for list

您将看到所有项目模板的列表,如下所示:

创建我们的第一个项目

现在我们已经安装了Photino项目模板,我们可以转到开发目录(我命名我的dev/dotnet/photino/以包含我所有的photino项目),然后发出以下命令。

~/dev/dotnet/photino $ dotnet new photinoapp -o FirstOne

运行该命令将:

  1. 在我的photino目录下创建一个名为FirstOne的新目录(-o输出)

  2. 创建一个新的.NET Core项目(包括.csproj文件)和所有其余的基本应用程序文件。

  3. 创建一个wwwroot ——Photino用来存储用户界面文件(HTML、JavaScript、CSS)的特殊文件夹

运行基本应用程序

创建样板项目后,您可以立即运行它。

只需进入新目录并运行:

$ dotnet run  // compiles & runs the app

该应用程序将启动,中间会出现一个弹出对话框,以证明您可以通过 JavaScript 执行操作。

点击 [关闭] 按钮,您可以看到主界面。

单击[ Call .NET ]按钮,您将看到以下内容:

不是太惊人......然而

到目前为止没有什么太惊人的了。让我们看一下项目中包含的文件和代码,以便我们了解实际发生的情况。之后,我们将通过C#进行“桌面API”调用,它永远不会在Web应用程序中运行,以证明这个应用程序确实非常了不起。

Program.cs:一切开始的地方

这是Visual Studio Code中项目的一个大快照,其中显示了很多细节。

好而老的主要入口点

Program.cs文件的右上角,您可以看到我们有我们熟悉和喜爱的常规Main()方法。

魔术来了:它是如何工作的

这是一个实际的C# .NET程序。神奇之处在于它会自动加载WebView2(Microsoft文档)作为主Form界面,然后在该WebView2控件中加载目标 HTML。

如果我们在代码中向下滚动一点,您会看到该Main()方法进行的最后一次调用是以下Photino库调用:

.Load("wwwroot/index.html");

当然,正如您在左侧看到的那样,该index.html文件位于wwwroot文件夹中。

index.html文件如下所示:

这只是简单的HTML,但该文件构成了该应用程序的整个用户界面。这真是太神奇了。

现在你可以梦想

这意味着您现在可以使用任何HTML5(基于Web)的应用程序并将其封装在Photino中,并将其转换为可以在任何Mac、Linux或Windows机器上本地运行的桌面应用程序。

极端例子

作为一个实验,我创建了一个模板Photino项目,使用我基于Web的C'YaPass 应用程序(密码生成器),放入HTML(index.html)、JavaScript和CSS文件并运行Photino应用程序并得到以下结果代码更改。

该应用程序使用HTML5 Canvas和localStorage各种其他HTML技术,但可以在任何桌面上完美运行。

但为什么?

该应用程序还通过JavaScript函数生成SHA-256哈希码(用作密码)。现在,有了Photino,我可以删除JavaScript并使用.NET Core Cryptopgraphy库让一切变得更简洁。我可以这样做,因为我可以通过Photino框架内的C#调用桌面API 。

让我们看看如何对.NET API进行简单调用。

通过C#调用桌面API

为了证明这一点,我们确实需要通过C#调用Desktop API。

我们需要做什么

为了完成这项工作,我们将:

  1. 添加一个按钮来触发功能——当然,这个按钮将在index.html中创建

  2. 单击按钮时,我们需要向Photino窗口(C#端)发送一条消息,该窗口将请求调用相关的桌面API。

  3. 将消息发送回用户界面(index.html )

  4. 在用户界面(index.html )中显示我们调用的结果

获取源代码

我将在本文顶部添加完整的代码,以便您轻松试用。

仅供参考——从模板中删除代码

自动弹出的代码很烦人,所以我删除了它。

第 1 步:添加按钮

为了简单起见,我将在现有按钮下方添加一个新按钮(来自项目模板):

<button id="callApiButton" onclick="callApi()">Call API</button>

仅供参考——是的,我知道很多人不喜欢将事件处理程序(onclick)放在HTML元素上,但对于我们的示例来说,这是简化的。

添加后,您可以运行并看到按钮存在,但什么也不做。

如果您跟随运行该应用程序,只需转到您的项目命令行并键入:

$ dotnet run

现在,让我们让按钮做点什么。

第 2 步:向C#端发送消息

我将添加一个新的JavaScript文件(api.js)并将其包含在index.html文件的顶部。api.js文件将包含处理callApi()函数的代码

我正在从index.html中复制样板代码,该代码用于在单击第一个按钮时向应用程序发送消息:

window.external.sendMessage('Hi .NET! 🤖');

那是用于与处理消息发送的Photino库进行交互的JavaScript代码。

更改消息

模板项目发送的消息非常幼稚,因为它只是一个string。实际上,我们可能希望/需要发送某种结构,其中包含:

  1. 命令信息

  2. C#端的目标函数将使用的一个或多个参数。

JavaScript对象和JSON

我将创建一个JavaScript对象,然后使用JSON.stringify(创建完美JSON)将其string发送到C#端,然后将其反序列化并输出命令。

这是api.js的完整代码列表:

function callApi()
let message = ; // create basic object
message.command = "getUserProfile";
message.parameters = "";
let sMessage = JSON.stringify(message);
console.log(sMessage);
window.external.sendMessage(sMessage);

在这种情况下,我没有使用任何其他参数,但无论如何我都在传递它们。

此外,我不必创建单独的sMessage变量,但我这样做是为了让您查看我们正在传递的实际string (JSON)。

现在我们的按钮会做点什么

如果您一直在关注,请不要忘记在index.html的顶部添加对我们新api.js的引用。

完成所有设置后,运行应用程序($ dotnet run)并单击新按钮。

您将在控制台窗口(来自Photino.net)中看到一些日志记录,并且您会在应用程序中看到收到的消息弹出窗口。

对收到的消息采取行动

但这还没有完成,因为我们希望它捕获message.Command并采取相应的行动(调用特定的桌面API)。

将JSON解析为对象

为此,我们需要更改Program.cs以将我们发送的JSON解析为适当的对象。我们需要在C#方面做这项工作。

首先,让我们创建一个简单的DTO(数据传输对象)

我添加了一个名为Model的新文件夹(用于域模型对象),并创建了名为WindowMessage.cs的新DTO类文件。(您将在本文所附的最终代码中看到这一切。)

这是一个简单的代码,现在可以非常轻松地在我们的代码中使用C# JSON序列化器/反序列化器。

using System;
class WindowMessage
public WindowMessage(String command, String parameters)

this.Command = command;
this.Parameters = parameters;
this.AllParameters = parameters.Split(',',StringSplitOptions.RemoveEmptyEntries);

public String Commandget;set;
public String[] AllParametersget;set;
public String Parametersget;set;

传入的参数将以逗号分隔的string,然后类将自动对其进行拆分并创建一个String数组,其中包含我们可能想要使用的参数。

现在让我们使用这个代码。

Program.cs中,主消息处理程序(来自项目模板)是一个简化的方法,如下所示:

.RegisterWebMessageReceivedHandler((object sender, string message) => 
var window = (PhotinoWindow)sender;
// The message argument is coming in from sendMessage.
// "window.external.sendMessage(message: string)"
string response = $"Received message: \\"message\\"";
// Send a message back the to JavaScript event handler.
// "window.external.receiveMessage(callback: Function)"
window.SendWebMessage(response);
)

您可以看到传入的消息只是一个string。

当然,在我们的新代码中,我们保证发送一个WindowMessage对象(通过JSON)。

因为C#使JSON反序列化变得如此简单,我们可以添加以下代码来反序列化到我们的DTO(WindowMessage)并处理该Command值。

我在Program.csusing的顶部添加了语句:

using System.Text.Json;
using System.Text.Json.Serialization;

现在我可以在.RegisterWebMessageReceivedHandler()函数调用的顶部添加以下代码:

WindowMessage wm = JsonSerializer.Deserialize<WindowMessage>(message);

这会将传入的数据解析message String为我们的目标、DTO。

打开WindowMessage.Command

现在,我们的代码.RegisterWebMessageRecievedHandler()如下所示:

.RegisterWebMessageReceivedHandler((object sender, string message) => 
var window = (PhotinoWindow)sender;
WindowMessage wm = JsonSerializer.Deserialize<WindowMessage>(message);
switch(wm.Command)
case "getUserProfile":
window.SendWebMessage($"I got : wm.Command");
break;

default :
// The message argument is coming in from sendMessage.
// "window.external.sendMessage(message: string)"
string response = $"Received message: \\"wm.Parameters\\"";
// Send a message back the to JavaScript event handler.
// "window.external.receiveMessage(callback: Function)"
window.SendWebMessage(response);
break;


)

我们只需将JSON反序列化为DTO,然后打开该wm.Command值。

注意:我对原始Button JavaScript进行了更改,因此它也会传递一个有效WindowMessage对象,但您可以自己查看该代码。

这是单击新按钮时运行的样子。

我们现在可以成功运行各种C#代码,这取决于我们的WindowMessage中的Command是什么。经验丰富的开发人员:这一切如何回到原始的Windows消息循环(Windows API编程)和处理消息,这不是很有趣吗?

总结:通过环境获取用户配置文件

好吧,这应该是对Photino的快速介绍,所以让我们添加一个对.NET API的调用,然后一天结束。

但是,正确地总结一下,我们还需要向您展示如何使用返回给用户界面端(HTML)的值。

在用户界面端注册消息接收器(HTML)

为了取回值,我们需要在应用加载时在用户界面端注册一个消息接收器。

我们会做两件事:

  1. 向HTML添加一个onload函数,该函数将运行初始化并设置消息接收器

  2. 将initApi()方法添加到api.js

这是代码(在api.js中),它将在应用程序启动时(在HTML加载时)被初始化。

function initApi()
window.external.receiveMessage(response => 
response = JSON.parse(response);
switch (response.Command)
case "getUserProfile":
alert(`user home is: $response.Parameters`);
document.querySelector("#output").innerHTML = `$response.Parameters`;
break;

default:
alert(response.Parameters);
break;


);

调用Desktop API后,此代码将获得响应(从C#端发送) 。它将包含用户目录的值(通过C#检索Environment.GetFolderPath(Environment.SpecialFolder.UserProfile))。

一旦此代码(JavaScript)接收到该值,它将使用alert()显示它并使用document.querySelector("#output").innerHTML将其写入主HTML。

这是最终的C#代码。

.RegisterWebMessageReceivedHandler((object sender, string message) => 
var window = (PhotinoWindow)sender;
WindowMessage wm = JsonSerializer.Deserialize<WindowMessage>(message);
switch(wm.Command)
case "getUserProfile":
wm.Parameters = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
window.SendWebMessage(JsonSerializer.Serialize(wm));
break;

default :
// The message argument is coming in from sendMessage.
// "window.external.sendMessage(message: string)"
wm.Parameters = $"Received message: \\"wm.Parameters\\"";
// Send a message back the to JavaScript event handler.
// "window.external.receiveMessage(callback: Function)"
window.SendWebMessage(JsonSerializer.Serialize(wm));
break;


)

这是我单击新按钮后的快照。

现在,您去尝试并制作一些您自己的应用程序。

记住:构建和部署到任何操作系统

请记住,您现在可以获取此代码并构建它并将其部署到任何操作系统,它将正常运行。惊人!

你觉得呢

这是构建桌面应用程序的新方法吗?我认为这是构建可在任何平台上运行的用户界面的一种非常酷的方式。我认为这很了不起,我将继续追求进一步的发展。

https://www.codeproject.com/Articles/5333548/Photino-Open-Source-for-Building-Cross-Platform-De

以上是关于Photino:通过.NET 构建跨平台桌面应用程序的主要内容,如果未能解决你的问题,请参考以下文章

.NET 创建无边框的跨平台应用

开发一个简单的Windows系统托盘桌面应用程序来使用.NET Web服务

Flutter支持构建Linux桌面应用,Snap格式却惹质疑

构建完整的 Ubuntu 桌面 docker 镜像

VS Code + NWJS(Node-Webkit)0.14.7 + SQLite3 + Angular6 构建跨平台桌面应用

.net core 跨平台桌面应用