本指南将介绍如何使用 Avalonia 和 DotNetBrowser 在 Raspberry Pi 5 上构建自助终端应用。

所谓自助服务终端应用,通常是指带有锁定浏览器机制的系统——用户无法随意离开当前页面、打开新窗口,或访问操作系统控制界面。

在本指南中,应用在架构层面上实现锁定。它无需桌面环境即可运行,完全控制显示与输入。

该应用程序使用 C# 编写。它利用 Avalonia UI 创建一个全屏窗口,并使用 DotNetBrowser Web 视图组件来显示 Web 内容。

您将学习如何设置开发环境、配置操作系统以及为带有触摸显示屏的自助服务终端部署应用。

准备工作 

您需要一台 树莓派 5(Raspberry Pi 5),理想配置为 8 GB 或 16 GB 内存。由于 DotNetBrowser 基于 Chromium,运行时内存占用较高,因此更大的内存会带来更好的性能。

开发与部署从您的常用电脑开始。您可以在安装了 .NET 8 SDK 的 Windows、macOS 或 Linux 系统上进行开发。使用您喜欢的任何现代 IDE:

  • Visual Studio 2022 或更高版本;
  • Visual Studio Code;
  • JetBrains Rider。

创建应用程序 

安装 DotNetBrowser 模板 

DotNetBrowser 提供了模板,可以帮助您快速设置一个带有 DotNetBrowser 的新 Avalonia 应用程序。

在您的开发环境中,打开命令行,并安装 DotNetBrowser 模板:

dotnet new install DotNetBrowser.Templates

获取试用许可证 

在创建新项目之前,您需要获得 DotNetBrowser 许可证。DotNetBrowser 是一个商业库。要对其进行评估,您可以申请一个免费的 30 天试用许可证。

填写表单即可获取,许可证密钥会发送至您的邮箱。

Spinner

发送中。。。

抱歉,发送中断

请再次尝试,如果问题仍然存在,请联系我们 info@teamdev.com.

阅读并同意条款以继续。

您的个人 DotNetBrowser 试用密钥和快速入门指南将在几分钟内发送至您的电子邮箱。

生成锁定浏览器应用 

使用 dotnetbrowser.avalonia.app 模板生成一个包含所有必要依赖项的 Avalonia 入门项目。运行以下命令,从 RaspberryKiosk 文件夹中的模板创建项目:

dotnet new dotnetbrowser.avalonia.app -o RaspberryKiosk -li <your_license_key>

默认情况下,该项目目标框架为 net8.0。您可以使用 -f 标志将目标框架更改为 net9.0net7.0net6.0

dotnet new dotnetbrowser.avalonia.app -o RaspberryKiosk -li <your_license_key> -f net9.0

配置应用程序窗口 

项目模板已经创建了一个可工作的 Avalonia 窗口,其中嵌入了 DotNetBrowser 控件,并在代码后台中处理了其初始化。接下来需要将此窗口适配为自助服务终端使用。

由于该应用将在自助服务终端上运行,它必须以全屏方式打开、始终置顶,并防止用户最小化或关闭它。在 MainWindow.axaml 文件中,将主窗口配置为:

  • 无边框,
  • 全屏,
  • 无窗口装饰,
  • 或任务栏访问。

MainWindow.axaml 文件中添加如下属性:

<Window
    ... 
    WindowState="FullScreen"
    Topmost="True"
    ExtendClientAreaToDecorationsHint="True"
    ExtendClientAreaChromeHints="NoChrome"
    ShowInTaskbar="False"
>
    <app:BrowserView x:Name="BrowserView"/>
</Window>

配置 Web 视图 

接下来启动 Chromium 引擎,并针对 Raspberry Pi 调整部分运行参数。这里有两个重点设置:

  • HardwareAccelerated 渲染模式 — 让 Chromium 直接使用 GPU 绘图,速度更快、CPU 占用更低。
  • 禁用捏合缩放 (--disable-pinch) — 关闭捏合缩放功能,以防止用户意外更改页面缩放比例。
MainWindow.xaml.cs
public partial class MainWindow : Window
{
    private readonly IBrowser browser;
    private readonly IEngine engine;

