当前位置: 代码网 > it编程>编程语言>Asp.net > 分享下最近基于Avalonia UI和MAUI写跨平台时间管理工具的体验

分享下最近基于Avalonia UI和MAUI写跨平台时间管理工具的体验

2024年06月04日 Asp.net 我要评论
起因 几个月前,我在寻找一款时间管理软件,类似番茄时钟的工具,但是希望可以自定义时间。 需要自定义的场景 做雅思阅读,3篇文件需要严格控制时间分配,需要一个灵活的计时器 定期提醒,每30分钟需要喝水或者上个厕所或者摸一下鱼... 总结起来就是:专注一段时间,比如30分钟,然后休息10分钟,且没有杂七 ...

起因

几个月前,我在寻找一款时间管理软件,类似番茄时钟的工具,但是希望可以自定义时间。

需要自定义的场景

  1. 做雅思阅读,3篇文件需要严格控制时间分配,需要一个灵活的计时器
  2. 定期提醒,每30分钟需要喝水或者上个厕所或者摸一下鱼...

总结起来就是:专注一段时间,比如30分钟,然后休息10分钟,且没有杂七杂八的功能。
理论上有的番茄时钟也能满足需求,但是我的需求是:

  • 界面尽可能的简洁。
  • 免费使用且最好是开源的。
  • 可以自定义时间。
  • 最好能跨平台,因为有时候是在macos下使用,有时候又是在windows上。
    但就其中部份条件还好,完全符合的竟然没符合我需求的。

在apple store找到一个比较接近需求的一款,叫itimer, 非常简洁好用,但是自定义时间需要内购,且只能在macos下。

于是我在使用的时候就想,这软件功能极简,就几个页面,为什么我不自己做一个能。 于是每次利用一点时间空隙我就写一部份,一开始是选型maui,然后中途切换成avalonia,最后基本完成了这个简易的版本。这里记录下开发心得
结论是:
代码都是c# + xaml,没有很复杂的逻辑和代码,新手完全可以轻松写一个日常使用的ui tool。

代码放在github,也没啥技术含量,有需要的自取
https://github.com/hoyho/itimeslot/tree/main

暂时没有发布二进制文件
需要的自己用git 克隆下来,然后dotnet build 或者dotnet publish即可

成品预览

macos下使用默认主题:

使用material theme

windows和linux (使用xfce 桌面)

其他杂七杂八的需求
弹窗, 托盘等

就目前而言,基本能满足我的需求了。

谈谈体验

why choose maui

一开始,觉得是微软官方出的框架,应该不会有啥大坑吧,于是看了下官方介绍,文档的demo

  • 可以ios, android,macos, windows, looks good
  • 不同平台的ui实现不一样,比如在windows上是winui,在macos上则是mac catalyst, 即uikit, appkit平台开放的api等等, 看起来还挺好看的😶
  • 文档也很清晰,至少比avalonia的清晰
    就哼哧哼哧地把环境配置,然后写了个hello world.
    也就是这个

我是在macos开发的,按照文档来就好,
https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?view=net-maui-8.0&tabs=visual-studio-code

相比windows下的visual studio,使用vs code来开发而且还要
macos 的开发套件
xcode-select --install
中间错了个错误,具体什么错误忘记了,后来加上sudo执行就ok了

持续踩坑

组件picker 在macos下没有默认值,需要点击后才能正常显示

app设置倒计时需要设置一个时间段,于是选择了人picker组件,然而测试下来,在macos下运行时,即使绑定了一组数据后,
组件默认是没有选择上的,而是点击了done按钮后才能正常选择,具体同 https://github.com/dotnet/maui/issues/10208
显然是一个bug。。。

后来解决办法: 窗体初始化的时候主动设置一个selectedindex来触发变更,从而绑定上数据源

macos 上有办法实现关闭窗口后不退出

本来期望是设置了之后,点击关闭按钮能hook住关闭事件,然后继续后台运行,这在传统的winform或者 gtk框架都能轻松实现
然而maui的设计似乎更倾向于移动端也就ios和android的生命周期,点击即关闭。

好吧,也不是不能用.

无法实现托盘后台运行

还是macos下,暂时也没找到原生的方式实现托盘后台运行,并支持右键菜单
经过一番挣扎,找到了官方的一个demo有类似的实现。
但是不是原生支持,而是通过调用object-c语言绑定,通过动态链接库来调用macos提供接口objc_msgsend,然后访问系统提供的接口来实现比如这个nsstatusbar
这是一个完整的在macos下,trayservic实现,来欣赏下:

using system.runtime.interopservices;
using foundation;
using objcruntime;
using weathertwentyone.services;

namespace weathertwentyone.maccatalyst;

public class trayservice : nsobject, itrayservice
{
    [dllimport("/usr/lib/libobjc.dylib", entrypoint = "objc_msgsend")]
    public static extern intptr intptr_objc_msgsend_nfloat(intptr receiver, intptr selector, nfloat arg1);

    [dllimport("/usr/lib/libobjc.dylib", entrypoint = "objc_msgsend")]
    public static extern intptr intptr_objc_msgsend_intptr(intptr receiver, intptr selector, intptr arg1);

    [dllimport("/usr/lib/libobjc.dylib", entrypoint = "objc_msgsend")]
    public static extern intptr intptr_objc_msgsend(intptr receiver, intptr selector);

