Собрал
небольшой проект на винформах, чтобы продемонстрировать, как это работает.
При клике по кнопке создается объект вью-модели, в конструкторе которой создается таск (допустим, в нем выполняется запрос к БД). Вот как выглядит вью-модель:
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()?