@romaro

Допустима ли такая реализация асинхронной вью-модели?

Собрал небольшой проект на винформах, чтобы продемонстрировать, как это работает.

При клике по кнопке создается объект вью-модели, в конструкторе которой создается таск (допустим, в нем выполняется запрос к БД). Вот как выглядит вью-модель:
internal class TestFormViewModel
    {
        public TestFormViewModel()
        {
            Title = "Loading...";
            Task.Run(() => RunLoaderAsync());
        }

        public bool IsLoaded { get; init; }
        public string Title { get; set; }

        public event EventHandler? ViewModelLoaded;
        void OnViewModelLoaded()
        {
            ViewModelLoaded?.Invoke(this, EventArgs.Empty);
        }

        async void RunLoaderAsync()
        {
            await Task.Delay(2000);
            Title = "Loaded!";
            OnViewModelLoaded();
        }
    }


Вью-модель биндится к объекту TestForm при помощи фичи, которую добавили в .NET 7 — Control.DataContext. Т.е. я переопределяю обработчик события смены контекста, в котором проверяю, завершена ли загрузка вью модели. Если нет, подписываюсь на ViewModelLoaded:
public partial class TestForm : Form
    {
        public TestForm()
        {
            InitializeComponent();
        }

        protected override void OnDataContextChanged(EventArgs e)
        {
            base.OnDataContextChanged(e);

            if (DataContext is TestFormViewModel ctx)
            {
                if (ctx.IsLoaded)
                {
                    Text = ctx.Title;
                }
                else
                {
                    ctx.ViewModelLoaded += DataContext_Loaded;
                }
            }
        }

        void DataContext_Loaded(object? sender, EventArgs e)
        {
            if (DataContext is TestFormViewModel ctx)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(() => Text = ctx.Title);
                }
                else
                {
                    Text = ctx.Title;
                }
            }
        }
    }


Когда асинхронная операция, запущенная в конструкторе вью-модели, будет наконец завершена, форма получит уведомление и изменит свое название (тут приходится выполнять проверку InvokeRequired, т.к. обращение к форме выполняется из фонового потока):
void DataContext_Loaded(object? sender, EventArgs e)
        {
            if (DataContext is TestFormViewModel ctx)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(() => Text = ctx.Title);
                }
                else
                {
                    Text = ctx.Title;
                }
            }
        }


Есть ли у такой реализации проблемные места? Например, возможна ли ситуация, при которой загрузка вью-модели будет завершена после того, как проверка IsLoaded показала False, но до подписки на событие? В итоге форма никогда не узнает, что вью-модель загружена, т.к. подпишется уже после того, как во вью-модели был вызван ViewModelLoaded.Invoke()?
  • Вопрос задан
  • 94 просмотра
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы