Avalonia開發小結


停止更新,整理中,參考另一個帖子。

Avalonia開發筆記(2023/12/7更新) - wzwyc - 博客園
https://www.cnblogs.com/wzwyc/p/17578218.html

 

官網:

https://avaloniaui.net/

源碼:

https://github.com/AvaloniaUI/Avalonia

目前最新版本:11.0.5

 

 

 

 

 

系統測試情況

目前試了一下,能夠正常運行的系統,除了Windows系統外,流行的Ubuntu,Centos,Redhat這些系統應該都沒啥問題。

目前因為國產化要求,經常需要運行在銀河麒麟系統下,目前測試了沒有啥問題。

在Linux系統下運行,偶爾會因為字體的原因運行失敗,所以保險起見,建議AvaloniaUI應用都加上自字義字體的代碼。

 

 

 

常見問題解決

1、Ubuntu系統下TextBox中文顯示亂碼

貌似Avalonia必須指定一下當前窗體的字體,不然中文就是會顯示亂碼,之前是直接設置成“Microsoft YaHei”,會導致Ubuntu系統下找不到相應的字體:

private static string GetPlatformFontFamily()
{
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        return "Microsoft YaHei";
    }
    else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
    {
        return "Menlo"; //換成OSX下的中文字體
    }
    else
    {
        return "Noto Sans CJK SC";
    }
}

 

2、ToolTip的顯示

在要顯示ToolTip的控件上加上附加屬性:

ToolTip.Tip="Tip內容"

 

3、Ubuntu設置.net core程序開機自啟動

在/etc/systemd/system/ 目錄下創建.service文件。
UploadServer.service文件:

[Unit]
Description=UploadServer
After=network.target
 
[Service]
WorkingDirectory=/www/wwwroot/db.cnsos.net/UploadServer/UploadServer/bin/Debug/netcoreapp3.1
ExecStart=/usr/bin/dotnet /www/wwwroot/db.cnsos.net/UploadServer/UploadServer/bin/Debug/netcoreapp3.1/UploadServer.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
SyslogIdentifier=UploadServer

[Install]
WantedBy=multi-user.target

3、UploadServer.service無法正常啟動

 發現程序在Ubuntu的終端下用dotnet run可以正常啟動和運行,但是設置為service以后,就是無法正常使用。

通過下面的命令看了一下:

sudo journalctl -f -u FileServer.service

發現服務在不停地啟動和停止。

看了一下代碼,Main函數的未尾使用了Console.ReadLine();

換成:

while (true)
{
    Thread.Sleep(1000);
}

應該是service會自動跳過Console.ReadLine(),然后程序就結束了,然后服務本身設置了自動重啟,所以不停地停止和重啟。

3、Ubuntu系統下路徑不正常的問題

 程序在Windows系統下測試良好,但是在Ubuntu系統上卻無法正常運行,看了一下,是文件路徑的問題。

因為客戶端在Windows下運行的,客戶端上傳的路徑里的“\”需要換成“/”。寫了一個路徑轉換函數:

public static string ChangePath(string path)
{
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
    {
        path = path.Replace("\\", "/");
        if (path.StartsWith("/"))
            path = path.Substring(1);

        return path;
    }

    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        path = path.Replace("/", "\\");
        if (path.StartsWith("\\"))
            path = path.Substring(1);

        return path;
    }

    return path;
}

另外,在使用Path.Combine(path1,path2)進行路徑組合的時候,path2不能以"\"或“/”開頭,不然的話,路徑會組合失敗。

3、“Default font family name can't be null or empty.”錯誤問題

部分Linux操作系統下,能夠正常編譯,但是無法啟動應用,會報“Default font family name can't be null or empty.”的錯誤。應該是跟字體有關系。網上找了一下資料。可以參照以下網址的方法來嘗試解決。

https://www.cnblogs.com/joyandjoys/p/14346935.html

 下面是我現在用的代碼:

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using Avalonia.Platform;
using Avalonia.Skia;
using SkiaSharp;

namespace CodeKeeper
{
    public class CustomFontManagerImpl : IFontManagerImpl
    {
        private readonly Typeface[] _customTypefaces;
        private readonly string _defaultFamilyName;

        //Load font resources in the project, you can load multiple font resources
        private readonly Typeface _defaultTypeface =
            new Typeface("resm:CodeKeeper.Assets.Fonts.msyh#微軟雅黑");

        public CustomFontManagerImpl()
        {
            _customTypefaces = new[] { _defaultTypeface };
            _defaultFamilyName = _defaultTypeface.FontFamily.FamilyNames.PrimaryFamilyName;
        }

        public string GetDefaultFontFamilyName()
        {
            return _defaultFamilyName;
        }

        public IEnumerable<string> GetInstalledFontFamilyNames(bool checkForUpdates = false)
        {
            return _customTypefaces.Select(x => x.FontFamily.Name);
        }

        private readonly string[] _bcp47 =
        {
            CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName
        };

