用WindowsAppSDK(WASDK)优雅的开发上位机应用

C#开发上位机应用的一些选择

如果你不想看介绍,可以直接跳到优雅开发示例那里。

1. WASDK(WinUI 3)

Windows 应用 SDK 是一组新的开发人员组件和工具,它们代表着 Windows 应用开发平台的下一步发展。 Windows 应用 SDK 提供一组统一的 API 和工具,可供从 Windows 11 到 Windows 10 版本 1809 上的任何桌面应用以一致的方式使用。

Windows 应用 SDK 不会用 C++ 替换 Windows SDK 或现有桌面 Windows 应用类型,例如 .NET(包括 Windows 窗体和 WPF)和桌面 Win32。 相反,Windows 应用 SDK 使用一组通用 API 来补充这些现有工具和应用类型,开发人员可以在这些平台上依赖这些 API 来执行操作。 有关更多详细信息,请参阅Windows 应用 SDK 的优势。

这个WASDK目前是微软主推的开源的,UI部分是结合了WinUI 3。

2. WPF

欢迎使用 Windows Presentation Foundation (WPF) 桌面指南,这是一个与分辨率无关的 UI 框架,使用基于矢量的呈现引擎,构建用于利用现代图形硬件。 WPF 提供一套完善的应用程序开发功能,这些功能包括 Extensible Application Markup Language (XAML)、控件、数据绑定、布局、二维和三维图形、动画、样式、模板、文档、媒体、文本和版式。 WPF 属于 .NET,因此可以生成整合 .NET API 其他元素的应用程序。

目前WPF也已经开源,而且整体上更为成熟,Visual Studio就是WPF 4.x开发的,生态也比较好。

3. WinForms

欢迎使用 Windows 窗体的桌面指南,Windows 窗体是一个可创建适用于 Windows 的丰富桌面客户端应用的 UI 框架。 Windows 窗体开发平台支持广泛的应用开发功能,包括控件、图形、数据绑定和用户输入。 Windows 窗体采用 Visual Studio 中的拖放式可视化设计器,可轻松创建 Windows 窗体应用。

这个也是开源的,Winform算是上手即用的开发框架了,通过拖拉拽可以很轻松的创建出UI和编写对应的功能,对于UI美观程度不太重要的工业领域,这个用来做工具开发很简单,上手也容易。

4. UWP

UWP 是创建适用于 Windows 的客户端应用程序的众多方法之一。 UWP 应用使用 WinRT API 来提供强大的 UI 和高级异步功能,这些功能非常适用于 Internet 连接的设备。

微软对于UWP,只能说曾经爱过,当初UWP可是当红炸子鸡,号称跨windows全平台,不过现在也是跨windows全平台,可惜没搞好,不过虽然不够受重视,但是一时半会还是死不掉,毕竟WASDK还不够成熟。

为什么选择WASDK

通过上面的介绍,大家对于windows下的原生UI开发框架应该有了一些了解,如果抛开语言限制的话还有更多的选择,比如QT,各种前端的跨平台,像微软自己家的MAUI什么的,我之前还写了一篇WinUI迁移到即将”过时”的.NET MAUI个人体验

最近的微软Windows App SDK 1.1版本发布了,意味着BUG应该少了很多,也可以正式的在一些项目中使用了。通过官方的WinUI库,我们可以轻松的构建符合Win11设计规范的UI,由于UWP的种种问题,WPF和WinForms又是只开源,应该不会有大的新特性了,外加本人以前也经常玩玩UWP,通过前景和自己的喜好,肯定是选择WASDK了。

优雅开发示例

1. 做一个上位机应用

上位机示例图
上图为应用的展示图,采用的WASDK1.1版本开发,目前已经上架了Windows商店,打包方式为MSIX,目前x64和arm64是分开的MSIX包,文档里提到可以多个MSIX包合成一个集合包,不过我采用上传多个包,让商店自动匹配。

此应用是为稚晖君的ElectronBot开发的第三方的上位机,名字就叫电子脑壳。下图是效果图展示,结合Surface平板,触摸体验良好,个人感觉很优雅。

实物控制

B站演示视频

2. 整体的开发步骤

ElectronBot本身连接电脑采用的是libusb生成的驱动吧,这个不知道叙述的是否正确。

看下图大体能明白电脑和ElectronBot通过高速USB进行连接,当我们驱动安装成功就可以进行操作了。

img

电子脑壳应用=>ElectronBot.DotNet SDK=>LibUsbDotNet

底层调用采用的是LibUsbDotNet这个库进行底层数据传输的操作,我根据稚晖君提供的c++版本的sdk进行了封装。

目前c#版本的SDKElectronBot.DotNet是开源的,demo示例也是windowsAppSDK的,大家感兴趣的可以star一下。

开始创建项目前最好安装下Template Studio for WinUI

img

2.1 创建项目

img

选择模板进行创建,可以根据需要进行选择,本人选择如下。
img