    [dllimport("/usr/lib/libobjc.dylib", entrypoint = "objc_msgsend")]
    public static extern void void_objc_msgsend_intptr(intptr receiver, intptr selector, intptr arg1);

    [dllimport("/usr/lib/libobjc.dylib", entrypoint = "objc_msgsend")]
    public static extern void void_objc_msgsend_bool(intptr receiver, intptr selector, bool arg1);

    nsobject systemstatusbarobj;
    nsobject statusbarobj;
    nsobject statusbaritem;
    nsobject statusbarbutton;
    nsobject statusbarimage;

    public action clickhandler { get; set; }

    public void initialize()
    {
        statusbarobj = runtime.getnsobject(class.gethandle("nsstatusbar"));
        systemstatusbarobj = statusbarobj.performselector(new selector("systemstatusbar"));
        statusbaritem = runtime.getnsobject(intptr_objc_msgsend_nfloat(systemstatusbarobj.handle, selector.gethandle("statusitemwithlength:"), -1));
        statusbarbutton = runtime.getnsobject(intptr_objc_msgsend(statusbaritem.handle, selector.gethandle("button")));
        statusbarimage = runtime.getnsobject(intptr_objc_msgsend(objcruntime.class.gethandle("nsimage"), selector.gethandle("alloc")));

        var imgpath = system.io.path.combine(nsbundle.mainbundle.bundlepath, "contents", "resources", "platforms", "maccatalyst", "trayicon.png");
        var imagefilestr = nsstring.createnative(imgpath);
        var nsimageptr = intptr_objc_msgsend_intptr(statusbarimage.handle, selector.gethandle("initwithcontentsoffile:"), imagefilestr);

        void_objc_msgsend_intptr(statusbarbutton.handle, selector.gethandle("setimage:"), statusbarimage.handle);
        void_objc_msgsend_bool(nsimageptr, selector.gethandle("settemplate:"), true);

        // handle click
        void_objc_msgsend_intptr(statusbarbutton.handle, selector.gethandle("settarget:"), this.handle);
        void_objc_msgsend_intptr(statusbarbutton.handle, selector.gethandle("setaction:"), new selector("handlebuttonclick:").handle);
    }

    [export("handlebuttonclick:")]
    void handleclick(nsobject senderstatusbarbutton)
    {
        var nsapp = runtime.getnsobject(class.gethandle("nsapplication"));
        var sharedapp = nsapp.performselector(new selector("sharedapplication"));

        void_objc_msgsend_bool(sharedapp.handle, selector.gethandle("activateignoringotherapps:"), true);

        clickhandler?.invoke();
    }
}

本来apple的文档就不咋滴,看到这一坨彻底是震惊到了,居然还要熟悉苹果的那一套api才能搞得定,且不说这代码可读性和健壮性以及维护成本
不过改改也能用,,,

macos发送通知无法弹出

原生的接口似乎只找到displayalert 和toasts
勉强凑活着用吧
然而窗口非置顶的情况也就是程序没有获得焦点的情况下,通知窗口压根就不会弹出,也就是通知了也看不到,几乎半残
一个对时间管理敏感的程序,到时间了还弹不出通知,那要来何用。。。

于是在完成某一次commit之后,我在思考,趁着现在还没完成开发,切换成avalonia是否还来得及

答案是肯定的

切换到avalonia并不困难,在同目录先用新名字初始化一个空的avalonia项目,把关键代码复制改改基本上一两个小时的就完成迁移

踩坑avalonia

其实还好,
由于avalonia的ui都是自绘的,有时候看着美观性还差那么点意思,但是不影响
真正的遇到的问题是有一个版本存在内存泄漏,折腾了好久,以为是自己的代码哪里没处理好,导致的泄漏
终于在某一天看到官网的更新日志,修复了一个内存泄漏的问题,于是更新版本后神奇地修复了,大喜

换成avalonia后基本上linux端也能用了,似乎没啥大毛病

其他体验

善用mvvm模式

虽说前期用maui 折腾了一会,但是真正回顾下,切换到avalonia后感觉上手真的非常快。
无论maui还是avalonia都是推荐mvvm开发模式,熟用绑定,基本上每个页面都比较清晰。
尽管这里还是部份就在code behind把逻辑就写了。。。(反面教程)

c# 开发效率

这里用的是vs code, 在macos下开发,偶尔搭配下rider,目前为止还是比较丝滑。没有遇到大坑
虽然不足windows + vs 无敌,但是满足日常使用,即使换到linux也能继续

编译文件:
在macos打包成img 也不过是一百多m, 在windows 和linux只需几十m,而且可以打包成单个文件。
也可以aot编译,简直就是秒开,相比electron 之类的,还是有不错的优势。

运行效率

maui的忘记对比资源占用了。
最后的版本,在windows 内存基本在六七十m,比较合理,
在macos和linux下稍微多一点大概80-100m之间,也能接受

结论

maui 比较倾向移动端, 用来开发桌面软件,还是一言难尽
推荐还是avalonia,两者就上手难度而言,只要用过.net的,稍微阅读下文档,其实就能把自己日常的需求开发起来,没有太大负担,值得拥有。
至于是不是重复造轮子,见仁见智

最后放上代码仓库:
虽然没啥技术含量,有兴趣的可以看看 https://github.com/hoyho/itimeslot

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com