пятница, 10 февраля 2017 г.

Интересная реализация SplashScreen'а на C# с использованием интерфейса IDisposable

В одном из проектов, которые я поддерживаю, нашел очень интересную реализацию SplashScreen'а. 

SplashScreen - это окно, которое отображается пользователю, когда приложение долгое время выполняет какую-то работу  и не может отвечать на действия пользователя.
Обычно для реализации такого окна создается второй Thread, в котором отображается SplashScreen, а в первом Thread'e выполняется вся работа. Это позволяет избавиться от эффекта "зависшего" приложения.

В данном случае реализация SplashScreen'а разделена на 2 части:
  • WaitManager.cs
  • WaitForm.cs

Код WaitManager'а выглядит следующим образом:

 public class WaitManager : IDisposable
    {
        Thread myThread;
        WaitForm frm;
        bool shown = false; 
        public bool Finished { get; private set; }
   
        public WaitManager()
        {
            Finished = false;
            myThread = new Thread(DoWork);
            myThread.Start();
        }
        void DoWork()
        {
            frm= new WaitForm(this, progressFunc);
            frm.Shown += new EventHandler(frm_Shown);
            frm.ShowDialog();
        }
        void frm_Shown(object sender, EventArgs e)
        {
            shown = true;
        }
        public void Dispose()
        {
           Finished = true;
            while (!shown || frm.Visible)
                Thread.Sleep(1);
            myThread.Abort();
        }
    }
 WaitForm - это форма, которая отобразится пользователю. На форму добавлен таймер (о нем позже) и ссылка на WaitManager, которая передается в конструктор объекта формы:

  public partial class WaitForm : Form
    {
        WaitManager manager;     
     
        private WaitForm(WaitManager manag)
        {
            InitializeComponent();
            manager = manag;
            timer = DateTime.Now;
        }
     
        private void timer1_Tick(object sender, EventArgs e)
        {         
            if (manager.Finished)
                Close();
        }

    }
Самое интересное в этой реализации - то, как легко происходит вызов SplashScreen'а:
using (new WaitManager ())
{
    DoSomeStuff();
}
Всё, что нужно сделать для отображения SplashScreen'а -  добавить одну строку кода + фигурные кавычки.
Как это работает:

  1. При создании нового экземпляра WaitManager'а создается новый поток, в котором создается и отображается новый экземпляр WaitForm'ы
  2. В когда метод DoSomeStuff() в основном потоке заканчивает свое выполнение, при выходе из области using вызовется метод WaitManager'а Dispose (для этого и нужна реализация IDisposable). 
  3. WaitManager сигнализирует WaitForm'e, что он готов завершить ее поток (Finished = true)
  4. Таймер в WaitForm'e периодически проверяет, не сигнализирует ли WaitManager о готовности завершить поток (см. 3)
  5.  Если  Finished==true, то вызывается закрытие формы
  6. После этого WaitManager завершает поток, в котором была открыта форма, и передает управление вызывающему коду
В целом, реализация довольно логичная и простая, но в интернете подобной реализации я не встречал.

PS. Автор кода - не я. К сожалению, никаких контактов автора этого решения у меня нет.

Комментариев нет:

Отправить комментарий