Avalonia 11.1 已知問題 應用啟動時 PointToScreen 無法獲取正確座標

lindexi發表於2024-08-10

本文記錄 Avalonia 11.1 版本的已知問題,在 Linux 上使用 X11 時,在應用啟動時,即使在 Loaded 或 Activated 事件裡,都無法使用 PointToScreen 獲取到正確的螢幕座標,只會將傳入的點作為返回值

此問題已經報告給 Avalonia 官方,請看 https://github.com/AvaloniaUI/Avalonia/issues/16622

如以下程式碼所示

    public MainWindow()
    {
        InitializeComponent();
        Loaded += MainWindow_Loaded;
        Activated += MainWindow_Activated;
    }

    private void MainWindow_Activated(object? sender, EventArgs e)
    {
        var pointToScreen = this.PointToScreen(new Point(0, 0));
        Console.WriteLine($"MainWindow_Activated PointToScreen={pointToScreen}");
    }

    private void MainWindow_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
    {
        var pointToScreen = this.PointToScreen(new Point(0, 0));
        Console.WriteLine($"MainWindow_Loaded PointToScreen={pointToScreen}");
    }

將以上程式碼執行在 X11 上,將無法在 Loaded 或 Activated 事件裡使用 PointToScreen 獲取到正確的螢幕座標

執行以上程式碼在 X11 上將會在控制檯有以下資訊

MainWindow_Loaded PointToScreen=0, 0
MainWindow_Activated PointToScreen=0, 0

如果此時在 MainWindow_Loaded 新增 Task.Delay 一秒即可拿到正確的螢幕座標

    private async void MainWindow_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
    {
        var pointToScreen = this.PointToScreen(new Point(0, 0));
        Console.WriteLine($"MainWindow_Loaded PointToScreen={pointToScreen}"); // It can not get the correct coordinates here!

        await Task.Delay(1000);

        pointToScreen = this.PointToScreen(new Point(0, 0));
        Console.WriteLine(pointToScreen); // It can get the correct coordinates.
    }

以上問題我在 UOS 統信系統和 Kylin 麒麟系統上都進行測試,且透過分析程式碼可以瞭解到此問題與系統沒有相關性。即不是 UOS 統信系統和 Kylin 麒麟系統挖的坑

此問題原因是在 Avalonia 裡面依賴當前視窗座標進行 PointToScreen 的計算,而座標是在 X11 的 ConfigureNotify 事件裡面更新的,這就意味著在視窗 Loaded 或 Activated 事件裡還沒有完成座標的更新,從而導致無法正確計算螢幕座標

由於視窗座標更新將會觸發 PositionChanged 事件,如果想要規避此問題,可以將在 Loaded 事件執行的 PointToScreen 方法嘗試更改為 PositionChanged 執行,如下面程式碼

    public MainWindow()
    {
        InitializeComponent();
        PositionChanged += MainWindow_PositionChanged;
    }

    private void MainWindow_PositionChanged(object? sender, PixelPointEventArgs e)
    {
        var pointToScreen = this.PointToScreen(new Point(0, 0));
        Console.WriteLine($"PositionChanged PointToScreen={pointToScreen}");
    }

必須說明的是 PositionChanged 和 Loaded 是完全不相同的時機,還請大家根據自己的業務進行修改

本文程式碼放在 githubgitee 上,可以使用如下命令列拉取程式碼。我整個程式碼倉庫比較龐大,使用以下命令列可以進行部分拉取,拉取速度比較快

先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 7036c43bcea5d9057dcddfea7ff3ef7aae84dc07

以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼,將 gitee 源換成 github 源進行拉取程式碼。如果依然拉取不到程式碼,可以發郵件向我要程式碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 7036c43bcea5d9057dcddfea7ff3ef7aae84dc07

獲取程式碼之後,進入 AvaloniaIDemo/JejanayaYemjergayle 資料夾,即可獲取到原始碼

更多 Avalonia 相關部落格,請參閱 部落格導航

相關文章