    public MainWindow()
    {          
        // 启动 Chromium 进程。
        EngineOptions engineOptions = new EngineOptions.Builder
        {
            LicenseKey = "",
            RenderingMode = RenderingMode.HardwareAccelerated,
            ChromiumSwitches =
            {
                "--disable-pinch",
            }
        }.Build();
        engine = EngineFactory.Create(engineOptions);

        // 创建 Browser 实例。
        browser = engine.CreateBrowser();

        // 初始化 Avalonia 组件。
        InitializeComponent();

        // 初始化 BrowserView 控件。
        BrowserView.InitializeFrom(browser);

        // 禁用上下文菜单。
        browser.ShowContextMenuHandler = null;

        // 导航到任意页面以确保其正常工作。
        browser.Navigation.LoadUrl("https://google.com");
     }
 }

在 Avalonia 全屏窗口中显示网页。

在 Avalonia 全屏窗口中显示网页

创建调试关闭按钮 

由于自助服务终端在全屏模式下运行且没有系统控件,在开发过程中没有直接的方法来关闭应用程序。为了使调试更方便,您可以添加一个工具调试栏,它只在调试构建时出现。

调试模式下自动添加的工具栏。

调试模式下自动添加的工具栏

以下脚本会创建一个固定的屏幕按钮,该按钮始终可见,并在点击时关闭应用程序:

MainWindow.xaml.cs
browser.navigation.FrameDocumentLoadFinished += (s, e) => 
{
#if DEBUG
    IFrame frame = e.Frame;

    IJsObject window = 
        args.Frame.ExecuteJavaScript<IJsObject>("window").Result;
    // 添加关闭应用程序的函数。
    window.Properties["closeApplication"] = 
         () => Dispatcher.UIThread.InvokeAsync(Close);

    // 添加带有关闭链接的调试栏。
    browser.MainFrame.ExecuteJavaScript<>(@" 
        (() => {
          const bar = document.createElement('div');
          Object.assign(bar.style, {
            position: 'fixed',
            top: '0',
            left: '0',
            width: '100%',
            padding: '5px 0 5px 0px',
            background: 'pink',
            textAlign: 'center',
            zIndex: '9999'
          });
          bar.innerHTML = 
             'DEBUG MODE. <a onclick="closeApp()">Close application.</a>';
          document.body.appendChild(bar);
        })();
    ");
#else
#endif
});

这样,发布版本仍然保持锁定状态,但您仍然可以方便地在测试期间退出应用程序。

添加网页 

在树莓派(Raspberry Pi)上,有几种方法可以将网页加载到 DotNetBrowser 中,具体取决于您的自助服务终端访问其内容的方式:

  • 加载远程 URL。 最简单的选项,适用于保持在线的自助服务终端。
  • 运行本地 Web 服务器。 适用于离线设置,允许浏览器加载来自同一设备的页面。
  • 打开本地 HTML 文件。 对于简单情况,您可以使用 file:// 协议,但 Chromium 限制本地访问,因此它不适用于复杂的应用。
  • 从应用程序资源提供页面。 您可以使用 DotNetBrowser 自定义协议拦截器 直接从应用程序资源提供页面。

配置运行时和部署 

该应用程序需要 .NET 运行时,而 Linux 系统通常不会默认安装它。因此,您可以不安装运行时,而是将应用程序发布为一个包含 .NET 运行时的独立构建包。

RaspberryKiosk/RaspberryKiosk.csproj 文件中,添加运行时标识符 linux-arm64,并将项目设置为自包含项目:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <RuntimeIdentifiers>win-x64;linux-arm64</RuntimeIdentifiers>
    <SelfContained>true</SelfContained>
    <PublishSingleFile>true</PublishSingleFile>
  </PropertyGroup>
</Project>

此配置会将完整的 .NET 8 运行时打包在应用程序中,并将其发布为一个单文件可执行程序。

发布应用程序 

为了让应用程序在 Raspberry Pi 上运行,需要将其发布为 Linux ARM64 平台版本,可使用以下命令:

dotnet publish RaspberryKiosk/RaspberryKiosk.csproj \
       -c Release -r linux-arm64 --self-contained true -o ./publish/rpi

命令执行后,会在 ./publish/rpi 目录生成可直接在 Raspberry Pi 5 上运行的文件。

