停止更新,整理中,參考另一個帖子。
Avalonia開發筆記(2023/12/7更新) - wzwyc - 博客園
https://www.cnblogs.com/wzwyc/p/17578218.html
官網:
源碼:
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>