Avalonia学习实践(三)--一个对话框引发的惨案

跨平台开发最令人头疼的是在一个平台上能正常运行,换到另外一个平台就不行了,尤其是那些细小的功能,来回的代码修改、重新编译、发布运行着实令人崩溃。


在桌面操作系统中,对文件的选择、查找是很常见的操作,因此系统对话框算是一个桌面UI库最基本的需求了。来看看通过解决一个小小对话框的问题又学到了什么。

1.Avalonia中的系统对话框

Avalonia中的系统对话框都是派生自抽象类SystemDialog,位于Avalonia.Controls命名空间下,公共属性有Title“标题”。文件操作相关的对话框都派生自抽象类FileSystemDialog,公共属性为初始路径。
在这里插入图片描述
以打开文件对话框OpenFileDialog为例,可以设置初始文件名称、文件类型过滤器、是否可选多个文件;打开对话框的方法为ShowAsync(),输入参数为对话框的父级窗口,必填;返回参数为字符串数组,即选中文件的完整路径的集合,如果取消选择则返回null。
ShowAsync(Window parent)的实现为:

/// <summary>
/// Shows the open file dialog.
/// </summary>
/// <param name="parent">The parent window.</param>
/// <returns>
/// A task that on completion returns an array containing the full path to the selected
/// files, or null if the dialog was canceled.
/// </returns>
public Task<string[]?> ShowAsync(Window parent)
{
    
    
    if(parent == null)
        throw new ArgumentNullException(nameof(parent));
    var service = AvaloniaLocator.Current.GetService<ISystemDialogImpl>() ??
        throw new InvalidOperationException("Unable to locate ISystemDialogImpl.");
    return service.ShowFileDialogAsync(this, parent);
}

使用方法如下:

这不是最佳写法,后面再附上完整示例代码

private void SelectStationFile()
{
    
    
    var dialog = new OpenFileDialog
    {
    
    
        Title = "请选择文件"
    };
    var result = dialog.ShowAsync(Views.MainWindow.Instance);
    if (result.Result != null)
    {
    
    
        var filePath= result.Result[0];
    }
}

2.跨平台调试的痛

开发环境是Window10、AMD处理器、VisualStudio2022,测试环境是统信UOS、Windows10,CPU均有AMD5000系列和Intel i7系列。
就是打开对话框选择文件这个小小的功能,Windows环境顺畅运行,统信UOS时好时坏,大多数版本都无法正常打开对话框,点击打开对话框的按钮后,应用即进入卡死状态,甚至强制退出都不好使,只能重启电脑。
多次调整代码还是有问题,考虑问题出现的原因可能涉及几方面:

  • UI渲染库:不同的平台使用不同UI渲染库的差异;
  • MVVM模式:MVVM模式默认使用的是ReactiveUI,普通模式则没有限定;
  • UI线程:打开对话框是异步方法,不在主线程内,线程堵塞;

基于上面思路排查问题的过程中,又了解到Avalonia中日志和异常处理的内容。

3.涨知识涨经验

3.1.日志跟踪

Avalonia使用System.Diagnostics.Trace进行日志跟踪,可配置日志跟踪的级别和范围。

public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>()
        .UsePlatformDetect()
        .LogToTrace(LogEventLevel.Debug, LogArea.Property, LogArea.Layout);

也可引用第三方类库将日志输出到控制台或者文件,官方文档中使用的是Serilog

官方文档:Logging Errors and Warnings

3.2.异常处理

在Program.cs的Main方法中可以进行全局异常捕获。

[STAThread]
public static void Main(string[] args)
{
    
    
	//日志类库使用Serilog
    Log.Logger=new LoggerConfiguration().WriteTo.File("log.txt",rollingInterval:RollingInterval.Day).CreateLogger();
    try
    {
    
    
        BuildAvaloniaApp()
            .StartWithClassicDesktopLifetime(args);
    }
    catch (Exception ex)
    {
    
    
        Log.Fatal(ex, "Unhandled Exception");
    }
    finally
    {
    
    
        Log.CloseAndFlush();
    }
}
  • 多线程任务相关的异常需要安装TaskScheduler.UnobservedTaskException进行捕获。
  • ReactiveUI相关的异常需要在代码中订阅RxApp.DefaultExceptionHandler进行捕获。

官方文档:Unhandled Exceptions

3.3.访问UI线程

与WPF类似,Avalonia访问主UI线程使用Dispatcher,可以在非UI线程中访问界面对象,位于Avalonia.Threading命名空间下,用法如下:

private void SelectStationFile()
{
    
    
     var dialog = new OpenFileDialog
     {
    
    
         Title = "请选择文件"
     };
     var result = dialog.ShowAsync(Views.MainWindow.Instance);
     if (result.Result != null)
     {
    
    
         Dispatcher.UIThread.Post(() =>
         {
    
    
             _stationSourcePath = result.Result[0];
         });
     }
}

官方文档:Accessing the UI thread

4.原因竟是这样

按照前面的思路排查下来竟然还没有解决,百无聊赖之下将绑定命令调用的方法改成async异步,竟然能行了。

这是正确的代码,能够正常工作

private async void SelectClimateFile()
{
    
    
    try
    {
    
    
        var dialog = new OpenFileDialog
        {
    
    
            Title = "请选择文件"
        };
        var result = await dialog.ShowAsync(Views.MainWindow.Instance);
        if (result != null)
        {
    
    
            ClimateSourcePath = result[0];
        }

    }
    catch (Exception ex)
    {
    
    
        var message = MessageBox.Avalonia.MessageBoxManager.GetMessageBoxStandardWindow("Error", ex.Message);
        message.Show();
    }
}

回想起来,从前后端命令的定义、绑定到基类中打开对话框的实现,都是使用多线程Task,异步的操作应该是必要的。至于为什么使用同步方法时Windows环境能正常工作,Linux环境就不行的原因,还是要更深入的了解底层原理。


只有扎实的基础,才能更快看清问题的本质。

猜你喜欢

转载自blog.csdn.net/lordwish/article/details/124770136