配置 Raspberry Pi 

为了保持系统高效和稳定,自助服务终端将运行在 Raspberry Pi OS Lite 上 —— 仅包含图形功能所需的组件。您将使用 X 服务器和一个最小化窗口管理器,而不是安装完整的桌面环境。

X 服务器是 Linux 中负责提供图形输出并处理键盘和鼠标等输入设备的部分。它本身不绘制窗口,只是管理显示界面。窗口管理器位于其之上,控制窗口的显示方式和行为 —— 位置、焦点、边框和堆叠顺序。

这种极简的设置可以防止用户离开自助服务终端应用程序,并避免桌面环境带来的额外软件开销。

安装操作系统 

按照官方指南在您的设备上安装 Raspberry Pi OS Lite 64 位版本。它可在官方 Raspberry Pi Imager 镜像烧录工具中找到:

官方 Imager 中的 Raspberry Pi OS Lite。

官方 Imager 中的 Raspberry Pi OS Lite

安装系统依赖项 

要在 Raspberry Pi OS Lite 上运行带有 DotNetBrowser 的 Avalonia 应用,需要安装 X 服务器和 OpenBox(一个轻量级窗口管理器):

sudo apt update && sudo apt upgrade -y
sudo apt install --no-install-recommends \
     xserver-xorg xinit openbox gldriver-test

gldriver-test 包用于在 X 服务器中配置 GPU usage。

将您的 Raspberry Pi 用户添加到 tty 用户组,以便 X 服务器可以启动:

sudo useradd -G tty username

安装 Chromium 依赖项 

DotNetBrowser 需要几个 Chromium 依赖项。使用以下命令安装它们:

sudo apt install libgtk2.0-0 \
        libgtk-3-0 \
        libgbm1 \
        libnotify4 \
        libnss3 \
        libxss1 \
        libasound2 

部署应用程序 

现在,您可以将发布的应用程序从开发机器复制到 Raspberry Pi。您可以使用 USB 闪存盘,或通过 scp 命令进行复制:

scp -r ./publish/rpi username@<your-host-name>:~/

在 Raspberry Pi 上,为该文件赋予可执行权限:

cd rpi
chmod +x RaspberryKiosk

现在您可以启动应用了。在 ~/rpi 目录中,使用 startx 命令在 X 服务器会话中运行它:

startx ./RaspberryKiosk

配置自动启动 

一个标准的自助服务终端应用应该在启动后自动运行。为此,可以在主目录中创建一个名为 start-kiosk.sh 的简单脚本,用于启动应用:

#!/bin/bash
cd ~/rpi
startx ./RaspberryKiosk

并为其赋予可执行权限:

chmod +x ~/start-kiosk.sh

接下来,配置系统在启动时运行您的脚本。为此,需要将以下代码片段添加到 ~/.bashrc 文件的末尾:

if [ "$(tty)" = "/dev/tty1" ]; then
  ~/start-kiosk.sh
fi

最后,设置自动登录。打开 raspi-config 工具:

sudo raspi-config

在该工具中,选择 “System Options”(系统选项) > “Auto Login”(自动登录)。在对话框中,选择 “Yes” 并按 Enter 保存更改。然后,选择 “Finish”(完成) 退出。

启动自助服务终端 

现在,您可以重启 Raspberry Pi,自助服务终端应用将在启动时自动运行。

在 Raspberry Pi 5 上启动的带有 Avalonia 和 DotNetBrowser 的自助服务终端应用

在 Raspberry Pi 5 上启动的带有 Avalonia 和 DotNetBrowser 的自助服务终端应用

结论 

通过在 Raspberry Pi 上结合使用 Avalonia 与 DotNetBrowser,您可以获得一个轻量级且稳定可靠的自助服务终端系统。您可以完全控制硬件与软件,使资源占用保持在较低水平,并可以让应用在系统启动时自动运行。由于 UI 与部署过程相互独立,更新也可以轻松推送。

该自助服务终端可以加载本地 HTML 文件,也可以显示远程网页,因此您可以从单一控制点管理所有展示内容。这是一个适用于数字标牌、数据仪表板或任何需要自动展示最新内容的屏幕的简单高效方案。