Eto.Formsの安定したGUI操作(GTK対策)

これは、C# Advent Calendar 2017への投稿でもあります。24日が未投稿のままだったのでいただいちゃいました。

Eto.FormsでGUIアプリケーションを書いていると、個人的な範囲だと、Windowsだと問題ないのにLinux(GTK)だと挙動(特に初期設定)がおかしい、という場面に割とあたります。Shownイベントに書いてもLoadCompleteイベントでもだめ、とか。

なので、力づくな対応策を考えてみました。「フォームが見えたら幅と高さが正の値になる」という前提で、それまでUITimerで待つというものです。とりあえず手元(Windows, Linux)では正常に動いてます。

(2017-12-30改訂:コードを少し整理しました。挙動はほぼ同様です)

public class MyEtoForm : Form
{
   #region ===== Prepared detection =====

    public bool IsPrepared
    {
        get { return _isPrepared; }
        set
        {
            if (value == _isPrepared) return;
            if (value == true)
            {
                Prepared?.Invoke(this, EventArgs.Empty);
                if (showCheckTimer.Started) { showCheckTimer.Stop(); }
            }
            _isPrepared = value;
        }
    }
    private bool _isPrepared = false;

    public event EventHandler<EventArgs> Prepared;

    UITimer showCheckTimer;
    int initPreparedCounter = 0;

    void InitPreparedDetection()
    {
        if (showCheckTimer == null) {
            showCheckTimer = new UITimer { Interval = 0.05 };
            showCheckTimer.Elapsed += (s, e) => { CheckPrepared(null,EventArgs.Empty); };
        }
        Shown += CheckPrepared;
    }

    void OnShown(object s, EventArgs e)
    {
        if (IsDisplayed)
        {
            IsPrepared = true;
        }
        else
        {
            if (showCheckTimer.Started == false)
            {
                initPreparedCounter = 0;
                showCheckTimer.Start();
            }
        }
    }

    void CheckPrepared(object s, EventArgs e)
    {
        if (IsDisplayed)
        {
            if (initPreparedCounter < 1)
            {
                initPreparedCounter++;
            }
            else
            {
                IsPrepared = true;
            }
        }
    }

    bool IsDisplayed
    {
        get { return (Width > 0 && Height > 0); }
    }

   #endregion

    public MyEtoForm()
    {
        InitPreparedDetection();
    }

}

このMyEtoFormを継承します。

  • GUIを操作する準備ができたか示すプロパティ IsPrepared
  • GUIを操作する準備ができたら発生するイベント Prepared (引数はただのEventArgs.Empty)

この2つがメンバに加わるので、ご自由にご利用ください。

なお、macOSでの動作は確認していません。申し訳ないです。