由于ElectronBot .Net SDK本身已经开源,直接以上位机主体应用做讲解。下图为应用的依赖项,主要包含SDK和OpenCV相关的nuget包。

应用依赖项

应用整体不复杂,通过.Net框架自带的DI容器进行对象生命周期的管理,通过MVVM进行数据的绑定和更新。

结合Win2D和OpenCV进行图形数据处理,然后通过SDK写入到usb设备里进行控制和展示。

软件整体的实现逻辑

2.2 关键点代码讲解

下面的代码是通过切换Combox事件,动态创建不同的表盘并绑定到MainWindows的控件上。

private ICommand _clockChangedCommand;
public ICommand ClockChangedCommand => 
    _clockChangedCommand ?? (_clockChangedCommand = new RelayCommand(ClockChanged));

private async void ClockChanged()
{
    var clockName = _clockComboxSelect.DataKey;

    if (!string.IsNullOrWhiteSpace(clockName))
    {
        var viewProvider = _viewProviderFactory.CreateClockViewProvider(clockName);

        Element = viewProvider.CreateClockView(clockName);
    }

    await Task.CompletedTask;
}

public UIElement Element
{
    get => _element;
    set => SetProperty(ref _element, value);
}

xaml代码如下。

img

通过此操作,能够正常显示表盘,数据刷新也能正常使用。

当切换到时钟模式的时候,另外一个定时器会定时抓取表盘并将xaml转化成图片进行传输,主要涉及到Win2D库的使用,代码如下。

if (_electron.Connect())
{
    var bitmap = new RenderTargetBitmap();
    await bitmap.RenderAsync(Element);
    var pixels = await bitmap.GetPixelsAsync();

    // Transfer the pixel data from XAML to Win2D for further processing.
    using CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    using CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromBytes(
        canvasDevice, pixels.ToArray(), bitmap.PixelWidth, bitmap.PixelHeight, 
        Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized);

    using IRandomAccessStream stream = new InMemoryRandomAccessStream();

    await canvasBitmap.SaveAsync(stream, CanvasBitmapFileFormat.Png);

    Bitmap image = new Bitmap(stream.AsStream());

    var mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(image);

    var mat1 = mat.Resize(new OpenCvSharp.Size(240, 240), 0, 0, OpenCvSharp.InterpolationFlags.Area);

    var mat2 = mat1.CvtColor(OpenCvSharp.ColorConversionCodes.RGBA2BGR);

    var dataMeta = mat2.Data;

    var data = new byte[240 * 240 * 3];

    Marshal.Copy(dataMeta, data, 0, 240 * 240 * 3);

    await Task.Run(() =>
    {
        if (_electron.Connect())
        {
            _electron.SetImageSrc(data);

            _electron.Sync();
        }
    });


}

上面代码通过RenderTargetBitmap和Win2D将Xaml元素转化成CanvasBitmap,然后再通过OpenCV将canvasBitmap转化成下位机可识别的字节数组,通过SDK进行传输到下位机。

整体的开发过程和UWP很相似,UI部分用到的很多API都是UWP的改名版本,上位机目前没有开源,所以只能截取部分代码进行讲解了,如果想交流大家可以评论区见。

3. 遇到的一些问题

目前Windows App SDK有一些BUG,在我使用的过程中主要发现使用WinRT的串口监听事件失效,已在github提了bug,回头应该能够修复,还有WinRT里的一些API只认UWP UI Api windows.UI开头的一些对象,还需要大家多使用多反馈,这样WASDK开发才能良性循环。

public async Task InitAsync()
{
    // Target all Serial Devices present on the system
    var deviceSelector = SerialDevice.GetDeviceSelector();

    var myDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(deviceSelector);

    deviceWatcher = DeviceInformation.CreateWatcher(deviceSelector);

    deviceWatcher.Added += new TypedEventHandler<DeviceWatcher, DeviceInformation>(this.OnDeviceAdded);
    deviceWatcher.Removed += new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>(this.OnDeviceRemoved);

}

上面代码注册的事件,在目前1.1版本的WASDK不生效,官方已经标注为BUG,当然在UWP里就正常,UWP在有些时候还是挺靠谱的嘛。

个人总结感悟

通过这个上位机应用的开发,也是对WASDK和UWP相关技术的使用能熟练一些了,从WPF到UWP再到WASDK和MAUI,XAML相关的开发都是可继承的,开发方式很相似,对于技术的迁移来说也算是没什么障碍吧,经常会听到很多人说微软出了这么多技术,都学不动了什么的,其实大家掌握内涵,对于新技术的接受还是很快的。

特别鸣谢以及参考推荐文档

感谢dino.c大佬的一个番茄钟,因为我的表盘其实就是抄他番茄钟的代码。

感谢h哥火火给的一些思路。

当然还要感谢超超,毕竟有些代码还是抄他的。

参考推荐文档如下

一个番茄钟

Win2D samples

opencvsharp

WindowsAppSDK

WindowsCommunityToolkit

ElectronBot

ElectronBot.DotNet

LibUsbDotNet

Tags: