Для целей этого вопроса у меня есть простой Window
со следующим XAML:
<StackPanel>
<TextBox Text="{Binding MyText}" />
<CheckBox IsChecked="{Binding IsChecked}">Check</CheckBox>
</StackPanel>
Всякий раз, когда пользователи вводят текст в TextBox
или проверяют CheckBox
, я хотел бы выполнить медленную задачу (например, сохранить состояние моей модели на диск). Вот вид модели:
public class ViewModel : ViewModelBase // using GalaSoft.MvvmLight
{
private string _myText;
public string MyText
{
get => _myText;
set
{
if (Set(ref _myText, value))
Save();
}
}
private bool _isChecked;
public bool IsChecked
{
get => _isChecked;
set
{
if (Set(ref _isChecked, value))
Save();
}
}
private async void Save()
{
var id = Guid.NewGuid();
Debug.WriteLine($"Starting save {id}");
await Task.Delay(100); // Simulate slow task
Debug.WriteLine($"Finished save {id}");
}
}
Метод Save
имитирует медленную задачу, такую как сохранение на диск. В целях отладки он выводит уникальный идентификатор до и после выполнения этой операции. Кроме того, метод является асинхронным, потому что я не хочу, чтобы пользовательский интерфейс зависал во время операции.
Проблема в том, что после того, как пользователь что-то наберет в TextBox
, а затем проверит CheckBox
, свойства обновляются очень быстро. Это приводит к следующему выводу примера отладки:
Starting save 6ea6c102-cbe7-472f-b8b8-249499ff7f64
Starting save c77b4478-14ca-4243-a45b-7b35b5663d49
Finished save 6ea6c102-cbe7-472f-b8b8-249499ff7f64
Finished save c77b4478-14ca-4243-a45b-7b35b5663d49
Как видите, первая операция сохранения (с MyText
) не выполняется до начала второй операции сохранения (с IsChecked
). Это немного пугает меня, потому что, я думаю, данные могут быть сохранены в неправильном порядке и повреждены.
Есть ли хорошая практика для решения такого рода проблем?
Я думал о паре возможных решений. Первый - использовать что-то вроде Delay=100
в привязке TextBox
. Это приведет к вызову метода Save
после того, как пользователь перестанет набирать текст в течение 100 мс. Это уродливое решение по разным причинам.
Второй - использовать SemaphoreSlim
. Внутри метода Save
я могу окружить код try
/ finally
, чтобы использовать семафор, как описано здесь . Это на самом деле работает, но я не уверен, что это лучший способ справиться с этой проблемой.