        public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
            CultureInfo culture, out Typeface typeface)
        {
            foreach (var customTypeface in _customTypefaces)
            {
                if (customTypeface.GlyphTypeface.GetGlyph((uint)codepoint) == 0)
                {
                    continue;
                }

                typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight);

                return true;
            }

            var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight,
                SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);

            typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);

            return true;
        }

        public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
        {
            SKTypeface skTypeface;

            if (typeface == null || typeface.FontFamily == null)
            {
                skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name);
            }
            else
            {
                switch (typeface.FontFamily.Name)
                {
                    case FontFamily.DefaultFontFamilyName:
                    case "微軟雅黑": //font family name
                        skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name);
                        break;
                    default:
                        skTypeface = SKTypeface.FromFamilyName(typeface.FontFamily.Name,
                            (SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal,
                            (SKFontStyleSlant)typeface.Style);
                        break;
                }
            }

            return new GlyphTypefaceImpl(skTypeface);
        }
    }
}

 

4、第三方的MessageBox.Avalonia控件有點問題,會導致應用崩潰退出。

初步判斷是因為窗口的圖標引起的,而且這個圖標不設置也不行。

最新版本已經沒有問題。

var msBoxStandardWindow = MessageBox.Avalonia.MessageBoxManager.GetMessageBoxStandardWindow(new MessageBoxStandardParams
{
    ButtonDefinitions = ButtonEnum.Ok,
    ContentTitle = "提示",
    ContentMessage = "請輸入網址",
    Icon = Icon.Info,
    Style = Style.UbuntuLinux,
    WindowStartupLocation = WindowStartupLocation.CenterOwner,
    FontFamily = "Microsoft YaHei",
});
await msBoxStandardWindow.Show(App.GetActiveWindow());

不過這個對話框控件容易出現一些問題,類似中文顯示亂碼等等問題,其實自己寫一個簡單的對話框窗體也是不錯的選擇。

 目前對話框,一般我都是自己寫。

5、Ubuntu系統下,從IDE上Debug可以正常運行,但是通過系統的桌面圖標無法打開。

試了一下,用.desktop里的路徑直接在終端里面運行,也是無法正常運行的。在終端上面輸出了一些錯誤信息。看了一下,配置文件的路徑,用的是相對路徑。

把它改成下面的形式,應用就能正常打開了。

Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Formats/AppConfig")

 

6、在部分機子上,打開OpenFileDialog時,會卡UI,軟件無響應。

在Main方法上加上 [STAThread] 的標簽。如下所示:

    internal class Program
    {
        // Initialization code. Don't use any Avalonia, third-party APIs or any
        // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
        // yet and stuff might break.
        [STAThread]
        public static void Main(string[] args) => BuildAvaloniaApp()
            .StartWithClassicDesktopLifetime(args);

        // Avalonia configuration, don't remove; also used by visual designer.
        public static AppBuilder BuildAvaloniaApp()
            => AppBuilder.Configure<App>()
                .UsePlatformDetect()
                .LogToTrace();
    }

 

7、托盤圖標的功能

示例代碼:

public override void OnFrameworkInitializationCompleted()
{
    if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
    {
        desktop.MainWindow = new MainWindow
        {
            DataContext = new MainWindowViewModel(),
        };

        var notifyIcon = new TrayIcon();
        notifyIcon.Menu ??= new NativeMenu();
        notifyIcon.ToolTipText = "ToDoList";
        var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
        notifyIcon.Icon = new WindowIcon(assets.Open(new Uri("avares://TodoList/App.ico")));
        var exit = new NativeMenuItem() { Header = "退出" };
        exit.Click += (sender, args) => Environment.Exit(0);
        notifyIcon.Menu.Add(exit);
        notifyIcon.Clicked += (sender, args) => { desktop.MainWindow.WindowState = WindowState.Normal; };
    }

    base.OnFrameworkInitializationCompleted();
}

 

7、ValueConverter功能

實現類:

using Avalonia.Data.Converters;
using Jaya.Shared;
using Jaya.Ui.Services;
using System;
using System.Globalization;

namespace Jaya.Ui.Converters
{
    public class BooleanToTreeNodeVisibilityConverter : IValueConverter
    {
        readonly SharedService _shared;

        public BooleanToTreeNodeVisibilityConverter()
        {
            _shared = ServiceLocator.Instance.GetService<SharedService>();
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (_shared.ApplicationConfiguration.IsHiddenItemVisible)
                return true;

            return !(bool)value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

定義和使用:

<UserControl.Resources>
    <c:BooleanToTreeNodeVisibilityConverter x:Key="TreeNodeVisibilityConverter" />
    <j:BitmapValueConverter x:Key="BitmapValueConverter" />
</UserControl.Resources>

<UserControl.Styles>
    <Style Selector="TreeViewItem">
        <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=OneWayToSource}" />
        <Setter Property="IsVisible" Value="{Binding FileSystemObject.IsHidden, Mode=OneWay, Converter={StaticResource TreeNodeVisibilityConverter}, FallbackValue=True}" />
    </Style>
</UserControl.Styles>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM